npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@capgo/capacitor-watch

v8.0.8

Published

Capacitor plugin for Apple Watch communication with bidirectional messaging support

Readme

@capgo/capacitor-watch

Apple Watch communication plugin for Capacitor with bidirectional messaging support.

Why Capacitor Watch?

The only Capacitor 8 compatible plugin for bidirectional Apple Watch communication:

  • Two-way messaging - Send and receive messages between iPhone and Apple Watch
  • Application context - Sync app state with latest-value-only semantics
  • User info transfers - Reliable queued delivery even when watch is offline
  • Request/Reply pattern - Interactive workflows with callback-based responses
  • SwiftUI ready - Includes watch-side SDK with ObservableObject support
  • iOS 15+ - Built for modern iOS with Swift Package Manager

Essential for health apps, fitness trackers, remote controls, and any app extending to Apple Watch.

Documentation

The most complete doc is available here: https://capgo.app/docs/plugins/watch/

Compatibility

| Plugin version | Capacitor compatibility | Maintained | | -------------- | ----------------------- | ---------- | | v8.*.* | v8.*.* | ✅ | | v7.*.* | v7.*.* | On demand | | v6.*.* | v6.*.* | ❌ | | v5.*.* | v5.*.* | ❌ |

Note: The major version of this plugin follows the major version of Capacitor. Use the version that matches your Capacitor installation (e.g., plugin v8 for Capacitor 8). Only the latest major version is actively maintained.

Install

npm install @capgo/capacitor-watch
npx cap sync

Requirements

  • iOS: iOS 15.0+ (Capacitor 8 minimum). Requires WatchConnectivity capability.
  • watchOS: watchOS 9.0+. Requires companion app with CapgoWatchSDK.
  • Android: Not supported (Apple Watch is iOS-only). Methods return appropriate errors.
  • Hardware: Real Apple Watch required - simulators do not support WatchConnectivity.

Complete Setup Tutorial

This tutorial walks you through setting up bidirectional communication between your Capacitor app and Apple Watch. Follow each step carefully.

Step 1: Install the Plugin

First, add the plugin to your Capacitor project:

npm install @capgo/capacitor-watch
npx cap sync ios

Then open your iOS project in Xcode:

npx cap open ios

Step 2: Add iOS App Capabilities

Your iOS app needs specific capabilities to communicate with Apple Watch.

  1. Select your App target in Xcode (not the project)
  2. Go to the Signing & Capabilities tab
  3. Click the + Capability button

Add capability in Xcode

  1. Add the following capabilities:
    • Background Modes - Enable "Background fetch" and "Remote notifications"
    • Push Notifications (required for background wake)

Your capabilities should look like this when complete:

Final capabilities configuration

Step 3: Configure AppDelegate.swift

[!NOTE] For now this will not compile. This is fine, we will fix in later steps

import UIKit
import Capacitor
import WatchConnectivity
import CapgoWatchSDK

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Initialize WatchConnectivity session
        if WCSession.isSupported() {
            WCSession.default.delegate = CapWatchSessionDelegate.shared
            WCSession.default.activate()
        }
        return true
    }

    // ... rest of your AppDelegate code
}

Step 4: Create the Watch App Target

Now create the watchOS companion app:

  1. In Xcode, go to File > New > Target
  2. Select watchOS tab
  3. Choose App and click Next

Create watch target

  1. Configure the watch app:
    • Product Name: Your app name (e.g., "MyApp Watch")
    • Bundle Identifier: Must follow the pattern [your-app-bundle-id].watchkitapp
      • Example: If your app is com.example.myapp, use com.example.myapp.watchkitapp
    • Language: Swift
    • User Interface: SwiftUI

Watch target options

Step 5: Add the CapgoWatchSDK Package

The watch app needs our SDK to communicate with the phone. Add it as a Swift Package:

  1. Select your project in the navigator (top level, blue icon)
  2. Go to Package Dependencies tab
  3. Click the + button to add a package

Project package dependencies

  1. Click on the plus button to add a package

Plus button

  1. Click Add Package

Add local SPM package

  1. When prompted, select CapgoWatchSDK and add it to your Watch App target (not the main app)

