Skip to Content

iOS:通过蓝牙连接(原生)

在 WKWebView 中运行 @onekeyfe/hd-common-connect-sdk,通过底层传输插件桥接到 CoreBluetooth。以原生 iOS 示例 和底层传输插件契约为起点。

原生宿主负责蓝牙生命周期;SDK 负责协议探测、帧处理和请求/响应处理。

应用配置

  • 添加 NSBluetoothAlwaysUsageDescription,说明连接硬件钱包的用途。支持较旧 iOS 版本时,核对其额外要求。
  • 使用 CoreBluetooth,以及原生示例声明的桥接依赖。
  • 在授予蓝牙权限的真实 iOS 设备上测试。扫描前处理权限拒绝、受限和蓝牙关闭状态。

构建并加载 JavaScript 包

在 hardware-js-sdk 仓库中运行:

cd packages/connect-examples/native-ios-example/web yarn yarn build

将完整的 web_dist/ 目录加入应用 target 的资源,保留相对资源路径。依次创建 WKWebView 和桥接、注册原生处理器,再加载打包的 HTML。不要让具有设备访问能力的 WebView 导航到不可信内容。

使用示例的完整实现及依赖版本。自定义桥接必须满足以下完成条件。

连接生命周期

CBCentralManager.connect() 只是发起连接尝试,不能在这时就将 JS connect 请求返回为成功。

阶段必须处理的事项
扫描等待 .poweredOn,按 OneKey 服务过滤、去重,并在设定的截止时间停止扫描。
选择匹配用户所选外设 UUID;通过扫描恢复连接时也只能连接该外设。
连接等待 didConnect;didFailToConnect 或超时均返回失败。
发现查找所需服务、写入和通知特征,处理发现错误及缺失特征。
订阅启用通知,等待 didUpdateNotificationStateFor 无错误且通知已启用。
就绪此时才完成待处理的 JS 连接请求;每个桥接回调只完成一次。

通知订阅完成的依据见 Apple 的通知状态回调 。

用途UUID
服务00000001-0000-1000-8000-00805f9b34fb
写入00000002-0000-1000-8000-00805f9b34fb
通知00000003-0000-1000-8000-00805f9b34fb

有序收发

  • send 接收 SDK 准备的十六进制数据。格式错误或长度为奇数时返回失败,不要静默丢弃无效字节。
  • 遵守请求的写入模式和外设写入容量。无响应写入应遵守 CoreBluetooth 背压;写入入队不代表设备已经完成 SDK 请求。
  • 按外设维护原始通知块队列。每次 receive(uuid) 返回一个未修改的十六进制数据块,不解析 V1 头部,也不补齐到 USB 包长。
  • 断连时拒绝待处理操作,清空旧连接的通知队列;重连后不得读到上一条连接的残留数据。
  • 用原生写入容量实现 getProtocolV2PacketCapacity。参见插件接口。

SDK 与钱包身份

用 env: 'lowlevel' 和桥接插件初始化 SDK,并检查结果。通过 searchDevices() 选择 connectId,再调用 getDeviceState(connectId) 读取 identity.deviceId。

iOS 外设 UUID 用于连接路由,不是种子身份。重连后重新读取状态;如果目标钱包身份变化,应停止操作。参见标识符。

PIN、密码短语与确认

在受保护调用前接入 UI 事件。原生弹窗可以收集输入或显示提示,但 JS 处理器必须保留原请求的响应关联字段。

只回答输入请求。REQUEST_BUTTON、REQUEST_PASSPHRASE_ON_DEVICE 和设备端 PIN 事件都是通知。宿主回调返回“OK”不能在设备上批准操作。用户取消时,取消对应连接上的 SDK 操作,并使待处理 UI 回调失效。

地址确认和签名应使用同一个明确的标准/隐藏钱包绑定。不要将宿主端输入的 PIN 或密码短语写入日志或持久化存储。

验证与恢复

测试权限拒绝、蓝牙关闭、特征缺失、通知订阅失败、设备输入、取消和重连。让用户在真实设备上确认一个地址,再判断桥接是否可用。

超时后不要自动重放签名或固件升级。参见断线与重连。

最后更新于