# 36.2 iOS 节点应用

> **生成模型**：Claude Opus 4.6 (anthropic/claude-opus-4-6) **Token 消耗**：输入 \~180k tokens，输出 \~5k tokens（本节）

***

OpenClaw 的 iOS 应用与 macOS 应用有本质区别：macOS 应用是一个**控制端**（operator），而 iOS 应用是一个**节点**（node）。节点不运行 Gateway，而是连接到局域网中的 Gateway 实例，将 iPhone/iPad 的硬件能力（摄像头、屏幕、麦克风、位置）暴露给 AI Agent 使用。

## 36.2.1 Bonjour 配对

### 服务发现

iOS 应用启动后的第一件事是找到局域网中的 Gateway。这通过 Apple 的 **Bonjour**（mDNS/DNS-SD）协议实现：

```swift
// apps/ios/Sources/Gateway/GatewayConnectionController.swift
@MainActor
@Observable
final class GatewayConnectionController {
    private let discovery = GatewayDiscoveryModel()
    private(set) var gateways: [GatewayDiscoveryModel.DiscoveredGateway] = []
    
    init(appModel: NodeAppModel, startDiscovery: Bool = true) {
        GatewaySettingsStore.bootstrapPersistence()
        self.updateFromDiscovery()
        self.observeDiscovery()
        if startDiscovery { self.discovery.start() }
    }
    
    // 场景生命周期感知
    func setScenePhase(_ phase: ScenePhase) {
        switch phase {
        case .background:  self.discovery.stop()   // 后台时停止发现
        case .active, .inactive: self.discovery.start()
        }
    }
}
```

> **衍生解释 — Bonjour (mDNS/DNS-SD)**
>
> Bonjour 是 Apple 对两个 IETF 标准的实现：mDNS（多播 DNS，RFC 6762）和 DNS-SD（基于 DNS 的服务发现，RFC 6763）。设备在局域网中通过 UDP 多播（224.0.0.251:5353）广播和发现服务，无需中央 DNS 服务器。例如，Gateway 广播一个 `_openclaw._tcp` 服务，iOS 应用通过 `NWBrowser` 发现它并获取 IP 和端口。

发现到 Gateway 后，用户可以选择连接。连接参数包括主机地址、端口、TLS 配置和认证凭据：

```swift
func connect(_ gateway: DiscoveredGateway) async {
    let host = resolveGatewayHost(gateway)
    let port = gateway.gatewayPort ?? 18789
    let tlsParams = resolveDiscoveredTLSParams(gateway: gateway)
    let url = buildGatewayURL(host: host, port: port, useTLS: tlsParams?.required == true)
    // 通过 WebSocket 连接 Gateway，角色为 "node"
    startAutoConnect(url: url, tls: tlsParams, token: token, password: password)
}
```

### 应用入口

iOS 应用的入口很简洁：

```swift
// apps/ios/Sources/OpenClawApp.swift
@main
struct OpenClawApp: App {
    @State private var appModel: NodeAppModel
    @State private var gatewayController: GatewayConnectionController

    var body: some Scene {
        WindowGroup {
            RootCanvas()
                .environment(appModel)
                .environment(appModel.voiceWake)
                .environment(gatewayController)
                .onOpenURL { url in
                    Task { await appModel.handleDeepLink(url: url) }
                }
                .onChange(of: scenePhase) { _, newValue in
                    appModel.setScenePhase(newValue)
                    gatewayController.setScenePhase(newValue)
                }
        }
    }
}
```

`NodeAppModel` 是应用的核心状态模型，管理与 Gateway 的会话、设备能力、语音唤醒等。

## 36.2.2 Canvas 表面

iOS 应用的主界面是一个 **Canvas 表面**——它不仅是聊天界面，更是 AI Agent 可以直接操控的"画布"。Agent 可以发送 Canvas 命令来控制界面内容，实现展示图表、动画、交互式控件等能力。

源码结构：

```
apps/ios/Sources/
├── RootCanvas.swift        # 主 Canvas 视图
├── Camera/                 # 摄像头能力
├── Screen/                 # 屏幕录制能力
├── Voice/                  # 语音能力
├── Location/               # 位置能力
├── Chat/                   # 聊天界面
├── Gateway/                # 连接管理
├── Model/                  # 数据模型
├── Settings/               # 设置界面
├── Status/                 # 状态显示
└── SessionKey.swift        # 会话键管理
```

Canvas 命令由共享库 `OpenClawKit` 定义（见 28.4 节），包括 DOM 操作、文件显示、A2UI（Agent-to-UI）动作等。

## 36.2.3 Voice Wake / Talk Mode

iOS 节点同样支持语音唤醒和对话模式，实现方式与 macOS 类似（共享自 `OpenClawKit`），但有以下适配：

1. **后台限制**：iOS 不允许应用在后台无限制运行音频捕获。语音唤醒在应用进入后台时暂停（`setScenePhase(.background)` → 停止发现和录音）
2. **音频会话管理**：需要正确配置 `AVAudioSession` 的 category 和 mode，确保与系统音频（如电话、音乐）和平共处
3. **权限模型**：iOS 的权限弹窗是一次性的——如果用户拒绝了麦克风权限，只能引导到系统设置中手动开启

## 36.2.4 摄像头与屏幕录制

iOS 节点的独特价值在于将移动设备的传感器暴露给 AI Agent：

**摄像头**（`Camera/`）：Agent 可以请求拍照或实时视频帧，用于视觉分析。摄像头能力通过 `AVCaptureSession` 实现。

**屏幕录制**（`Screen/`）：Agent 可以请求屏幕截图或录制。iOS 使用 `ReplayKit` 框架提供屏幕内容捕获。

这些能力在连接 Gateway 时通过 `caps`（capabilities）字段声明：

```swift
// apps/shared/OpenClawKit/Sources/OpenClawKit/Capabilities.swift
public enum OpenClawCapability: String, Codable, Sendable {
    case canvas         // 画布操控能力
    case camera         // 摄像头
    case screen         // 屏幕录制
    case voiceWake      // 语音唤醒
    case location       // 位置信息
}
```

Gateway 根据节点声明的能力来决定哪些工具可以使用——只有连接了具备 `camera` 能力的节点时，Agent 才能调用拍照工具。

> **v2026.3.9 更新** —— iOS 应用新增了 **Home Canvas 刷新**机制。当 iOS 应用从后台恢复到前台时，Home Canvas 会自动刷新其内容（通过向 Gateway 发送 `canvas.refresh` 请求），确保用户看到的 Canvas 状态始终是最新的。此前，后台恢复后 Canvas 可能显示过时内容，需要用户手动触发刷新。

***

## 本节小结

1. **iOS 应用是"节点"角色**，不运行 Gateway，而是通过 Bonjour 发现并连接到局域网中的 Gateway 实例。
2. **服务发现**基于 mDNS/DNS-SD 协议，应用自动感知场景生命周期（前台/后台）来启停发现。
3. **Canvas 表面**是主界面，AI Agent 可以通过命令直接操控界面内容。
4. **语音唤醒**共享自 OpenClawKit，但受 iOS 后台执行限制。
5. **摄像头和屏幕录制**是节点的核心硬件能力，通过 capabilities 声明让 Gateway 知晓可用工具。
