7d9ff3081dfc98bb175fd21d236845759decedf0
Duooomi BLE SDK (Native iOS)
纯原生 Swift 蓝牙 SDK,使用 CoreBluetooth 直接操作,零第三方依赖。
架构设计
调用方 (宿主 App)
│
└── DuooomiBleSDK (ObservableObject) ← 公开 API + 状态观察
│
├── BleClient ← CoreBluetooth 封装
│ CBCentralManager / CBPeripheral
│
├── BleProtocolService ← 协议帧收发 + 分包重组
│ ProtocolManager (编码/解码/校验)
│
├── DeviceInfoService ← 设备命令 (JSON 请求/响应)
│
└── FileTransferService ← 文件下载 + 二进制传输
目录结构
Sources/DuooomiBleSDK/
├── DuooomiBleSDK.swift # 公开 API facade (ObservableObject)
├── Core/
│ ├── BleClient.swift # CoreBluetooth 封装 (scan/connect/write/notify)
│ └── BleTypes.swift # ConnectionState, DiscoveredDevice, DuooomiBleError
├── Protocol/
│ ├── ProtocolConstants.swift # BLE UUID, 帧常量, CommandType 枚举
│ ├── ProtocolFrame.swift # 协议帧结构体
│ └── ProtocolManager.swift # 帧编解码、校验和、分片/重组
├── Services/
│ ├── BleProtocolService.swift # 监听通知、解析帧、分包重组、帧发送
│ ├── DeviceInfoService.swift # 设备命令发送 (JSON payload)
│ └── FileTransferService.swift # 文件下载 + 二进制传输
└── Models/
├── DeviceInfo.swift # 设备信息 (allspace/freespace/brand/...)
├── VersionInfo.swift # 固件版本
├── BindingResponse.swift # 绑定响应 (sn/success/contents)
├── UnbindResponse.swift # 解绑响应
├── DeleteFileResponse.swift # 删除文件响应
└── PrepareTransferResponse.swift # 预传输响应
协议帧格式
[head:1][type:1][subpageTotal:2][curPage:2][dataLen:2][data:N][checksum:1]
| 字段 | 大小 | 说明 |
|---|---|---|
| head | 1 byte | 方向标识: APP→Device=0xC7, Device→APP=0xB0 |
| type | 1 byte | 命令类型 (CommandType 枚举) |
| subpageTotal | 2 bytes | 总分包数 (0=单帧) |
| curPage | 2 bytes | 当前包序号 (从 total-1 倒数到 0) |
| dataLen | 2 bytes | 数据段长度 |
| data | N bytes | 数据内容 (JSON 或二进制) |
| checksum | 1 byte | 校验: (~字节和 + 1) & 0xFF |
请求-响应模式
// 发送命令 → 注册 CheckedContinuation → 等待设备回包 → 自动解码
let info = try await sdk.getDeviceInfo() // 10s 超时
内部流程:
- 注册 continuation (keyed by commandType)
- 编码 JSON → 创建协议帧 → 写入 write characteristic
- 设备回包 → notify → 解析帧 → 分包重组 → 匹配 continuation → resume
功能清单
扫描
| 方法 | 说明 |
|---|---|
scan() |
开始扫描,结果通过 discoveredDevices 属性观察 (500ms 批量刷新) |
stopScan() |
停止扫描 |
连接
| 方法 | 说明 |
|---|---|
connect(deviceId:) async throws -> DiscoveredDevice |
连接设备 (含服务发现 + 特征发现 + notify 启用) |
disconnect() async throws |
断开连接,清理所有状态 |
设备命令 (请求-响应,10s 超时)
| 方法 | 发送 payload | 响应类型 |
|---|---|---|
getDeviceInfo() |
{"type":13} |
DeviceInfo |
getVersion() |
{"type":7} |
VersionInfo |
bind(userId:) |
{"type":15,"userId":"..."} |
BindingResponse |
unbind(userId:) |
{"type":18,"userId":"..."} |
UnbindResponse |
deleteFile(key:) |
{"type":19,"key":"..."} |
DeleteFileResponse |
prepareTransfer(key:size:) |
{"type":20,"key":"...","size":N} |
PrepareTransferResponse |
文件传输
| 方法 | 说明 |
|---|---|
transferFile(fileUri:commandType:) async throws |
下载文件 + 分帧传输,进度通过 transferProgress 观察 |
可观察状态
| 属性 | 类型 | 说明 |
|---|---|---|
btState |
ConnectionState |
idle/scanning/connecting/connected/disconnecting/disconnected |
discoveredDevices |
[DiscoveredDevice] |
扫描发现的设备列表 |
connectedDevice |
DiscoveredDevice? |
当前连接的设备 |
deviceInfo |
DeviceInfo? |
设备信息 (空间/品牌/电量等) |
version |
String |
固件版本 |
isActivated |
Bool |
是否已绑定 |
transferProgress |
Int |
传输进度 0-100 |
error |
String? |
最近的错误信息 |
CommandType 枚举
| Case | 值 | 用途 |
|---|---|---|
.otaPackage |
0x02 | OTA 升级包 |
.transferBootAnimation |
0x03 | 开机动画 |
.transferAniVideo |
0x05 | ANI 视频 (默认) |
.transferJpegImage |
0x06 | JPEG 图片 |
.getDeviceVersion |
0x07 | 获取固件版本 |
.getDeviceInfo |
0x0D | 获取设备信息 |
.bindDevice |
0x0F | 绑定设备 |
.unbindDevice |
0x12 | 解绑设备 |
.deleteFile |
0x13 | 删除文件 |
.prepareTransfer |
0x14 | 预传输检查 |
集成方式
Swift Package Manager (推荐)
// Package.swift
dependencies: [
.package(path: "../duooomi-ios-sdk")
]
// 或远程
dependencies: [
.package(url: "https://github.com/xxx/duooomi-ios-sdk.git", from: "1.0.0")
]
使用示例
import DuooomiBleSDK
@main
struct MyApp: App {
@StateObject private var sdk = DuooomiBleSDK()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(sdk)
}
}
}
struct ContentView: View {
@EnvironmentObject var sdk: DuooomiBleSDK
var body: some View {
VStack {
Text("State: \(sdk.btState.rawValue)")
Button("Scan") { sdk.scan() }
ForEach(sdk.discoveredDevices) { device in
Button(device.name ?? device.id) {
Task {
try await sdk.connect(deviceId: device.id)
let info = try await sdk.getDeviceInfo()
print("Device: \(info.brand)")
}
}
}
}
}
}
Info.plist 权限
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app needs Bluetooth to communicate with your device</string>
技术规格
- 平台: iOS 16.0+
- 语言: Swift 5.9+
- 依赖: 零 (仅 CoreBluetooth 系统框架)
- 分发: SPM / XCFramework
- 线程模型: 公开 API 为 @MainActor,BLE 操作在专用串行队列
与旧 Brownfield SDK 对比
| 维度 | Brownfield | 原生 SDK |
|---|---|---|
| 体积 | ~75MB (含 Hermes + RN) | ~几百KB |
| 构建 | 7 个 patch + prebuild + pod | swift build |
| 类型安全 | [String: Any] 字典 |
强类型 struct/enum |
| API | 回调 + 消息传递 | async throws |
| 调试 | JS + Native 两层 | 纯 Xcode 断点 |
| 依赖 | React Native, Hermes, Expo | CoreBluetooth (系统框架) |
Description
Languages
Swift
99.4%
Ruby
0.6%