使用 AudioAccessoryKit 為已配對的第三方藍牙耳機或耳塞式耳機提供自動音訊切換支援。適用於配套應用程式(companion app)註冊音訊配件、應用程式擴充套件(app extension)回報佩戴/取下狀態或已連接的來源裝置變更,或者需要處理 AccessoryControlDevice 功能與錯誤的情境。請勿用於一般 AVAudioSession 音訊路由、藍牙傳輸或初始配件配對。
AudioAccessoryKit
為第三方音訊配件提供自動音訊切換支援與智慧音訊路由輸入。讓配套應用程式機能向系統註冊音訊配件設定,並允許應用程式擴充套件回報佩戴位置與已連接來源的變更,以協助系統切換音訊輸出。
支援 iOS 26.4+ / iPadOS 26.4+。
Beta 敏感提示: AudioAccessoryKit 為 iOS 26.4 新增功能。在依賴特定 API 細節前,請重新確認最新的 Apple 官方文件。
AudioAccessoryKit 建構於 AccessorySetupKit 之上。配件必須先透過 AccessorySetupKit 完成配對,才能註冊音訊相關功能。
核心型別為 AccessoryControlDevice,它負責從容器應用程式(container app)註冊 Configuration,並套用來自應用程式擴充套件(app extension)的持續設定更新。
目錄
設定
先決條件
- 透過 AccessorySetupKit 使用藍牙配對配件。這會取得一個
ASAccessory物件。 - 在容器應用程式與擴充套件中,依需求匯入架構(framework):
import AccessorySetupKit
import AudioAccessoryKit
架構支援度
| 平台 | 最低版本要求 |
|---|---|
| iOS | 26.4+ |
| iPadOS | 26.4+ |
在目前的 Xcode 26.6 工具鏈中,AudioAccessoryKit 僅存在於實體裝置 SDK 中,未包含在 iPhone 模擬器 26.5 SDK 中。請使用實體裝置作為此 Target 的建置目標。若應用程式的其他部分必須為模擬器進行建置,請隔離 Target 成員資格,或使用 #if canImport(AudioAccessoryKit) 保護 import 與實作邏輯,並提供模擬器存根(stub)。
工作階段管理
註冊配件
透過 AccessorySetupKit 完成配對後,請從容器應用程式傳入描述配件所支援功能與初始狀態的 AccessoryControlDevice.Configuration 來註冊配件:
let accessory: ASAccessory // 從 AccessorySetupKit 配對取得
let configuration = AccessoryControlDevice.Configuration(
devicePlacement: .offHead,
deviceCapabilities: [.audioSwitching, .placement]
)
try await AccessoryControlDevice.register(accessory, configuration)
註冊會啟用指定的功能,並提供系統參與音訊路由決策所需的設定。
取得目前設定
在應用程式擴充套件中,使用靜態方法 current(for:) 存取裝置的目前設定:
let device = try AccessoryControlDevice.current(for: accessory)
let currentConfig = device.configuration
這會傳回與已配對 ASAccessory 關聯的 AccessoryControlDevice 實例。該裝置同時公開 accessory 參考與目前的 configuration。Apple 將 current(for:) 標記為僅限應用程式擴充套件使用(app-extension-only)。
更新設定
在應用程式擴充套件中,使用 update(_:) 將設定變更推送至系統。請僅更新在註冊期間已宣告功能的欄位:
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(_:)。
此方法不傳回設定值;請僅在呼叫成功後才更新應用程式端的對映狀態(mirror)。若失敗,請依照錯誤處理中的處置方式處理。Apple 將 update(_:) 標記為僅限應用程式擴充套件使用。
音訊切換
自動音訊切換可讓系統根據佩戴位置與已連接來源,智慧地將音訊輸出路由至正確的裝置。
啟用音訊切換
在上述標準註冊流程中宣告 .audioSwitching。僅在配件能夠持續回報佩戴位置變更時,才包含 .placement 與初始佩戴位置。
功能宣告
自動切換通常使用以下 AccessoryControlDevice.Capabilities:
| 功能 | 用途 |
|---|---|
.audioSwitching |
裝置支援自動音訊切換 |
.placement |
裝置可回報其物理佩戴位置 |
請依需求組合功能。除非配件能夠持續為系統更新真實的佩戴狀態,否則請勿宣告 .placement。
裝置佩戴位置
從應用程式擴充套件回報配件的物理位置,以協助系統做出路由決策。每當配件偵測到位置變更時即進行更新。
佩戴位置數值
AccessoryControlDevice.Placement 定義了四種情況:
| 佩戴位置 | 含意 |
|---|---|
.inEar |
配件已塞入耳中(例如:入耳式耳機/耳塞) |
.onHead |
配件戴在頭上(例如:頭戴式耳機) |
.overTheEar |
配件罩在耳朵上(例如:耳罩式耳機) |
.offHead |
配件未被佩戴 |
更新佩戴位置
config.devicePlacement = .inEar
在上述標準的「取得目前設定→複製→更新」順序中套用此修改。
常見的狀態轉換:
- 使用者戴上配件時:從
.offHead轉換為.onHead或.inEar - 取下配件時:從
.onHead或.inEar轉換為.offHead - 每當偵測到變更時立即更新,以維持即時回應的音訊路由
已連接的音訊來源
對於可同時連接多個藍牙裝置的配件,請從應用程式擴充套件通知系統目前已連接哪些裝置。這能讓系統從適當的來源路由音訊。
設定音訊來源識別碼
將已連接裝置的藍牙位址以 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? |
次要已連接藍牙裝置位址 |
功能探索
查詢功能
在應用程式擴充套件中,透過裝置的設定檢查其已宣告的功能:
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:
// 再次協調容器應用程式進行註冊
break
case .unknown:
// 記錄、呈現或向上拋出;Apple 未將此歸類為暫時性錯誤
throw error
@unknown default:
throw error
}
}
切勿推斷 .invalidated 或 .unknown 是暫時性的。請修正無效的功能或請求參數、捨棄已失效的控制代碼(handle),並在適當時通知容器應用程式重新評估註冊,同時適當呈現未指定的錯誤。完整的處置方式與失效交接流程請參閱 Error Recovery Patterns。
常見錯誤
切勿:在透過 AccessorySetupKit 配對前就進行註冊
僅註冊已完成 AccessorySetupKit 配對後傳回的 ASAccessory。
切勿:宣告佩戴位置功能卻不更新佩戴位置
若註冊時宣告了 .placement,擴充套件必須在每次偵測到狀態轉換時,使用標準更新流程更新佩戴位置。
切勿:忽略多裝置配件的連接狀態變更
每當藍牙連接變更時,請清除或替換主次要來源識別碼;過期的識別碼會降低切換精準度。
切勿:忘記處理失效(invalidated)錯誤
// 錯誤 -- 忽略失效狀態,繼續使用過期的裝置參考
try await device.update(config) // 拋出 .invalidated,未處理
// 正確 -- 捨棄控制代碼並讓容器應用程式重新評估註冊
do {
try await device.update(config)
} catch AccessoryControlDevice.Error.invalidated {
await notifyContainerAppToReevaluateRegistration(accessory)
}
審查清單
- [ ] 在進行 AudioAccessoryKit 註冊前,配件已透過 AccessorySetupKit 完成配對
- [ ] 已同時匯入
AccessorySetupKit與AudioAccessoryKit - [ ] 容器應用程式使用
AccessoryControlDevice.Configuration呼叫register(_: _:) - [ ] 應用程式擴充套件呼叫
current(for:)與update(_:) - [ ] 註冊設定中的功能與實際硬體支援相符
- [ ] 更新操作僅觸及註冊時已宣告功能的欄位
- [ ]
.placement功能伴隨持續的佩戴位置更新 - [ ] 佩戴位置轉換(戴上/取下)均能即時回報
- [ ] 音訊來源裝置識別碼在藍牙連接變更時能同步更新
- [ ] 已處理所有
AccessoryControlDevice.Error情況,包含@unknown default - [ ]
update(_:)呼叫使用try await並進行錯誤處理 - [ ] 失效的裝置參考能觸發容器應用程式的註冊復原流程
- [ ] 部署目標設定為 iOS 26.4+ 或 iPadOS 26.4+
參考資料
- 延伸模式(註冊流程、佩戴位置監控、多裝置協調):references/audioaccessorykit-patterns.md
- [AudioAccessoryKit
<!-- truncated for translation batch; full body continues in source -->




