A Cordova/Capacitor plugin that provides a cross-platform JavaScript API for Android & iOS mobile apps to communicate with their companion watch apps on their respective wearable platform (Wear OS and watchOS).
It is currently the only plugin that offers a unified plugin API for both iOS/watchOS & Android/Wear OS platforms.
- Provides two key mechanisms for phone<->watch communication: - simple request/response and event-driven API for sending messages and data between a phone app and a watch app. - optional "Shared State Sync" subsystem for keeping UI state in sync between phone and watch. - Background-capable listeners on Android via a foreground service so messages can be received while the app is backgrounded. - A consistent semantics surface across Android/Wear OS and iOS/watchOS implementations so developers can write one integration that works across platforms.
Highlights
- True cross-platform support: The only Cordova/Capacitor plugin that targets both Android/Wear OS and iOS/watchOS with a unified JavaScript API. Write your integration code once and deploy to both platforms.
- Bi-directional communication out-of-the-box: Built-in support for sending and receiving messages in both directions—phone to watch and watch to phone—with a clean, Promise-based API.
- Shared State Synchronization: Includes an optional per-path Last-Writer-Wins (LWW) CRDT state sync subsystem for effortless state replication between devices. Perfect for syncing UI state, preferences, and small data objects without manual message handling.
- Modern platform support: Leverages current Google Play Services APIs (MessageClient, DataClient, CapabilityClient) and WatchConnectivity framework, ensuring compatibility with modern Wear OS and watchOS versions.
- Active development and maintenance: This plugin is under active development with ongoing improvements, bug fixes, and support for the latest platform features—unlike many abandoned wearable plugins.
- Minimal platform modifications: Unlike other wearable plugins that require extensive manual changes to Cordova's volatile platform projects (breaking the Cordova workflow), this plugin uses Cordova hooks and automation to handle platform integration with minimal developer intervention.
- Tested and verified: Comes with a comprehensive automated test suite using cordova-paramedic to ensure reliability across platforms and use cases.
- Developer-friendly: Comprehensive documentation, working example apps for both phone and watch platforms, and detailed debugging guidance to help you build robust wearable experiences.
Quickstart & Resources
- Example apps: Clone the sibling repo cordova-plugin-wearable-example/ for working Cordova (cordova-app/), Capacitor (capacitor-app/), Wear OS (wearos-app/), and watchOS (watchos-app/) samples that mirror the plugin API and state sync flows. - Testing: Run the automated suite with cordova-paramedic or the VS Code tasks declared in the example repo (test-plugin-android, test-plugin-ios, test-plugin-both). Manual scenarios live in the example Cordova app via cdvtests/index.html once the test plugins are installed. - Platform refresh commands: When you touch native sources (src/android, src/ios) or watch companion projects, re-run the platform refresh steps documented in .github/copilot-instructions.md to keep generated platform code in sync. - Contributing: Review CONTRIBUTING.md for workflow, coding-style, and test expectations before opening a pull request.
`bash npm install cordova-plugin-wearable npx cap sync `
Manual steps
When the plugin is installed into a Cordova project, it automatically applies necessary platform modifications. However, Capacitor does not support this automatic modification process. Therefore, when using Capacitor, you must manually apply the following steps after installing the plugin:
$3
The plugin ships a small JS shim (www/wearable.js) that exposes the Wearable global expected by the API examples. Capacitor does not automatically copy this file into your native projects, so add it to your web assets and ensure it is loaded at runtime.
Options:
- Copy the file into your app's web assets and include it in index.html:
If your project defines these versions via variables, you can substitute them accordingly. These versions are the plugin defaults; bump if your project requires newer versions.
Then build the Android app as you normally would (Android Studio or ./gradlew assembleDebug).
$3
On iOS you must add the Swift source files to your Xcode app target and link the WatchConnectivity framework. The plugin Swift files are under node_modules/cordova-plugin-wearable/src/ios/.
Steps (Xcode):
1. Add the WatchConnectivity framework to your app target: in the project settings, under General → Frameworks, Libraries, and Embedded Content click
+ and add WatchConnectivity.framework.
2. If your app does not already use Swift, ensure the project is configured to embed Swift standard libraries (Xcode will normally prompt to create a Swift bridging header or set the flag automatically when adding Swift files). Set the Swift version to match your Xcode toolchain.
3. If your app uses a companion watchOS app, ensure the watch app target is present and embedded. The plugin expects the watch and phone app to share the same group/identifiers; follow the example project if you need guidance embedding the watch app into the iOS project.
4. Run a build in Xcode (
Product → Build).
$3
The Cordova
plugin.xml exposes two plugin preferences to override Play Services versions at install time:
If your Android project centralises Google Play Services versions (for example using
ext variables or gradle.properties), align the dependency versions to avoid version conflicts. Adjust the implementation lines shown above accordingly.
$3
- If you see unresolved references to Kotlin/Android classes, verify the Kotlin plugin is applied and the Kotlin stdlib is available to the project. - If message/capability discovery fails between phone and watch, confirm both apps use the same applicationId/package and are signed with the same signing key (see plugin README and example project notes). This is required for Google Play Services Data Layer deliveries. - When making native changes you will usually need to rebuild the native project (clean + build) so Xcode/Gradle pick up the new sources. - For watchOS embedding on iOS, follow the example app (the plugin repository includes an example watchOS target and an Xcode hook script in the example project). If you're unsure, inspect
cordova-plugin-wearable-example/cordova-app for a working configuration.
Cross-platform usage
The plugin exposes a consistent JavaScript API for both Android and iOS platforms:
Configure the plugin
Before using the plugin, you must configure it:
`javascript await Wearable.configure({ // Android-only (required on Android, ignored on iOS): capability: 'my_capability', // Capability advertised by your Wear OS watch app path: '/my/path', // Message path used for phone<->watch comms
// Android-only (optional, ignored on iOS): customize the foreground service notification notificationTitle: 'My App', notificationText: 'Connected to your watch',
// Cross-platform: enableLogging: true });
// Note: iOS uses WatchConnectivity framework which has different semantics: // - No capability/path configuration needed (WCSession manages pairing automatically) // - No foreground service notification (iOS uses background transfers and applicationContext)
// Start and stop connectivity explicitly await Wearable.startConnectivity(); // ... later ... await Wearable.stopConnectivity();
`
Plugin events
Register for plugin-generated events:
`javascript await Wearable.registerEventListener(function (evt) { // evt.event is the event name // Platform-specific events: // Android: 'isPairedChanged', 'isConnectedChanged' // iOS: 'sessionWatchStateDidChange', 'sessionReachabilityDidChange', // 'sessionDidBecomeInactive', 'sessionDidDeactivate' // evt.value contains event-specific data (e.g., boolean for paired/connected state) }); `
API semantics
The plugin provides a consistent API across platforms, but the underlying implementation details differ:
isSupported() - Android: Performs a capability-client call to verify the Wearable API is available - iOS: Returns
This plugin includes an optional "Shared State Sync" subsystem that provides a small, per-path Last-Writer-Wins (LWW) replicated state store between the phone and watch device. It's intended for small UI-centric state (toggle buttons, small feature flags, simple JSON blobs) to keep the watch UI in sync with the phone app without manual message handling.
Shared state is a durable, convergent data store synchronized between the phone app and watch device. It uses per-path Last-Writer-Wins (LWW) with tombstones and reliable delivery so edits made while offline converge on reconnect.
Key concepts - Path-based keys: Each state entry is addressed by a JSON Pointer path string (RFC 6901), for example:
/list/items/123/checked. - Values: Any JSON-serializable value. Deletions are represented as tombstones. - Versioning: For each path, maintain a logical version v = { c, r } where c is a monotonic counter local to the replica for that path and r is a stable replicaId (UUID) for the app install. Ordering is lexicographic: higher (c, r) wins. - Conflict resolution: LWW per-path using version ordering. Deletion is an operation type and participates in LWW; whichever op has the higher version determines the effective value or absence. - Tombstones: Deletes set tombstone=true. Tombstones are garbage-collected after the peer acknowledges having seen the delete (“seen up to” acks).
Transport - The plugin uses the configured
path (e.g., /data). Shared state uses a subspace: - Base: ${configuredPath}/state - Ops: ${configuredPath}/state/op/{opId} (reliable per-op DataItem) - Snapshot: ${configuredPath}/state/snapshot (current full state for bootstrap) - Ack: ${configuredPath}/state/ack (batched acknowledgements)
Payload shapes (JSON) - Operation (op): sent for every local mutation and replayed to peers when connected.
Replica identity & epochs - replicaId: a stable UUID persisted per install (SharedPreferences on Android; UserDefaults on iOS in future). - epoch: optional monotonic integer. Unknown replicaId implies a cold peer; send snapshot.
Merge rules (receiver) 1) For each incoming op or snapshot entry, compare incoming version v with local vL for that path. 2) If v > vL, apply the change: store value (tombstone=false) or delete (tombstone=true). 3) Persist and notify the JS listener only if the effective materialized state changed. 4) Enqueue an ack for { path, version } back to the sender (batch these).
Materialization - Persist per-path entries keyed by their JSON Pointer. To compute the full state object for the JS listener, start from
{} and apply each (path, value) with pointer semantics; skip tombstoned paths. Optionally cache materialized state and invalidate on changes.
Size and chunking - Prefer small values per path. Keep individual DataItem payloads under ~100KB. For large snapshots, split the entries array across multiple snapshot chunks with indices; future work may add explicit chunk fields.
Multiple watches & re-installs - The scheme works for N replicas. Tie-breaks on equal counters use replicaId ordering. If a device is re-installed (new replicaId), peers treat it as unknown and send a snapshot.
Security & privacy - Do not place secrets in shared state. Treat payloads as application data synchronized between your paired apps.
$3
-
registerStateListener(callback) - Immediately invokes the callback with { event: 'stateChanged', state: