iOS SDK
iKho Embedded SDK 的 iOS 接口形态设计:扫描并绑定 S1 录音卡、读取设备状态、把录音通过 BLE 或 Wi-Fi 同步到手机。SDK 尚未开放发放。
开发者平台处于内测阶段,接口契约以联调时提供的正式文档为准。账号由对接工程师开通,详见联系我们。
以下是 iKho Embedded SDK 的 iOS 接口形态设计。SDK 开放后,你的 iOS 应用可以把 S1 录音卡当作一等公民的采集设备:一次配置用户令牌,即可扫描、绑定、读取设备状态,并把设备上的录音同步到手机。SDK 采用代理(delegate)驱动,设备生命周期与同步进度都通过回调返回。方法名与参数以正式发布为准。
环境要求
SDK 开放后,集成需要满足以下条件,具体版本以正式发布说明为准。
| 项目 | 要求 |
|---|---|
| iOS 部署目标 | iOS 15.0 及以上(以正式发布说明为准) |
| Xcode | 15 及以上 |
| Swift | 5.9 及以上 |
| 测试设备 | 需真机 iPhone。SDK 计划以 arm64 真机框架分发,模拟器不支持设备联调 |
| 硬件 | 一张 iKho S1 录音卡(硬件规格以量产规格为准) |
安装
SDK 尚未开放发放,以下是开放后的集成方式。届时以私有 Swift Package / CocoaPods 分发,包名为 IKhoEmbedded;依赖地址、私有源与版本号随开通邮件发放,请勿硬编码到公共仓库,也不要凭本页示例去公共包源安装同名包。
// Package.swift —— 开放后适用,依赖地址与版本以开通邮件为准
dependencies: [
.package(url: "https://<你的内测 Git 地址>/ikho-embedded-ios.git", from: "0.9.0"),
],
targets: [
.target(
name: "YourApp",
dependencies: [
.product(name: "IKhoEmbedded", package: "ikho-embedded-ios"),
]
),
]
# Podfile —— 开放后适用,私有源地址与版本以开通邮件为准
source 'https://<你的内测 Podspec 源>'
source 'https://cdn.cocoapods.org/'
target 'YourApp' do
use_frameworks!
pod 'IKhoEmbedded', '~> 0.9.0'
end
# 安装依赖
# pod install
初始化
集成后在应用启动时用用户令牌配置一次 SDK。用户令牌由你的后端调用认证 API为每个终端用户签发,密钥仅保存在后端,切勿随包下发。认证 API 已经可用,不依赖 SDK 开放,可以先在服务端跑通签发流程。
import IKhoEmbedded
// 应用启动时调用一次(如 AppDelegate / App.init)
IKhoEmbedded.configure(userToken: "USER_ACCESS_TOKEN")
userToken
String
必填
region
IKhoRegion
默认 .chinaMainland
.chinaMainland(中国大陆)一个取值,海外区域规划中。所有设备与文件请求均走境内节点,不出境。刷新用户令牌
用户令牌过期或重新登录后,更新令牌即可,无需重新配置 SDK。
IKhoEmbedded.setUserToken("NEW_USER_ACCESS_TOKEN")
权限声明
SDK 通过蓝牙与 S1 通信,并在应用内提供录制与试听,集成后需在 Info.plist 声明以下条目,否则系统会拒绝相关能力。
<key>NSBluetoothAlwaysUsageDescription</key>
<string>用于连接 iKho 录音卡并同步设备上的录音</string>
<key>NSMicrophoneUsageDescription</key>
<string>用于在应用内录制与试听音频</string>
<!-- 后台保持蓝牙连接,支持后台同步 -->
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
</array>
Wi-Fi 快传另需在应用能力中开启 Hotspot Configuration entitlement,见下方「Wi-Fi 快传」。
扫描与绑定设备
设置代理后开始扫描,扫描结果通过 deviceScanResult 回调返回;选中目标设备调用 bind(device:) 完成加密绑定。
IKhoDeviceDelegate 的对象设为代理,调用 scanDevices()。deviceScanResult(_:) 中按序列号或用户选择挑出目标设备,并 stopScan()。bind(device:),绑定结果通过 deviceBindChanged(_:) 返回。import IKhoEmbedded
final class DeviceCoordinator: IKhoDeviceDelegate {
private var lastSerial: String?
func start() {
IKhoDeviceManager.shared.delegate = self
IKhoDeviceManager.shared.scanDevices()
}
// 扫描到设备时回调(可能多次触发)
func deviceScanResult(_ devices: [IKhoDevice]) {
guard let target = devices.first(where: { $0.serialNumber == lastSerial })
?? devices.first else { return }
IKhoDeviceManager.shared.stopScan()
IKhoDeviceManager.shared.bind(device: target)
}
// 绑定结果
func deviceBindChanged(_ result: IKhoBindResult) {
if result.bound { print("已绑定 \(result.serialNumber)") }
}
}
一台 S1 同一时间只能绑定一个应用(用于文件加密与安全隔离)。绑定与应用安装绑定,用户卸载你的应用前请先解绑,否则设备无法再绑定到其他应用。
设备状态与电量
握手完成后,设备总体状态(电量、充电、剩余存储)通过 deviceStateChanged(_:) 回调返回;电量或充电变化会额外触发 deviceBatteryChanged。也可主动调用 refreshDeviceState() 拉取一次。
func deviceStateChanged(_ state: IKhoDeviceState) {
print("电量 \(state.batteryLevel)% · 充电中 \(state.isCharging)")
print("剩余存储 \(state.freeStorage) / \(state.totalStorage) 字节")
}
// 主动刷新一次设备状态
IKhoDeviceManager.shared.refreshDeviceState()
录音列表与同步
调用 fetchRecordings() 读取设备上的录音清单(默认走 BLE 通道),结果通过 recordingListUpdated(_:) 返回。选中某条录音调用 exportAudio 同步到手机,进度与结果通过代理回调。
// 读取设备上的录音列表(默认 BLE 通道)
IKhoDeviceManager.shared.fetchRecordings()
func recordingListUpdated(_ recordings: [IKhoRecording]) {
for r in recordings {
print("\(r.id) · \(r.durationSeconds)s · \(r.sizeBytes) bytes")
}
}
// 把某条录音同步到手机
IKhoDeviceManager.shared.exportAudio(recordingId: recording.id, format: .m4a)
func exportProgress(recordingId: String, progress: Int) { /* 0–100 */ }
func exportCompleted(recordingId: String, outputPath: String) { /* 文件已就绪 */ }
func exportFailed(recordingId: String, error: IKhoError) { /* 处理错误 */ }
大文件同步建议:显示进度并预告耗时;开启后台/断点续传,避免退到后台中断;大批量或大文件优先用 Wi-Fi 快传。
Wi-Fi 快传
BLE 之外,S1 支持 Wi-Fi 快传通道,吞吐显著高于 BLE,适合批量或大文件同步。开启后,后续 exportAudio 会在通道就绪后自动优先走 Wi-Fi。
// 开启 Wi-Fi 快传(设备会开启热点,SDK 自动接入)
IKhoDeviceManager.shared.setWiFiTransfer(enabled: true)
// 通道就绪后再发起同步
func wifiTransferReady() {
IKhoDeviceManager.shared.exportAudio(recordingId: recording.id, format: .m4a)
}
Wi-Fi 快传注意点:
- 需在应用能力中开启
Hotspot Configurationentitlement。 - 快传期间手机会临时接入 S1 的热点,过程中普通网络可能短暂中断。
- 握手或传输失败会自动回退到 BLE 同步。
导出音频
exportAudio 支持指定导出格式;多条录音可用 exportAudioBatch 批量导出,进度按条汇总。
// 单条导出,指定格式(mp3 / m4a / wav)
IKhoDeviceManager.shared.exportAudio(recordingId: id, format: .mp3)
// 批量导出多条录音
IKhoDeviceManager.shared.exportAudioBatch(recordingIds: selectedIds, format: .wav)
func exportBatchProgress(completed: Int, total: Int) {
print("已完成 \(completed) / \(total)")
}
| 格式 | 取值 | 适用 |
|---|---|---|
| MP3 | .mp3 | 通用、体积小,便于回放与上传转写 |
| M4A | .m4a | AAC 封装,画质与体积均衡,iOS 原生友好 |
| WAV | .wav | 无损,用于二次处理或高精度转写 |
后台运行与 BLE 重连
在 Info.plist 声明 bluetooth-central 后台模式(见上方「权限声明」)后,应用退到后台时 SDK 保持 BLE 连接,进行中的同步继续执行;系统在后台会降低蓝牙事件频率,大文件同步耗时可能长于前台。未声明该模式时,应用挂起后连接会被系统回收,回到前台需重新连接。
连接意外中断(超出距离、设备关机)时,SDK 默认开启自动重连:向系统登记待连请求,设备回到近场即自动恢复连接,前后台均生效,结果通过 deviceConnectionChanged(_:) 回调返回。未完成的同步在重连后从断点继续。
// 自动重连默认开启,可按需关闭
IKhoDeviceManager.shared.setAutoReconnect(enabled: true)
// 连接状态变化(断开与重连成功都会回调)
func deviceConnectionChanged(_ state: IKhoConnectionState) {
switch state {
case .connected:
print("已连接,恢复同步")
case .reconnecting:
print("连接中断,等待设备回到近场")
case .disconnected:
print("已断开")
}
}
审核提示:App Store 审核会核对后台模式声明与实际功能是否一致。声明 bluetooth-central 时,请在应用描述或审核备注中说明用途,例如「与录音配件保持蓝牙连接,在后台继续同步音频」;若你的应用不需要后台同步,可不声明该模式,改为回到前台后重新连接。
线程模型与回调队列
IKhoDeviceDelegate 的全部回调默认在 SDK 内部串行队列触发,保证事件按发生顺序抵达,但不在主线程。更新 UI、写入 @Published 属性或触发界面刷新前,必须切回主线程。
func exportProgress(recordingId: String, progress: Int) {
// 回调在 SDK 内部队列,更新 UI 前切回主线程
DispatchQueue.main.async {
self.progressText = "\(progress)%"
}
}
也可以在初始化后把回调队列整体指定为主队列。SDK 内部的扫描、传输仍在自有队列执行,仅回调的派发位置改变。
// 让所有代理回调直接派发到主队列
IKhoDeviceManager.shared.callbackQueue = .main
回调队列指定为主队列后,请勿在回调内执行耗时的同步操作(如大文件读写),以免阻塞界面。
存储占用与清理
同步完成的音频写入应用沙盒内 SDK 专属的缓存目录,exportCompleted 返回的 outputPath 即位于其中;缓存随应用卸载一并删除,不占用系统相册或共享空间,且默认排除在 iCloud 备份之外。缓存会随同步量增长,建议在设置页向用户展示占用并提供清理入口。
// 查询当前音频缓存占用(字节)
let usedBytes = IKhoDeviceManager.shared.audioCacheSize()
print("缓存占用 \(usedBytes) 字节")
// 清理指定日期之前的缓存文件
let thirtyDaysAgo = Calendar.current.date(byAdding: .day, value: -30, to: Date())!
IKhoDeviceManager.shared.clearAudioCache(olderThan: thirtyDaysAgo)
// 清空全部音频缓存
IKhoDeviceManager.shared.clearAudioCache()
清理只删除 SDK 缓存中的本地副本,不影响设备上的原始录音,也不影响你已复制到业务目录的文件。清理后再次需要某条录音时,重新发起同步即可。
解绑设备
用户切换应用或卸载前,调用 unbind(clear:) 解除绑定并清除本地绑定态。
// 解绑当前设备并清除本地绑定态
IKhoDeviceManager.shared.unbind(clear: true)
clear
Bool
必填
true 时清除全部本地连接与缓存的绑定信息。错误码
SDK 层错误通过 didEncounterError(_:) 或各操作的失败回调返回,IKhoError 携带以下错误码。
| 错误码 | 含义 | 建议处理 |
|---|---|---|
IKHO_ERR_UNAUTHORIZED | 用户令牌无效或已过期 | 重新签发用户令牌后调用 setUserToken |
IKHO_ERR_BLE_UNAVAILABLE | 蓝牙未开启或未授权 | 引导用户在系统设置开启蓝牙并授予权限 |
IKHO_ERR_DEVICE_NOT_FOUND | 扫描超时未发现设备 | 确认设备已开机且在近场,重试扫描 |
IKHO_ERR_BIND_OCCUPIED | 设备已被其他应用绑定 | 提示用户在原应用先解绑,再重试绑定 |
IKHO_ERR_CONNECTION_LOST | 与设备的连接中断 | 靠近设备后自动重连,或手动重新扫描 |
IKHO_ERR_TRANSFER_FAILED | 音频同步过程中断 | 支持断点续传,重新发起同步 |
IKHO_ERR_STORAGE_FULL | 手机本地存储空间不足 | 清理空间后重试导出 |
IKHO_ERR_WIFI_HANDSHAKE | Wi-Fi 快传握手失败 | 回退到 BLE 同步或稍后重试 |
代理回调一览
所有设备事件通过 IKhoDeviceDelegate 返回。其中 deviceStateChanged 为唯一必须实现的回调,其余可按需实现。
| 分组 | 回调 | 说明 |
|---|---|---|
| 连接 | deviceScanResult(_:) | 扫描结果更新 |
| 连接 | deviceConnectionChanged(_:) | 连接状态变化(已连接 / 断开) |
| 连接 | deviceBindChanged(_:) | 绑定状态变化 |
| 状态 | deviceStateChanged(_:) | 必须实现。握手完成后返回设备总体状态(电量 / 充电 / 存储) |
| 状态 | deviceBatteryChanged(level:isCharging:) | 电量或充电状态变化 |
| 录音 | recordingStarted(_:) | 设备开始录音 |
| 录音 | recordingStopped(_:) | 设备停止录音 |
| 录音 | recordingListUpdated(_:) | fetchRecordings() 结果返回 |
| 同步 | exportProgress(recordingId:progress:) | 单条同步进度,0–100 |
| 同步 | exportCompleted(recordingId:outputPath:) | 单条同步完成,文件写入 outputPath |
| 同步 | exportFailed(recordingId:error:) | 单条同步失败 |
| 同步 | exportBatchProgress(completed:total:) | 批量同步进度 |
| Wi-Fi | wifiTransferReady() | Wi-Fi 快传通道就绪,可发起同步 |
| 错误 | didEncounterError(_:) | 其他 SDK 层错误 |
回调默认在 SDK 内部队列触发,更新 UI 或已发布状态前请切回主线程。
下一步
现在就能跑:用自己的音频走通认证、上传与转写三组接口。
起步应用的集成路径与界面构成,仓库待 SDK 开放后提供。
桥模块的接口形态,同样待 SDK 开放后提供。