跳过主内容
教程 iOS Capacitor

如何为 Capacitor App 构建 Apple Watch App

基于 Capacitor 构建一个 Apple Watch 应用:添加一个 watchOS 目标,编写 SwiftUI 侧,使用 @capgo/capacitor-watch 同步数据。

文章来源

马丁·多纳迪厄

作者

瓦莱里亚

审阅者

乔丹

编辑

基于 Capacitor 构建一个 Apple Watch 应用

要在 Capacitor 中构建一个 Apple Watch 应用,您需要将一个原生 watchOS 目标添加到您的 iOS 项目中,使用 SwiftUI 编写其 UI,并将其连接到您的 web code @capgo/capacitor-watch 插件,使用 Apple 的 WatchConnectivity 框架包装。手机应用保留其 Capacitor web 层,而 Apple Watch 应用变成一个小型原生伴侣,发送和接收字典。

本指南涵盖了 Capacitor 8 和 Xcode 26 的完整路径:架构、Xcode 目标、Swift 和 TypeScript code 在每个侧,传输方法、在模拟器和设备上进行测试,以及 Apple Watch 应用如何发布。

为什么手表UI是本地的,而手机却是Capacitor

watchOS 没有提供 WebView。 在手表上没有 WKWebView 手表上没有 Capacitor 应用,因此无法在那里运行,并且没有插件的变化。您可以做的是将责任分开:

层 在 语言 工作
Capacitor 应用 iPhone TypeScript, 你的框架 业务逻辑、网络、认证、存储
@capgo/capacitor-watch iPhone Swift (插件), JS API 将 WatchConnectivity 桥接到 JavaScript
CapgoWatchSDK 苹果手表 Swift 在手表上处理会话
手表应用 苹果手表 SwiftUI 适合小屏幕、简洁 UI、快速操作

在实践中,手表应用是“依赖”的:它从手机获取数据。将逻辑保留在手机上(它已经存在于 TypeScript 中),并将只发送手表需要显示的内容。

前提条件

  • A Capacitor 8 应用程序,已添加 iOS 平台。如果您仍在使用较旧的版本,请遵循 Capacitor 8 升级指南 首先。
  • 在 Mac 上使用 Xcode 26。苹果要求从 2026 年 4 月起使用 Xcode 26 进行 App Store 上载,请参见 Xcode 26 要求.
  • 需要 Apple 开发者帐户进行设备测试和分发。
  • 手机上至少需要 iOS 15 或更高版本(插件最低要求),手表上至少需要 watchOS 9 或更高版本(SDK 最低要求)。
  • 理想情况下,配对的 iPhone 和 Apple Watch。模拟器有帮助,但它们无法覆盖每种传输类型。

步骤 1:在手机上安装插件

bun add @capgo/capacitor-watch
bunx cap sync ios

上下文:页面/区域:Capgo 页。角色:UI 标签。见于:关于.astro 页。消息键 `about_how_step_label` (关于如何步骤标签)。 WCSession 插件激活

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

const info = await CapgoWatch.getInfo();
console.log({
  supported: info.isSupported,          // false on iPad, Android without Wear OS, and web
  paired: info.isPaired,                // an Apple Watch is paired with this iPhone
  installed: info.isWatchAppInstalled,  // your watch app is on that watch
  reachable: info.isReachable,          // live messaging is possible right now
  state: info.activationState,          // 0 notActivated, 1 inactive, 2 activated
});

完整的API参考文档位于 插件文档 和 插件页面.

步骤 2:在 Xcode 中添加 watchOS 目标

上下文:关于 Capgo 页面。角色:UI 标签。见于:astro 页面。消息键 `about_how_step_label` (关于如何步骤标签)。 bunx cap open ios.Capacitor 8个使用Swift Package Manager创建的项目 App.xcodeproj。 __CAPGO_KEEP_0__ 使用 Swift Package Manager 创建的 8 个项目打开 App.xcworkspace;

  1. Choose ;两者都可以。 watchOS tab, 然后 App.
  2. 命名它,例如 MyWatch, 并使用 SwiftUI 作为界面。
  3. 在选项面板中,确保手表应用程序嵌入到您的现有 App 作为其伴侣。该设置是使 Xcode 将手表应用程序嵌入到 iPhone 构建中并设置伴侣包标识符的原因。
  4. 除非您现在需要它们,否则请不要启用通知场景和复杂性。您可以稍后添加小工具。
  5. 接受激活新方案的提示。