Pick target for package

After adding, your package dependencies should show the CapgoWatchSDK:

SPM finished

Step 6: Fix the build for main app

Right now, your main app is missing the CapgoWatchSDK. We need to add it to the main app.

  1. Select your project in the navigator (top level, blue icon)
  2. Go to your iOS app target
  3. Go to general
  4. Scroll to Frameworks, Libraries, and Embedded Content
  5. Click the plus button to add a framework

Add framework

  1. Click on the CapgoWatchSDK framework and click Add

Add framework

Step 6: Configure the Watch App

Update your watch app's main file to initialize the connection:

MyAppWatch/MyAppWatchApp.swift:

import SwiftUI
import WatchConnectivity
import CapgoWatchSDK

@main
struct MyAppWatchApp: App {
    init() {
        // Activate the watch connector
        WatchConnector.shared.activate()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

MyAppWatch/ContentView.swift:

import SwiftUI
import CapgoWatchSDK

struct ContentView: View {
    @ObservedObject var connector = WatchConnector.shared

    var body: some View {
        VStack(spacing: 20) {
            // Connection status indicator
            HStack {
                Circle()
                    .fill(connector.isReachable ? Color.green : Color.red)
                    .frame(width: 12, height: 12)
                Text(connector.isReachable ? "Connected" : "Disconnected")
                    .font(.caption)
            }

            // Send message button
            Button("Send to Phone") {
                connector.sendMessage(["action": "buttonTapped", "timestamp": Date().timeIntervalSince1970]) { reply in
                    print("Phone replied: \(reply)")
                }
            }
            .disabled(!connector.isReachable)

            // Display received context
            if let context = connector.receivedContext {
                Text("Last update: \(context["status"] as? String ?? "none")")
                    .font(.caption2)
            }
        }
        .padding()
    }
}

Your watch app structure should look like this:

Watch sources added

Step 7: Add Watch App Capabilities

The watch app also needs background capabilities:

  1. Select your Watch App target in Xcode
  2. Go to Signing & Capabilities tab
  3. Click + Capability
  4. Add Background Modes
  5. Enable Remote Notifications

Watch remote notifications capability

Step 8: Use the Plugin in Your Capacitor App

Now set up the JavaScript side in your Capacitor app:

import { Watch } from '@capgo/capacitor-watch';

// Check watch connectivity status
async function checkWatchStatus() {
  const info = await Watch.getInfo();
  console.log('Watch supported:', info.isSupported);
  console.log('Watch paired:', info.isPaired);
  console.log('Watch app installed:', info.isWatchAppInstalled);
  console.log('Watch reachable:', info.isReachable);
}

// Listen for messages from watch
Watch.addListener('messageReceived', (event) => {
  console.log('Message from watch:', event.message);
  // Handle the message (e.g., event.message.action === 'buttonTapped')
});

// Listen for messages that need a reply
Watch.addListener('messageReceivedWithReply', async (event) => {
  console.log('Watch asking:', event.message);

  // Send reply back to watch
  await Watch.replyToMessage({
    callbackId: event.callbackId,
    data: { response: 'acknowledged', processed: true }
  });
});

// Listen for connection changes
Watch.addListener('reachabilityChanged', (event) => {
  console.log('Watch reachable:', event.isReachable);
  // Update UI to show connection status
});

// Send data to watch (latest value wins)
async function updateWatchContext(data: Record<string, unknown>) {
  await Watch.updateApplicationContext({ context: data });
}

// Send message to watch (requires watch to be reachable)
async function sendMessageToWatch(data: Record<string, unknown>) {
  await Watch.sendMessage({ data });
}

// Queue data for reliable delivery (even when watch is offline)
async function queueDataForWatch(data: Record<string, unknown>) {
  await Watch.transferUserInfo({ userInfo: data });
}

Step 9: Build and Run

Use the target dropdown in Xcode to switch between building for your phone or watch:

Target dropdown

Build order:

  1. First, build and run the iOS App on your iPhone
  2. Then, build and run the Watch App on your Apple Watch

Important Notes:

  • You must use real devices - simulators do not support WatchConnectivity
  • Both apps must be running for bidirectional communication
  • The watch app will show "Disconnected" until the phone app is active

Communication Methods

Choose the right method for your use case:

| Method | Use Case | Delivery | Watch Must Be Reachable | |--------|----------|----------|-------------------------| | sendMessage() | Real-time interaction | Immediate | Yes | | updateApplicationContext() | Sync app state | Latest value only | No | | transferUserInfo() | Important data | Queued, in order | No |

Example: Complete Communication Flow

import { Watch } from '@capgo/capacitor-watch';

class WatchService {
  private isReachable = false;

  async initialize() {
    // Check initial status
    const info = await Watch.getInfo();
    this.isReachable = info.isReachable;

    // Monitor reachability
    Watch.addListener('reachabilityChanged', (event) => {
      this.isReachable = event.isReachable;
    });

    // Handle incoming messages
    Watch.addListener('messageReceived', (event) => {
      this.handleWatchMessage(event.message);
    });

    // Handle request/reply messages
    Watch.addListener('messageReceivedWithReply', async (event) => {
      const reply = await this.processWatchRequest(event.message);
      await Watch.replyToMessage({
        callbackId: event.callbackId,
        data: reply
      });
    });
  }

  async syncAppState(state: Record<string, unknown>) {
    // Always works - queues if watch is unreachable
    await Watch.updateApplicationContext({ context: state });
  }

  async sendInteractiveMessage(data: Record<string, unknown>) {
    if (!this.isReachable) {
      console.log('Watch not reachable, queueing message');
      await Watch.transferUserInfo({ userInfo: data });
      return;
    }
    await Watch.sendMessage({ data });
  }

  private handleWatchMessage(message: Record<string, unknown>) {
    // Process message from watch
    console.log('Watch action:', message.action);
  }

  private async processWatchRequest(message: Record<string, unknown>) {
    // Process and return reply
    return { status: 'ok', timestamp: Date.now() };
  }
}

SwiftUI Watch App Examples

Basic Watch UI

Example watch UI

Advanced Watch App with Data Display

import SwiftUI
import CapgoWatchSDK

struct ContentView: View {
    @ObservedObject var connector = WatchConnector.shared
    @State private var lastMessage = "No messages yet"

    var body: some View {
        ScrollView {
            VStack(spacing: 16) {
                // Status header
                StatusView(isConnected: connector.isReachable)

                Divider()

                // Action buttons
                Button("Request Data") {
                    connector.sendMessage(["action": "requestData"]) { reply in
                        if let status = reply["status"] as? String {
                            lastMessage = "Got: \(status)"
                        }
                    }
                }
                .buttonStyle(.borderedProminent)
                .disabled(!connector.isReachable)

                Button("Send Tap") {
                    connector.sendMessage(["action": "tap", "time": Date().timeIntervalSince1970])
                }
                .disabled(!connector.isReachable)

                Divider()

                // Message display
                Text(lastMessage)
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            .padding()
        }
    }
}

struct StatusView: View {
    let isConnected: Bool

    var body: some View {
        HStack {
            Image(systemName: isConnected ? "iphone.radiowaves.left.and.right" : "iphone.slash")
                .foregroundColor(isConnected ? .green : .red)
            Text(isConnected ? "Phone Connected" : "Phone Disconnected")
                .font(.caption)
        }
    }
}

API

Apple Watch communication plugin for Capacitor. Provides bidirectional messaging between iPhone and Apple Watch using WatchConnectivity.

sendMessage(...)

sendMessage(options: SendMessageOptions) => Promise<void>

Send an interactive message to the watch. The watch must be reachable for this to succeed. Use this for time-sensitive, interactive communication.

| Param | Type | Description | | ------------- | ----------------------------------------------------------------- | --------------------- | | options | SendMessageOptions | - The message options |

Since: 8.0.0


updateApplicationContext(...)

updateApplicationContext(options: UpdateContextOptions) => Promise<void>

Update the application context shared with the watch. Only the latest context is kept - this overwrites any previous context. Use this for syncing app state that the watch needs to display.

| Param | Type | Description | | ------------- | --------------------------------------------------------------------- | --------------------- | | options | UpdateContextOptions | - The context options |

Since: 8.0.0


transferUserInfo(...)

transferUserInfo(options: TransferUserInfoOptions) => Promise<void>

Transfer user info to the watch. Transfers are queued and delivered in order, even if the watch is not currently reachable. Use this for important data that must be delivered reliably.

| Param | Type | Description | | ------------- | --------------------------------------------------------------------------- | ----------------------- | | options | TransferUserInfoOptions | - The user info options |

Since: 8.0.0


replyToMessage(...)

replyToMessage(options: ReplyMessageOptions) => Promise<void>

Reply to a message from the watch that requested a reply. Use this in response to the messageReceivedWithReply event.

| Param | Type | Description | | ------------- | ------------------------------------------------------------------- | -------------------------------------------- | | options | ReplyMessageOptions | - The reply options including the callbackId |

Since: 8.0.0


getInfo()

getInfo() => Promise<WatchInfo>

Get information about the watch connectivity status.

Returns: Promise<WatchInfo>

Since: 8.0.0


getPluginVersion()

getPluginVersion() => Promise<{ version: string; }>

Get the native Capacitor plugin version.

Returns: Promise<{ version: string; }>

Since: 8.0.0


addListener('messageReceived', ...)

addListener(eventName: 'messageReceived', listenerFunc: (event: MessageReceivedEvent) => void) => Promise<PluginListenerHandle>

Listen for messages received from the watch.

| Param | Type | Description | | ------------------ | ----------------------------------------------------------------------------------------- | ------------------- | | eventName | 'messageReceived' | - The event name | | listenerFunc | (event: MessageReceivedEvent) => void | - Callback function |

Returns: Promise<PluginListenerHandle>

Since: 8.0.0


addListener('messageReceivedWithReply', ...)

addListener(eventName: 'messageReceivedWithReply', listenerFunc: (event: MessageReceivedWithReplyEvent) => void) => Promise<PluginListenerHandle>

Listen for messages from the watch that require a reply.

| Param | Type | Description | | ------------------ | ----------------------------------------------------------------------------------------------------------- | ------------------- | | eventName | 'messageReceivedWithReply' | - The event name | | listenerFunc | (event: MessageReceivedWithReplyEvent) => void | - Callback function |

Returns: Promise<PluginListenerHandle>

Since: 8.0.0


addListener('applicationContextReceived', ...)

addListener(eventName: 'applicationContextReceived', listenerFunc: (event: ContextReceivedEvent) => void) => Promise<PluginListenerHandle>

Listen for application context updates from the watch.

| Param | Type | Description | | ------------------ | ----------------------------------------------------------------------------------------- | ------------------- | | eventName | 'applicationContextReceived' | - The event name | | listenerFunc | (event: ContextReceivedEvent) => void | - Callback function |

Returns: Promise<PluginListenerHandle>

Since: 8.0.0


addListener('userInfoReceived', ...)

addListener(eventName: 'userInfoReceived', listenerFunc: (event: UserInfoReceivedEvent) => void) => Promise<PluginListenerHandle>

Listen for user info transfers from the watch.

| Param | Type | Description | | ------------------ | ------------------------------------------------------------------------------------------- | ------------------- | | eventName | 'userInfoReceived' | - The event name | | listenerFunc | (event: UserInfoReceivedEvent) => void | - Callback function |

Returns: Promise<PluginListenerHandle>

Since: 8.0.0


addListener('reachabilityChanged', ...)

addListener(eventName: 'reachabilityChanged', listenerFunc: (event: ReachabilityChangedEvent) => void) => Promise<PluginListenerHandle>

Listen for watch reachability changes.

| Param | Type | Description | | ------------------ | ------------------------------------------------------------------------------------------------- | ------------------- | | eventName | 'reachabilityChanged' | - The event name | | listenerFunc | (event: ReachabilityChangedEvent) => void | - Callback function |

Returns: Promise<PluginListenerHandle>

Since: 8.0.0


addListener('activationStateChanged', ...)

addListener(eventName: 'activationStateChanged', listenerFunc: (event: ActivationStateChangedEvent) => void) => Promise<PluginListenerHandle>

Listen for session activation state changes.

| Param | Type | Description | | ------------------ | ------------------------------------------------------------------------------------------------------- | ------------------- | | eventName | 'activationStateChanged' | - The event name | | listenerFunc | (event: ActivationStateChangedEvent) => void | - Callback function |

Returns: Promise<PluginListenerHandle>

Since: 8.0.0


removeAllListeners()

removeAllListeners() => Promise<void>

Remove all listeners for this plugin.

Since: 8.0.0


Interfaces

SendMessageOptions

Options for sending a message to the watch.

| Prop | Type | Description | | ---------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | data | WatchMessageData | The data to send to the watch. Must be serializable (string, number, boolean, arrays, or nested objects). |

UpdateContextOptions

Options for updating the application context.

| Prop | Type | Description | | ------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | context | WatchMessageData | The context data to sync with the watch. Only the latest context is kept - previous values are overwritten. |

TransferUserInfoOptions

Options for transferring user info.

| Prop | Type | Description | | -------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------- | | userInfo | WatchMessageData | The user info data to transfer. Transfers are queued and delivered in order. |

ReplyMessageOptions

Options for replying to a message from the watch.

| Prop | Type | Description | | ---------------- | ------------------------------------------------------------- | --------------------------------------------------------------- | | callbackId | string | The callback ID received in the messageReceivedWithReply event. | | data | WatchMessageData | The reply data to send back to the watch. |

WatchInfo

Information about Watch connectivity status.

| Prop | Type | Description | | ------------------------- | -------------------- | ---------------------------------------------------------------------------------------------- | | isSupported | boolean | Whether WatchConnectivity is supported on this device. Always false on iPad, web, and Android. | | isPaired | boolean | Whether an Apple Watch is paired with this iPhone. | | isWatchAppInstalled | boolean | Whether the paired watch has the companion app installed. | | isReachable | boolean | Whether the watch is currently reachable for immediate messaging. | | activationState | number | The current activation state of the WCSession. 0 = notActivated, 1 = inactive, 2 = activated |

PluginListenerHandle

| Prop | Type | | ------------ | ----------------------------------------- | | remove | () => Promise<void> |

MessageReceivedEvent

Event data for received messages.

| Prop | Type | Description | | ------------- | ------------------------------------------------------------- | ----------------------------------------- | | message | WatchMessageData | The message data received from the watch. |

MessageReceivedWithReplyEvent

Event data for messages that require a reply.

| Prop | Type | Description | | ---------------- | ------------------------------------------------------------- | ----------------------------------------------------------- | | message | WatchMessageData | The message data received from the watch. | | callbackId | string | The callback ID to use when replying with replyToMessage(). |

ContextReceivedEvent

Event data for application context updates.

| Prop | Type | Description | | ------------- | ------------------------------------------------------------- | ----------------------------------------- | | context | WatchMessageData | The context data received from the watch. |

UserInfoReceivedEvent

Event data for user info transfers.

| Prop | Type | Description | | -------------- | ------------------------------------------------------------- | ------------------------------------------- | | userInfo | WatchMessageData | The user info data received from the watch. |

ReachabilityChangedEvent

Event data for reachability changes.

| Prop | Type | Description | | ----------------- | -------------------- | ----------------------------------- | | isReachable | boolean | Whether the watch is now reachable. |

ActivationStateChangedEvent

Event data for activation state changes.

| Prop | Type | Description | | ----------- | ------------------- | ----------------------------------------------------------------------- | | state | number | The new activation state. 0 = notActivated, 1 = inactive, 2 = activated |

Type Aliases

WatchMessageData

Data that can be sent between iPhone and Apple Watch. Values must be serializable (string, number, boolean, arrays, or nested objects).

Record<string, unknown>

Record

Construct a type with a set of properties K of type T

{ [P in K]: T; }

Credits

Based on the enhanced WatchConnectivity implementation from CapacitorWatchEnhanced. Who was a fork of the offical CapacitorWatch