audioaccessorykit

audioaccessorykit

热门

借助 AudioAccessoryKit 为已配对的第三方蓝牙耳机(头戴式或入耳式)提供自动音频切换支持。适用于以下场景:主 App 注册音频配件、App Extension 上报佩戴/摘下状态或已连接源设备变更、以及处理 AccessoryControlDevice 的功能与错误。请勿用于常规 AVAudioSession 路由、蓝牙传输或初始配件配对。

967Star
49Fork
更新于 2026/7/31
SKILL.md
只读
名称
audioaccessorykit
描述

借助 AudioAccessoryKit 为已配对的第三方蓝牙耳机(头戴式或入耳式)提供自动音频切换支持。适用于以下场景:主 App 注册音频配件、App Extension 上报佩戴/摘下状态或已连接源设备变更、以及处理 AccessoryControlDevice 的功能与错误。请勿用于常规 AVAudioSession 路由、蓝牙传输或初始配件配对。

AudioAccessoryKit

为第三方音频配件提供自动音频切换支持及智能音频路由输入。支持主 App 向系统注册音频配件配置,并允许 App Extension 上报佩戴状态和已连接源设备变更,从而协助系统完成音频输出的自动切换。适用于 iOS 26.4+ / iPadOS 26.4+。

Beta 阶段提醒:AudioAccessoryKit 是 iOS 26.4 的新增框架。在依赖特定 API 细节前,请重新查阅 Apple 最新官方文档。

AudioAccessoryKit 基于 AccessorySetupKit 构建。配件必须先通过 AccessorySetupKit 完成配对,然后才能注册使用音频相关功能。核心类型为 AccessoryControlDevice,负责注册来自主 App(container app)的 Configuration,并应用来自 App Extension 的持续配置更新。

目录

快速配置

前置条件

  1. 使用 AccessorySetupKit 通过蓝牙完成配件配对,获取 ASAccessory 对象。
  2. 在主 App 和 Extension 中根据需要导入相关框架:
import AccessorySetupKit
import AudioAccessoryKit

框架可用性

平台 最低版本
iOS 26.4+
iPadOS 26.4+

在当前的 Xcode 26.6 工具链中,AudioAccessoryKit 仅包含在真机设备 SDK 中,暂未引入 iPhone Simulator 26.5 SDK。针对该 Target 请使用真机进行调试。如果 App 的其他模块必须支持模拟器编译,请隔离 Target 成员资格,或者使用 #if canImport(AudioAccessoryKit) 条件编译包裹导入与具体实现,并提供模拟器存根(stub)。

会话管理

注册配件

通过 AccessorySetupKit 完成配对后,在主 App 中传入描述配件支持的功能及初始状态的 AccessoryControlDevice.Configuration 来注册配件:

let accessory: ASAccessory  // 通过 AccessorySetupKit 配对获取

let configuration = AccessoryControlDevice.Configuration(
    devicePlacement: .offHead,
    deviceCapabilities: [.audioSwitching, .placement]
)

try await AccessoryControlDevice.register(accessory, configuration)

注册操作会激活指定的 Capabilities,并为系统提供参与音频路由决策所需的配置信息。

获取当前配置

在 App Extension 中,可以使用静态方法 current(for:) 获取设备的当前配置:

let device = try AccessoryControlDevice.current(for: accessory)
let currentConfig = device.configuration

该方法会返回与已配对 ASAccessory 关联的 AccessoryControlDevice 实例。设备对象暴露了 accessory 引用以及当前的 configuration。注意:Apple 将 current(for:) 标记为仅限 App Extension 使用。

更新配置

在 App Extension 中,使用 update(_:) 将配置变更推送给系统。注意:只能更新注册时已声明的 Capabilities 对应的字段:

let device = try AccessoryControlDevice.current(for: accessory)
var config = device.configuration

config.devicePlacement = .onHead
try await device.update(config)

建议采用门控写入流程(gated write workflow):先确认注册阶段已声明该功能,复制并修改 device.configuration,然后调用 try await update(_:)。该方法无返回值;请仅在调用成功后再更新 App 侧的镜像状态。若调用失败,请按照错误处理中的策略进行处理。Apple 将 update(_:) 标记为仅限 App Extension 使用。

音频切换

自动音频切换可以让系统根据佩戴状态和已连接的设备源,智能地将音频输出路由至最合适的设备。

开启自动音频切换

在上述标准注册流程中声明 .audioSwitching 即可。仅当配件能够持续上报佩戴状态变更时,才需要同时包含 .placement 及初始佩戴状态。

功能集合(Capabilities)

自动切换通常使用以下 AccessoryControlDevice.Capabilities

功能 作用
.audioSwitching 设备支持自动音频切换
.placement 设备可以上报其物理佩戴状态

可根据需求组合使用这些功能。如果配件无法向系统实时更新真实的佩戴状态,请勿声明 .placement

设备佩戴状态

在 App Extension 中上报配件的物理佩戴位置,以帮助系统做出路由决策。每当配件检测到佩戴位置发生变化时,即应更新佩戴状态。

佩戴状态枚举值

AccessoryControlDevice.Placement 定义了四种情况:

佩戴状态 含义
.inEar 配件已戴入耳中(例如入耳式耳机)
.onHead 配件已佩戴在头部(例如头戴式耳机)
.overTheEar 配件已罩在耳朵上(例如包耳式耳机)
.offHead 用户未佩戴配件

更新佩戴状态

config.devicePlacement = .inEar

请在上述标准的 current→copy→update 流程中应用此修改。