包标识符规则

手表应用程序的包标识符必须以 iPhone 应用程序的包标识符开头。如果您的应用程序是 com.example.shop,Xcode 提议 com.example.shop.watchkitapp。保持该前缀。如果您以后改变 iOS 包 ID,务必同时改变 watch 包 ID,否则构建将会失败验证,WatchConnectivity 将无法配对这两个应用。

在 签名与能力中

选择两个目标的相同团队。使用自动签名,Xcode 将为您注册第二个 App ID 和配置文件。

  1. 在 watch 目标中添加 CapgoWatchSDK.
  2. 文件 > 添加包依赖项 https://github.com/Cap-go/capacitor-watch.git.
  3. 输入 8.0.0.
  4. 使用“到下一个主要版本” CapgoWatchSDK CapgoWatchSDK watch 目标, 不是 iOS 应用。 iOS 端已经通过 cap sync.

步骤 3: 在 SwiftUI 中编写 watch 应用

The SDK exposes WatchConnector.shared, an ObservableObject , 一个 isReachable, isActivated, lastMessage已发布 applicationContext , 和

import SwiftUI
import CapgoWatchSDK

@main
struct MyWatchApp: App {
    init() {
        WatchConnector.shared.activate()
    }

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

属性。启动应用时激活它:

import SwiftUI
import CapgoWatchSDK

struct OrdersView: View {
    @ObservedObject private var connector = WatchConnector.shared
    @State private var status = ""

    var openOrders: Int {
        connector.applicationContext["openOrders"] as? Int ?? 0
    }

    var body: some View {
        VStack(spacing: 8) {
            Text("\(openOrders)")
                .font(.system(size: 44, weight: .bold))
            Text("open orders")
                .font(.footnote)
                .foregroundStyle(.secondary)

            Button("Refresh") {
                refresh()
            }
            .disabled(!connector.isReachable)

            if !status.isEmpty {
                Text(status).font(.caption2)
            }
        }
    }

    private func refresh() {
        Task {
            do {
                let reply = try await connector.sendMessage(["action": "refresh"])
                status = "Updated \(reply["count"] as? Int ?? 0)"
            } catch {
                status = "iPhone not reachable"
            }
        }
    }
}

The async 这个 sendMessage 的变体, 发送一个回复处理器, 因此手机将其作为一个 messageReceivedWithReply 事件。 回调形式的变体 sendMessage(_:replyHandler:errorHandler:) 让您跳过回复。

如果您更喜欢委托回调而不是观察发布的属性,请遵守 WatchConnectorDelegate 并设置 WatchConnector.shared.delegate。它具有用于消息、带有回复处理器的消息、应用程序上下文、用户信息、可达性和激活的方法。

步骤 4:从 TypeScript 与手表对话

context

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

export async function setupWatchBridge(getOpenOrders: () => Promise<number>) {
  // Watch asked for something and is waiting for an answer
  await CapgoWatch.addListener('messageReceivedWithReply', async (event) => {
    if (event.message.action === 'refresh') {
      const count = await getOpenOrders();
      await CapgoWatch.replyToMessage({
        callbackId: event.callbackId,
        data: { count },
      });
      await pushStateToWatch(count);
    }
  });

  // Fire-and-forget messages from the watch
  await CapgoWatch.addListener('messageReceived', (event) => {
    console.log('watch says', event.message);
  });

  await CapgoWatch.addListener('reachabilityChanged', (event) => {
    console.log('watch reachable:', event.isReachable);
  });

  await pushStateToWatch(await getOpenOrders());
}

export async function pushStateToWatch(openOrders: number) {
  const info = await CapgoWatch.getInfo();
  if (!info.isSupported || !info.isPaired || !info.isWatchAppInstalled) return;

  // Latest state only, delivered when the watch app next runs
  await CapgoWatch.updateApplicationContext({
    context: { openOrders, updatedAt: Date.now() },
  });
}

Page/area: About Capgo page. Role: UI label. Seen in: page about.astro. Message key `about_how_step_label` (About How Step Label). messageReceivedWithReply尽早注册监听器,例如在应用程序引导中,这样启动时到达的事件就不会被丢失。

始终回复

。手表正在等待,未回复的请求在手表端会导致超时错误。

您应该使用哪种传输方法? 配送 需要手表可达 适合
sendMessage 立即 是,否则拒绝 双端应用点击,实时更新
updateApplicationContext 最新值获胜,尽可能快 否 当前状态:计数,设置,已登录用户
transferUserInfo 排队,按顺序送达 否 必须到达的事件:已记录的运动,创建的记录

一个有效的模式:将应用程序上下文作为手表显示的真实来源,使用 sendMessage 仅在 Apple Watch 应用程序打开时进行交互,使用 transferUserInfo 对于任何你不能放弃的东西。

设计限制

