大多数浏览器和
Developer App 均支持流媒体播放。
-
了解 AccessorySetupKit
使用 AccessorySetupKit 提升你的配件设置体验。呈现精美的配对对话框,并在其中显示你的蓝牙或 Wi-Fi 配件的图像 — 无需访问“设置”App 就能实现。探索如何通过只将你的 App 与一个配件配对来加强隐私保护。此外,了解如何迁移现有配件,以便通过 AccessorySetupKit 进行管理。
资源
-
下载
大家好!我是 Yixin 来自 Apple 的 Bluetooth 团队
今天 我很高兴向大家介绍如何使用 AccessorySetupKit 获得更好的 配件设置体验 我们首先会进行简要概述
然后 深入了解 API 并一起创建一个示例 App
最后 我们将讨论 如何在配件选择器中 让你的配件呈现出色效果
让我们从概述开始
配件是我们生活中 极其重要的一部分 包括监测健康状况的健身追踪器 以及将艺术构想变为 现实的创意工具 在 iOS 18 和 iPadOS 18 中 我们创建了一些功能 来帮助你更好地设置配件
AccessorySetupKit 是一个全新框架 可为 App 提供一种简化 且保障隐私的方法 用于设置和管理 带有蓝牙和 Wi-Fi 的配件
只需调用某个 API 就会显示全新的配件选择器 其中列出了附近所有 可供你的 App 配对的配件 你会在选择器中看到自己配件的 高保真图案和友好名称
只需轻点一下 配件就能与你的 App 安全配对 并随时可供使用 完成这一轻点操作后 便可以 通过你的配件访问蓝牙和 Wi-Fi 前提是配件支持蓝牙和 Wi-Fi
App 可继续使用相同的 CoreBluetooth 和 NetworkExtension API 与配件进行通信
在“隐私与安全性”设置中 “配件”行是通过 AccessorySetupKit 管理的 所有配件的新位置 在每个配件的详情页面中 你的产品图像、 显示名称和硬件名称 可帮助用户轻松识别配件
用户可以修改配件名称 切换已配对 App 的访问权限 或将配件从系统中移除
新设计的配件页面还可以在 “蓝牙设置”和“Wi-Fi 设置”中找到 具体位置取决于你的配件 支持的技术
在 iOS 18 和 iPadOS 18 中 AccessorySetupKit 支持 蓝牙和 Wi-Fi 配件 那么 它是如何工作的呢 我们先了解一下 App 过去如何设置 配件以及 AccessorySetupKit 与过去的做法相比有什么不同
以前 App 需要请求获得 蓝牙使用权限 才能发现并连接到任一配件
App 需要另行请求加入配件 正在广播的 Wi-Fi 网络
另外可能还有额外的蓝牙配对请求 相比之下 AccessorySetupKit 为你和你的顾客 消除了所有障碍 只需点按一下 配件就会 与你的 App 配对 并向你的 App 授予 蓝牙和 Wi-Fi 访问权限 在新的配件选择器中 用户比以往更容易知晓 自己允许使用的是什么配件 因为选择器会显示配件的 友好名称和产品图像 以及配件通过无线方式 广播的硬件名称 这样用户就可以先确保配件真实性 然后再轻点进行设置 这一切都得益于 AccessorySetupKit 保障隐私的设计
你的 App 上显示的配件选择器 在单独的进程中运行 你的 App 会将发现规则 和素材发送给选择器进程 这些规则可以配置为 同时具有蓝牙和 Wi-Fi 接口 因此你可以在一个设置流程中 获得配件的所有 必要通信访问权限
一旦发现 配件就会 出现在选择器中 只需轻点一下 就能与你的 App 配对
以上就是 AccessorySetupKit 的概述 现在我们深入了解一下 API 我们将构建一个 App 用于设置数字骰子 并通过蓝牙同步掷骰子结果 你也可以下载示例项目 数字骰子由使用 CoreBluetooth 的 App 模拟 我们的示例 App 将与它连接 并订阅它的蓝牙 GATT 服务
为了使用 AccessorySetupKit 我们将在 App 的 Info.plist 中添加几个条目 我们将把 Bluetooth 放在 AccessorySetupKit - Supports 下 因为这是我们的示例 App 与骰子通信的方式 Wi-Fi 是这个数组中另一个 受支持的项目 然后 我们将为 配件的发现添加规则 无论是 Bluetooth Services、 Wi-Fi SSIDs 还是 Company Identifiers 由于我们的骰子有 2 种颜色变体 而且每种变体都广播 不同的服务 UUID 因此我们将这 2 个 UUID 放在 Bluetooth Services 下 现在 要在 App 中使用配件 有 3 个步骤 分别是 发现配件 授权使用配件 以及与配件通信 AccessorySetupKit 负责 发现和授权 而通信仍由 CoreBluetooth 和 NetworkExtension 处理 就像以前的 iOS 版本一样
我们在 AccessorySetupKit 中 遇到的第一个类 是 ASAccessorySession
这是一个中心对象 用于显示配件选择器、 发送事件通知以及管理配件
当我们要求 ASAccessorySession 显示选择器时 我们会提供一个 ASPickerDisplayItem 对象数组
ASPickerDisplayItem 定义 配件的名称和图像 以及一个发现描述符 用于告诉系统如何扫描配件
有了这些 我们就可以在 ASAccessorySession 上调用 showPicker 以开始选择器进程的 发现和授权操作 设置完成后 App 将通过会话对象 收到 ASAccessoryEvent 这个事件包含关于 新添加配件的重要信息 以便 App 与配件交互
现在让我们编写一些代码
正如前面提到的 我们首先要创建 一个 ASAccessorySession
这个会话将使用 DispatchQueue 和 eventHandler 进行激活 前者表示执行 API 调用的位置 后者供 App 用于 处理来自这个会话的事件
每当会话、配件或选择器有更新时 ASAccessoryEvent 就会传递给 事件处理程序 由于事件多种多样 你可以通过检查 eventType 来进行区分 使用 activated 事件作为信号 允许你通过读取会话中的 “accessories”属性来查询配件 或启动新的设置流程 稍后我们将进一步讨论事件处理 现在让我们学习如何通过 创建选择器显示项目 并调用配件选择器来启动设置流程
创建显示项目需要产品名称、 产品图像和发现描述符 通过 ASDiscoveryDescriptor 类 你可以将所有 必要的发现规则 合并为一个复合对象 系统会将扫描结果 与描述符内的所有规则进行匹配 以筛选出你关注的确切配件 ASDiscoveryDescriptor 非常灵活 有许多可自定的字段 如蓝牙服务 UUID 和 Wi-Fi SSID 请参阅 developer.apple.com/cn 上的文档 了解所有相关信息
要发现我们的骰子 发现描述符需要将它的 bluetoothServiceUUID 设置为骰子广播的 GATT 服务 UUID
由于粉色骰子和蓝色骰子 根据自身颜色来广播 不同的服务 UUID 因此我们需要使用 各自的服务 UUID 为每种颜色创建不同的发现描述符
然后 我们传入骰子的名称、图像 和相应的发现描述符 以便为这两种颜色变体的 骰子创建显示项目 这样 系统就可以 将这两种颜色变体 作为单独的配件进行扫描
你可以使用 ASPickerDisplayItem 的 更多属性来自定设置体验 请务必查看我们的文档 最后 我们在示例 App 中 设置了一个按钮 只需在激活的会话中 调用 showPicker 同时传入我们为粉色骰子和 蓝色骰子创建的显示项目即可 这时 配件选择器就会显示 并开始发现和设置进程 让我们开始吧
在桌面上 我有一台 模拟粉色骰子的手机 在我们的示例 App 中 如果我们轻点“Add”按钮 配件选择器就会滑出 并覆盖在 App 上 粉色骰子会立即被发现并显示 现在 如果我再用另一台 模拟手机打开我的蓝色骰子 就会看到骰子被发现并 以动画形式从屏幕边缘闪现 选择器会创建一个水平轮播界面 用于放置配件的精美素材 我们可以左右轻扫来选择 这次要设置的骰子 它们看起来都很可爱 但我们都喜欢亮粉色 让我们设置粉色骰子吧 连接...完成 粉色骰子现在可以 通过我们的 App 使用了 就是这么快
如果你的配件需要 更安全的配对方法 如数字比较和通行密钥输入 配件选择器会显示 各种蓝牙配对 UI 现在 粉色骰子已经 设置好了 接下来呢
当配件可以使用时 App 会收到 accessoryAdded 事件 通过读取这个事件中的 accessory 属性 我们会得到一个 ASAccessory 对象 代表我们新设置的骰子 其他一些事件包括 accessoryChanged 这个事件在配件属性更改时发送 例如显示名称可以在 “设置”中进行更改 当用户或 App 从系统中 移除配件时 就会发送 accessoryRemoved
ASAccessory 类代表 一个物理配件 它包含配件的授权状态 显示名称、发现描述符 以及配件的所有网络接口 包括 CBPeripheral 的 蓝牙标识符 如果配件广播 Wi-Fi 网络 那么还包括 Wi-Fi SSID 通过 accessoryAdded 事件 中的 ASAccessory 对象 我们可以使用 CoreBluetooth 连接到蓝牙外设
我们将像往常一样创建 CBCentralManager 由于我们在 Info.plist 中声明了 对 AccessorySetupKit 的支持 因此与以前版本的 iOS 不同 App 不会询问用户是否 允许使用蓝牙
CBCentralManager 状态 只有在 App 与配件 配对后才会开启 在我们的例子中 粉色骰子已与 AccessorySetupKit 配对 我们将使用 bluetoothIdentifier 获取 粉色骰子的蓝牙外设 当然 你也可以使用中央管理器的 扫描 API 来扫描外设 扫描结果只会返回 已与 App 配对的配件 然后 我们将连接到外设 连接成功后 就会发现它的 GATT 服务 这样我们就可以读取更新 并获得更新通知
如果你是初次使用 App 连接 蓝牙配件 请查阅 developer.apple.com/cn 上的 CoreBluetooth 文档了解更多详情 说了这么多 让我们尝试 连接到新设置的粉色骰子吧
我们可以从示例 App 连接 到骰子模拟器 让我们拿出模拟器 App 开始掷骰子吧
很好 掷骰子的结果会实时同步 到我们的示例 App 中 我们就是这样使用 AccessorySetupKit 来设置配件 然后使用现有的 网络 API 来连接配件 但是 如果你的 App 已获得授权 从而可以使用蓝牙来管理 现有配件而无需使用 AccessorySetupKit 该怎么办呢 App 如何从这个权限模型过渡到 AccessorySetupKit 中 更精细的模型呢
使用现有配件的 蓝牙外设标识符或 Wi-Fi SSID 你可以创建 ASMigrationDisplayItem 的实例 它是 ASPickerDisplayItem 的子类 将这些迁移项传递给 showPicker 调用 从而将现有配件升级为 由 AccessorySetupKit 进行管理 并在新的配件设置中找到它们 如果 showPicker 调用 只包含迁移项目 则会在 App 上 显示一个信息页面 向用户通知迁移情况 请注意 如果你选择将迁移项 与其他非迁移显示项一起传递 迁移只会在发现并设置 新配件时进行
这就是如何迁移现有配件 以便由 AccessorySetupKit 进行管理的方法 在我们结束今天的讲座之前 我想与大家分享一些 推荐的设计实践 这些实践能够真正提升 你的配件设置体验
配件选择器提供一个 180 x 120 磅值大小的 容器框 用于放置产品图像 确保你的图像有足够的像素分辨率 以便在各种缩放系数的 屏幕上都能清晰显示
务必在浅色和深色两种模式下 测试素材效果 图像背景应该是透明的 以便在两种模式下看起来都很美观
利用图像周围的透明边框 当系统将整个图像 缩放到容器框中时 透明边框越宽 屏幕上显示的配件就越小 通过调整透明边框来实现 内边距的效果 在选择器 UI 中 获得大小完美的图像
请记住 在用户设置配件时 App 的部分 UI 会被配件选择器遮挡住 不要进行在设置过程中 可能导致 UI 不可见的更新 你可以侦听 pickerDidPresent 和 pickerDidDismiss 事件 以跟踪选择器的显示和关闭
最后但也同样重要的是 虽然 showPicker API 随时可以调用 但为避免意外显示选择器 我们建议在显示选择器之前 向用户展示足够多的 关于添加新配件的上下文 如果可能的话 将 showPicker 调用绑定到一个按钮上 这样设置体验就始终由用户启动了
最后 我想重申 今天讲座的几个要点 AccessorySetupKit 是设置蓝牙 和 Wi-Fi 配件的全新方法 只需点按一下 App 就能同时 通过蓝牙和 Wi-Fi 访问配件 在用户初次体验你的产品 这一关键时刻 这项功能可最大限度地 降低给客户带来的不便
使用时 请在 App 的 Info.plist 中 添加 AccessorySetupKit 项目 创建 ASAccessorySession 并等待它激活 以启动任务 为每个要发现的配件创建一个 ASPickerDisplayItem 显示配件选择器以启动设置过程 并对事件作出适当响应 如果 App 已经使用 系统访问权限来管理现有配件 请使用 ASMigrationDisplayItem 将配件迁移至 AccessorySetupKit 注意素材的设计 确保配件在配件选择器内 以最佳方式展示给顾客
感谢观看
-
-
6:02 - Register event handler
import AccessorySetupKit // Create a session var session = ASAccessorySession() // Activate session with event handler session.activate(on: DispatchQueue.main, eventHandler: handleSessionEvent(event:)) // Handle event func handleSessionEvent(event: ASAccessoryEvent) { switch event.eventType { case .activated: print("Session is activated and ready to use") print(session.accessories) default: print("Received event type \(event.eventType)") } }
-
7:23 - Create picker display items
// Create descriptor for pink dice let pinkDescriptor = ASDiscoveryDescriptor() pinkDescriptor.bluetoothServiceUUID = pinkUUID // Create descriptor for blue dice let blueDescriptor = ASDiscoveryDescriptor() blueDescriptor.bluetoothServiceUUID = blueUUID // Create picker display items let pinkDisplayItem = ASPickerDisplayItem( name: "Pink Dice", productImage: UIImage(named: "pink")!, descriptor: pinkDescriptor ) let blueDisplayItem = ASPickerDisplayItem( name: "Blue Dice", productImage: UIImage(named: "blue")!, descriptor: blueDescriptor )
-
8:10 - Show picker
// Invoke accessory picker Button { session.showPicker(for: [pinkDisplayItem, blueDisplayItem]) { error in if let error { // Handle error } } } label: { Text("Add Dice") }
-
9:26 - Handle events
// Handle event func handleSessionEvent(event: ASAccessoryEvent) { switch event.eventType { case .accessoryAdded: let newDice: ASAccessory = event.accessory! case .accessoryChanged: print("Accessory properties changed") case .accessoryRemoved: print("Accessory removed from system") default: print("Received event with type: \(event.eventType)") } }
-
10:22 - Communicate with accessory
// Connect to accessory using CoreBluetooth let central = CBCentralManager(delegate: self, queue: nil) func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .poweredOn: // state will only be updated to poweredOn when you have paired accessories let peripheral = central.retrievePeripherals(withIdentifiers: [newDice.bluetoothIdentifier]).first central.connect(peripheral) default: print("Received event type \(event.eventType)") } } func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { peripheral.delegate = self peripheral.discoverServices(pinkUUID) }
-
11:58 - Migrate existing accessories
// Create migration items let pinkMigration = ASMigrationDisplayItem(name: "Pink Dice", productImage: UIImage(named: "pink")!, descriptor: pinkDescriptor) pinkMigration.peripheralIdentifier = pinkPeripheral.identifier // Present picker with migration items session.showPicker(for: [pinkMigration]) { error in if let error { // Handle error } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。