Commercial SupportGuaranteed response times on Shiny bug fixes, straight from the maintainers. Plans from US$1,500 a year.Guaranteed response times on Shiny bug fixes, straight from the maintainers. Plans from US$1,500 a year.See the plans
The wearables bridge connects the web app to its companion app on a paired Apple Watch (WatchConnectivity) or
Wear OS device (the Data Layer). It’s built on Shiny.Wearables 5.9. The watch doesn’t run
the web app: the page talks to the phone, and the phone talks to your native watch app.
Mode
What it’s for
Bridge
Live message
request and reply while the watch is reachable
POST /_bridge/wearables/messages
Context
the latest shared state; only the newest value is kept
GET/PUT /_bridge/wearables/context
Transfer
queued data, delivered in order even when the watch is away
AddWearablesBridge() also registers Shiny’s wearable service with the bridge’s delegate, so there’s nothing else to call.
WearablesBridgeOptions
Default
Root
data
the file root that files from the watch are saved into
Folder
wearables
the folder inside Root; each file lands in {Folder}/{id}/{name}
RegisterWearableService
true
turn off to call services.AddWearables(…) yourself; the bridge’s delegate is added either way
The companion app speaks Shiny.Wearables’ protocol: path/data dictionaries over WatchConnectivity, and paths
under /shiny plus the shiny_wearable capability on Wear OS. On Wear OS the phone and watch apps must share the
application id and signing key. The Shiny Wearables docs cover the protocol in full, with Swift
and Kotlin examples.
What the page sends reaches the watch as UTF-8 JSON text, so a string goes out quoted ("hi") and a companion app should
read it with its JSON parser. What the watch sends reaches the page as JSON. Plain UTF-8 text, which is what
Shiny.Wearables’ string overloads (WearableData.FromString) and a Swift or Kotlin String send, arrives as a string.
Anything else arrives as a base64 string with binary: true, so nothing is dropped.
Messages never queue. When no watch is reachable, sendMessage fails with 409 (not_reachable). Keep
messages small: about 64 KB on iOS and 100 KB on Wear OS. Use a transfer for anything that must arrive.
Queued sends return immediately. Context, transfers and files hand back a { id }. wearables.completed
reports that id when the send is delivered, fails or is cancelled. GET /_bridge/wearables/transfers lists what’s
still pending, and DELETE /_bridge/wearables/transfers/{id} cancels it.
Errors:501 not_supported (no wearable API), 409 not_reachable, 502 wearable_failed (the platform refused),
400 for a bad path or body.
What the watch sends goes to the web app’s handlers: the page if it’s listening, background.js otherwise. The
platform wakes the phone app for it. A wearables.message handler’s return value is the reply the watch gets.
// background.js — answers the watch even when no page is open
appdevicebridge.on("wearables.message", async ({ path, data }) => {
The same payloads are also events (wearables.message, wearables.context, wearables.transfer, wearables.file),
along with wearables.status and wearables.completed. The events are for a page that only displays traffic.
Answer a message from a handler, not from an event.
Files move through the file roots, so the page can’t send anything it couldn’t already
read.
Direction
Details
Sending
POST /_bridge/wearables/files with { path, file: { root, path }, metadata? }. The file must be in a root on disk. The platform reads it while it transfers, so leave it in place until wearables.completed reports its id. It answers 404 when the file isn’t there.
Receiving
files from the watch are saved to data/wearables/{id}/{name} (set by Root and Folder). The handler and event get them as a BridgeFile to read, move or delete through the files bridge. Names are reduced to one safe path segment, so a sender can’t write outside that folder.
Sends a live message and waits for the companion app's reply. Fails with 409 (not_reachable) when no wearable is reachable — a message never queues; use transfer for that.
Queues a file from a file root for the wearable. Fails with 404 when the file does not exist. The platform reads it while it transfers, so leave it in place until onTransferCompleted reports the id.
A file the page can reach through the files bridge: a named root and a path inside it. Bridges that take or return a file use this, so nothing reaches the page that /_bridge/files would not also serve.
The wearables routes sit under AppDeviceBridgePolicies.Bridges like every other bridge: callers on this device by
default. This bridge doesn’t change the default policy, the host-name check or the launch session. See
Security.