|
| 1 | +--- |
| 2 | +title: Getting Started |
| 3 | +description: Learn how to install and configure the Capacitor Watch plugin for bidirectional communication between iPhone and Apple Watch. |
| 4 | +sidebar: |
| 5 | + order: 2 |
| 6 | +--- |
| 7 | + |
| 8 | +import { Tabs, TabItem, Steps } from '@astrojs/starlight/components'; |
| 9 | +import { PackageManagers } from 'starlight-package-managers' |
| 10 | + |
| 11 | +<Steps> |
| 12 | +1. **Install the package** |
| 13 | + <PackageManagers pkg="@capgo/capacitor-watch" pkgManagers={['npm', 'pnpm', 'yarn', 'bun']} /> |
| 14 | + |
| 15 | +2. **Sync with native projects** |
| 16 | + <PackageManagers type="exec" pkg="cap" args="sync" pkgManagers={['npm', 'pnpm', 'yarn', 'bun']} /> |
| 17 | + |
| 18 | +3. **Configure the plugin** |
| 19 | + |
| 20 | + **Basic Usage Example:** |
| 21 | + ```typescript |
| 22 | + import { CapgoWatch } from '@capgo/capacitor-watch'; |
| 23 | + |
| 24 | + // Check watch connectivity status |
| 25 | + const info = await CapgoWatch.getInfo(); |
| 26 | + console.log('Watch paired:', info.isPaired); |
| 27 | + console.log('Watch reachable:', info.isReachable); |
| 28 | + |
| 29 | + // Listen for messages from watch |
| 30 | + await CapgoWatch.addListener('messageReceived', (event) => { |
| 31 | + console.log('Message from watch:', event.message); |
| 32 | + }); |
| 33 | + ``` |
| 34 | + |
| 35 | + **Send a Message to Watch:** |
| 36 | + ```typescript |
| 37 | + // Check if watch is reachable first |
| 38 | + const info = await CapgoWatch.getInfo(); |
| 39 | + if (info.isReachable) { |
| 40 | + await CapgoWatch.sendMessage({ |
| 41 | + data: { action: 'refresh', timestamp: Date.now() } |
| 42 | + }); |
| 43 | + } |
| 44 | + ``` |
| 45 | + |
| 46 | + <Tabs> |
| 47 | + <TabItem label="iOS"> |
| 48 | + **Required iOS Setup:** |
| 49 | + |
| 50 | + 1. Add the WatchConnectivity capability to your iOS app in Xcode |
| 51 | + 2. Create a watchOS app target in your Xcode project |
| 52 | + 3. Implement WatchConnectivity in your watchOS app (see Watch App Implementation below) |
| 53 | + |
| 54 | + The plugin automatically activates the WCSession when the plugin loads. |
| 55 | + </TabItem> |
| 56 | + <TabItem label="Android"> |
| 57 | + Apple Watch is only supported on iOS. On Android, all methods will reject with "Apple Watch is only supported on iOS" error. The `getInfo()` method returns `isSupported: false`. |
| 58 | + </TabItem> |
| 59 | + </Tabs> |
| 60 | + |
| 61 | +4. **Handle messages that require a reply** |
| 62 | + ```typescript |
| 63 | + // Listen for messages that need a response |
| 64 | + await CapgoWatch.addListener('messageReceivedWithReply', async (event) => { |
| 65 | + console.log('Request from watch:', event.message); |
| 66 | + |
| 67 | + // Process the request |
| 68 | + const result = await processWatchRequest(event.message); |
| 69 | + |
| 70 | + // Send reply back to watch |
| 71 | + await CapgoWatch.replyToMessage({ |
| 72 | + callbackId: event.callbackId, |
| 73 | + data: { result } |
| 74 | + }); |
| 75 | + }); |
| 76 | + ``` |
| 77 | + |
| 78 | +5. **Sync application state** |
| 79 | + ```typescript |
| 80 | + // Update application context (latest value only) |
| 81 | + await CapgoWatch.updateApplicationContext({ |
| 82 | + context: { |
| 83 | + theme: 'dark', |
| 84 | + userId: '123', |
| 85 | + lastSync: Date.now() |
| 86 | + } |
| 87 | + }); |
| 88 | + |
| 89 | + // Listen for context updates from watch |
| 90 | + await CapgoWatch.addListener('applicationContextReceived', (event) => { |
| 91 | + console.log('Context from watch:', event.context); |
| 92 | + }); |
| 93 | + ``` |
| 94 | + |
| 95 | +6. **Transfer user info reliably** |
| 96 | + ```typescript |
| 97 | + // Queue data for reliable delivery (even when watch is offline) |
| 98 | + await CapgoWatch.transferUserInfo({ |
| 99 | + userInfo: { |
| 100 | + recordId: '456', |
| 101 | + action: 'created', |
| 102 | + data: { name: 'Item 1' } |
| 103 | + } |
| 104 | + }); |
| 105 | + |
| 106 | + // Listen for user info transfers |
| 107 | + await CapgoWatch.addListener('userInfoReceived', (event) => { |
| 108 | + console.log('User info from watch:', event.userInfo); |
| 109 | + }); |
| 110 | + ``` |
| 111 | + |
| 112 | +7. **Monitor connectivity** |
| 113 | + ```typescript |
| 114 | + // Track reachability changes |
| 115 | + await CapgoWatch.addListener('reachabilityChanged', (event) => { |
| 116 | + console.log('Watch reachable:', event.isReachable); |
| 117 | + if (event.isReachable) { |
| 118 | + // Watch is now available for interactive messaging |
| 119 | + } |
| 120 | + }); |
| 121 | + |
| 122 | + // Track session activation state |
| 123 | + await CapgoWatch.addListener('activationStateChanged', (event) => { |
| 124 | + // 0 = notActivated, 1 = inactive, 2 = activated |
| 125 | + console.log('Session state:', event.state); |
| 126 | + }); |
| 127 | + ``` |
| 128 | +</Steps> |
| 129 | + |
| 130 | +## Watch App Implementation |
| 131 | + |
| 132 | +Your watchOS app needs to implement WatchConnectivity. Here's a SwiftUI example: |
| 133 | + |
| 134 | +```swift |
| 135 | +import SwiftUI |
| 136 | +import WatchConnectivity |
| 137 | + |
| 138 | +@main |
| 139 | +struct MyWatchApp: App { |
| 140 | + init() { |
| 141 | + WatchViewModel.shared.activate() |
| 142 | + } |
| 143 | + |
| 144 | + var body: some Scene { |
| 145 | + WindowGroup { |
| 146 | + ContentView() |
| 147 | + } |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | +class WatchViewModel: NSObject, ObservableObject, WCSessionDelegate { |
| 152 | + static let shared = WatchViewModel() |
| 153 | + |
| 154 | + @Published var lastMessage: [String: Any] = [:] |
| 155 | + |
| 156 | + func activate() { |
| 157 | + guard WCSession.isSupported() else { return } |
| 158 | + WCSession.default.delegate = self |
| 159 | + WCSession.default.activate() |
| 160 | + } |
| 161 | + |
| 162 | + // Send message to iPhone |
| 163 | + func sendToPhone(_ data: [String: Any]) { |
| 164 | + guard WCSession.default.isReachable else { |
| 165 | + print("iPhone not reachable") |
| 166 | + return |
| 167 | + } |
| 168 | + WCSession.default.sendMessage(data, replyHandler: nil) |
| 169 | + } |
| 170 | + |
| 171 | + // Send message with reply |
| 172 | + func sendToPhoneWithReply(_ data: [String: Any], completion: @escaping ([String: Any]) -> Void) { |
| 173 | + guard WCSession.default.isReachable else { return } |
| 174 | + WCSession.default.sendMessage(data, replyHandler: completion) |
| 175 | + } |
| 176 | + |
| 177 | + // Receive message from iPhone |
| 178 | + func session(_ session: WCSession, didReceiveMessage message: [String: Any]) { |
| 179 | + DispatchQueue.main.async { |
| 180 | + self.lastMessage = message |
| 181 | + } |
| 182 | + } |
| 183 | + |
| 184 | + // Receive application context |
| 185 | + func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String: Any]) { |
| 186 | + DispatchQueue.main.async { |
| 187 | + self.lastMessage = applicationContext |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + // Required delegate methods |
| 192 | + func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { |
| 193 | + print("Watch session activated: \(activationState.rawValue)") |
| 194 | + } |
| 195 | +} |
| 196 | +``` |
| 197 | + |
| 198 | +## API Reference |
| 199 | + |
| 200 | +### Methods |
| 201 | + |
| 202 | +#### `sendMessage(options: SendMessageOptions)` |
| 203 | +Send an interactive message to the watch. Requires watch to be reachable. |
| 204 | + |
| 205 | +**Parameters:** |
| 206 | +- `data`: Object - The data to send to the watch |
| 207 | + |
| 208 | +#### `updateApplicationContext(options: UpdateContextOptions)` |
| 209 | +Update application context. Only latest value is kept. |
| 210 | + |
| 211 | +**Parameters:** |
| 212 | +- `context`: Object - The context data to sync |
| 213 | + |
| 214 | +#### `transferUserInfo(options: TransferUserInfoOptions)` |
| 215 | +Queue user info for reliable delivery. |
| 216 | + |
| 217 | +**Parameters:** |
| 218 | +- `userInfo`: Object - The user info to transfer |
| 219 | + |
| 220 | +#### `replyToMessage(options: ReplyMessageOptions)` |
| 221 | +Reply to a message that requested a response. |
| 222 | + |
| 223 | +**Parameters:** |
| 224 | +- `callbackId`: string - The callback ID from messageReceivedWithReply event |
| 225 | +- `data`: Object - The reply data |
| 226 | + |
| 227 | +#### `getInfo()` |
| 228 | +Get watch connectivity status. |
| 229 | + |
| 230 | +**Returns:** `WatchInfo` object with: |
| 231 | +- `isSupported`: boolean - Whether WatchConnectivity is available |
| 232 | +- `isPaired`: boolean - Whether a watch is paired |
| 233 | +- `isWatchAppInstalled`: boolean - Whether watch app is installed |
| 234 | +- `isReachable`: boolean - Whether watch is reachable |
| 235 | +- `activationState`: number - Session state (0/1/2) |
| 236 | + |
| 237 | +#### `getPluginVersion()` |
| 238 | +Get the native plugin version. |
| 239 | + |
| 240 | +### Events |
| 241 | + |
| 242 | +| Event | Description | |
| 243 | +|-------|-------------| |
| 244 | +| `messageReceived` | Simple message from watch | |
| 245 | +| `messageReceivedWithReply` | Message expecting a reply (includes callbackId) | |
| 246 | +| `applicationContextReceived` | Context update from watch | |
| 247 | +| `userInfoReceived` | User info transfer from watch | |
| 248 | +| `reachabilityChanged` | Watch connectivity changed | |
| 249 | +| `activationStateChanged` | Session activation state changed | |
| 250 | + |
| 251 | +## Communication Patterns |
| 252 | + |
| 253 | +### Immediate Messaging (`sendMessage`) |
| 254 | +- Requires watch to be reachable |
| 255 | +- Best for interactive, time-sensitive communication |
| 256 | +- Fails immediately if watch is not available |
| 257 | + |
| 258 | +### Application Context (`updateApplicationContext`) |
| 259 | +- Latest value only - previous values are overwritten |
| 260 | +- Best for syncing current app state |
| 261 | +- Delivered when watch becomes available |
| 262 | + |
| 263 | +### User Info Transfer (`transferUserInfo`) |
| 264 | +- Queued and delivered in order |
| 265 | +- Best for important data that must be delivered |
| 266 | +- Works even when watch is temporarily unreachable |
| 267 | + |
| 268 | +## Platform Notes |
| 269 | + |
| 270 | +### iOS |
| 271 | +- Requires iOS 15.0 or later |
| 272 | +- Uses WatchConnectivity framework |
| 273 | +- Session automatically activates on plugin load |
| 274 | +- Supports background delivery for context and user info |
| 275 | + |
| 276 | +### Android |
| 277 | +- Not supported (Apple Watch is iOS-only) |
| 278 | +- All methods reject with appropriate error |
| 279 | +- `getInfo()` returns `isSupported: false` |
| 280 | + |
| 281 | +### Web |
| 282 | +- Not supported |
| 283 | +- All methods reject with unavailable error |
| 284 | +- `getInfo()` returns `isSupported: false` |
| 285 | + |
| 286 | +## Common Use Cases |
| 287 | + |
| 288 | +1. **Data Sync**: Keep watch and phone data in sync |
| 289 | +2. **Remote Control**: Control phone features from watch |
| 290 | +3. **Notifications**: Send custom notifications to watch |
| 291 | +4. **Health Data**: Share fitness and health metrics |
| 292 | +5. **Media Control**: Control music playback from watch |
| 293 | +6. **Smart Home**: Control devices from your wrist |
| 294 | + |
| 295 | +## Troubleshooting |
| 296 | + |
| 297 | +**Watch not reachable:** |
| 298 | +- Ensure watch is within Bluetooth range |
| 299 | +- Check that both apps are running |
| 300 | +- Verify WCSession is activated on both sides |
| 301 | + |
| 302 | +**Messages not received:** |
| 303 | +- Check that listeners are registered before sending |
| 304 | +- Verify the watch app implements WCSessionDelegate |
| 305 | +- Use `transferUserInfo` for guaranteed delivery |
| 306 | + |
| 307 | +**Session not activating:** |
| 308 | +- Ensure WatchConnectivity capability is added in Xcode |
| 309 | +- Check that watch app has the companion bundle ID |
| 310 | +- Verify both apps target compatible OS versions |
0 commit comments