  • 数据包大小 WatchConnectivity会拒绝过大的载荷 payloadTooLarge, and Apple does not publish a fixed byte count for every channel. Keep payloads small: IDs, counts, short strings. Fetch large content from your API instead.
  • 数据必须是属性列表兼容的:字符串、数字、布尔值、日期、数据、数组和字典。 JavaScript中的值不合法。请在发送前清除它们。 null 手机必须运行您的应用程序来处理事件。
  • 手机必须正在运行你的应用程序来处理事件。 Listeners live in the Capacitor JavaScript runtime. If iOS has terminated the app, events will not reach your TypeScript until the app runs again. Context and user info stay queued by the system, but design the watch UI so it works from cached context.
  • 配对。 苹果手表只与iPhone配对,安卓用户无法使用它,插件的安卓实现目标是Wear OS,详见我们的 Wear OS指南.
  • 独立性。 基于WatchConnectivity的伴侣应用程序依赖于手机。如果手表必须在手机的远处工作,请添加一个第二个数据源,如您的API通过手表的网络连接。

在模拟器和设备上进行测试

您可以在手表模拟器中迭代SwiftUI布局,但数据传输需要配对设置。

xcrun simctl list devices
xcrun simctl pair <watch-udid> <phone-udid>

在配对的iPhone模拟器上运行iOS应用,然后在手表模拟器上运行手表方案。两款应用程序必须在 isWatchAppInstalled 苹果指出Simulator不支持 transferUserInfo,因此在真实硬件上确认该路径。

对于设备,插入iPhone,确保手表已启用开发者模式,并选择手表作为手表方案的运行目标。使用 getInfo() 作为您的烟雾测试:

getInfo() 结果 含义
isPaired: false 未与此手机配对的智能手表
isWatchAppInstalled: false 手表应用程序丢失,或捆绑ID/伴侣设置错误
isReachable: false, 是真实的 手表应用程序不在前台或超出范围
activationState: 2所有真实 准备就绪 sendMessage

故障排除

isWatchAppInstalled 故障排除 保持 false。 App 检查手表捆绑ID前缀,检查手表目标是否嵌入到(构建阶段 > 嵌入手表内容),并重新安装两款应用程序。

手表的消息永远无法到达 JavaScript. 在手表发送之前注册监听器,并确认手表在启动时调用。 WatchConnector.shared.activate() 在手机上,检查 Xcode 控制台以获取插件的激活日志。

sendMessage 手表不可达时会拒绝。 当手表应用关闭时,预期会发生这种情况。请切换到 updateApplicationContext.

replyToMessage 似乎被忽略。 从事件中传递精确的 callbackId 并快速回复。手表的回复处理程序有一个超时。

构建失败,提示“嵌入式二进制文件的包标识符未以 iOS 包标识符开头”。 手表包标识符必须以 iOS 包标识符开头。

上传存档被拒绝,因为缺少图标。 手表目标需要自己的应用图标在 Assets.xcassets.

运送手表应用

手表应用不是一个独立的产品。它嵌入在iOS存档中,上传并显示在同一App Store Connect记录中,用户在手表标签下添加Apple Watch截图。无论是本地Xcode、CI还是 Capgo 构建需要为两个 bundle ID 配置分发配置文件,并且必须使用 Xcode 26。

需要两个bundle ID的分发配置文件,并且必须使用Xcode 26。 Capgo 实时更新 code 实时更新 v 上下文:Capgo营销网站。角色:呼吁行动按钮或链接标签。见于:页面代理/[slug].astro。保留Capgo产品/品牌和开发者术语的原始形式。消息键`agents_page_loop_update_link`(代理页面循环更新链接)。

Wrap-up

SwiftUI用于手表,Capacitor用于其他所有内容,三条WatchConnectivity通道位于中间。安装 @capgo/capacitor-watch总结

实时更新 Capacitor 应用

当一个 web 层 bug 活跃时,通过 Capgo 发布修复,而不是等待几天的 app store 审核。用户在后台接收更新,而原生变化仍在正常审查路径中。

最新博客

Capgo 给您创建真正专业移动应用所需的最佳见解。