常见状态转换场景:

  • 用户戴上配件时:从 .offHead 转换为 .onHead.inEar
  • 摘下配件时:从 .onHead.inEar 转换为 .offHead
  • 检测到变化后须立即上报,以确保音频路由响应敏捷

已连接音频源

对于支持同时连接多个蓝牙设备的配件,需要从 App Extension 中告知系统当前已连接了哪些设备。这样系统才能从正确的来源路由音频。

设置音频源标识符

将已连接设备的蓝牙地址以 Data 形式传入:

let primaryBTAddress = Data([0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC])
config.primaryAudioSourceDeviceIdentifier = primaryBTAddress

let secondaryBTAddress = Data([0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45])
config.secondaryAudioSourceDeviceIdentifier = secondaryBTAddress

当蓝牙连接状态发生变化(新设备连接、现有设备断开)时更新这些标识符,然后调用标准的 update(_:) 流程。

配置属性说明

自动切换会用到以下配置字段:

属性 类型 作用
deviceCapabilities Capabilities 已声明的设备功能
devicePlacement Placement? 当前物理佩戴状态
primaryAudioSourceDeviceIdentifier Data? 主连接蓝牙设备地址
secondaryAudioSourceDeviceIdentifier Data? 次连接蓝牙设备地址

功能探索与查询

查询设备功能

在 App Extension 中,可通过配置检查设备已声明的功能:

let device = try AccessoryControlDevice.current(for: accessory)
let caps = device.configuration.deviceCapabilities

if caps.contains(.audioSwitching) {
    // 设备支持自动音频切换
}

if caps.contains(.placement) {
    // 设备支持上报物理佩戴状态
}

检查佩戴状态

读取当前佩戴状态以判断配件是否正在被佩戴:

let device = try AccessoryControlDevice.current(for: accessory)

if let placement = device.configuration.devicePlacement {
    switch placement {
    case .inEar, .onHead, .overTheEar:
        // 用户正在佩戴配件
        break
    case .offHead:
        // 用户未佩戴配件
        break
    @unknown default:
        break
    }
}

错误处理

AccessoryControlDevice.Error 涵盖了注册和更新过程中的失败情况:

错误 原因
.accessoryNotCapable 配件不支持请求的功能
.invalidRequest 请求参数无效
.invalidated 设备注册已失效/被作废
.unknown 发生了未知错误

捕获并处理注册及更新调用中抛出的错误:

let configuration = AccessoryControlDevice.Configuration(
    devicePlacement: .offHead,
    deviceCapabilities: [.audioSwitching, .placement]
)

do {
    try await AccessoryControlDevice.register(accessory, configuration)
} catch let error as AccessoryControlDevice.Error {
    switch error {
    case .accessoryNotCapable:
        // 配件硬件不支持请求的功能
        break
    case .invalidRequest:
        // 检查注册参数是否正确
        break
    case .invalidated:
        // 重新协调主 App 进行注册
        break
    case .unknown:
        // 记录日志、抛出或向上传播;Apple 未将其归类为暂态错误
        throw error
    @unknown default:
        throw error
    }
}

切勿直接假设 .invalidated.unknown 是暂态错误。请修正无效的功能声明或请求参数,丢弃已失效的句柄并通知主 App 重新评估注册,同时妥善抛出未知错误。参阅 错误恢复模式 了解完整的处理策略与失效交接流程。

常见误区

切勿:未通过 AccessorySetupKit 配对就直接注册

必须使用 AccessorySetupKit 完成配对后返回的 ASAccessory 对象来进行注册。

切勿:声明了 placement 功能却不上报佩戴状态

如果注册时声明了 .placement,Extension 必须在每次检测到状态变化时通过标准更新流程同步佩戴状态。

切勿:忽略多设备配件的连接状态变更

蓝牙连接一旦改变,应立即清空或替换主/次音频源标识符;使用陈旧的标识符会导致音频切换准确率下降。

切勿:遗漏对 invalidated 错误的处理

// 错误示范 —— 忽略失效错误,继续使用已失效的设备引用
try await device.update(config)  // 抛出 .invalidated,未被处理

// 正确示范 —— 丢弃句柄,让主 App 重新评估并重新注册
do {
    try await device.update(config)
} catch AccessoryControlDevice.Error.invalidated {
    await notifyContainerAppToReevaluateRegistration(accessory)
}

检查清单

  • [ ] 在调用 AudioAccessoryKit 注册前,配件已通过 AccessorySetupKit 完成配对
  • [ ] 已同时导入 AccessorySetupKitAudioAccessoryKit
  • [ ] 主 App 使用 AccessoryControlDevice.Configuration 调用了 register(_: _:)
  • [ ] App Extension 正确调用了 current(for:)update(_:)
  • [ ] 注册配置中的 Capabilities 与实际硬件支持相匹配
  • [ ] 更新配置时仅触及注册时已声明功能对应的字段
  • [ ] 声明 .placement 功能的同时配有持续的佩戴状态更新
  • [ ] 佩戴状态转换(戴上/摘下)能第一时间上报
  • [ ] 蓝牙连接发生变化时能够及时更新音频源设备标识符
  • [ ] 已妥善处理所有 AccessoryControlDevice.Error 情况,包含 @unknown default
  • [ ] update(_:) 调用使用了 try await 并进行了错误捕获
  • [ ] 设备引用失效(Invalidated)时能触发主 App 恢复注册流程
  • [ ] Deployment Target 已设为 iOS 26.4+ 或 iPadOS 26.4+

参考资料

<!-- truncated for translation batch; full body continues in source -->