demo app + SDK updates:\n- Add direct ANI transfer (prepare -> transfer)\n- Add firmware update flow (fetch latest w/ x-api-key, OTA transfer)\n- Extract x-api-key to NetworkConstants\n- Rename example app to demo; move folder and entry to DemoApp\n- Switch from SPM package dependency to local framework target\n- Enable automatic Info.plist generation for framework target\n- Add placeholder test to satisfy SwiftPM test target\n- Regenerate XcodeGen project (demo.xcodeproj)

This commit is contained in:
km2023
2026-04-09 18:41:56 +08:00
parent c33e7d3fa1
commit 7d9ff3081d
30 changed files with 2594 additions and 516 deletions

261
README.md
View File

@@ -1,52 +1,223 @@
# DuooomiTest
# Duooomi BLE SDK (Native iOS)
DuooomiBleSDK 的 iOS 测试应用,用于验证蓝牙设备的扫描、连接、绑定、文件传输等功能
纯原生 Swift 蓝牙 SDK使用 CoreBluetooth 直接操作,零第三方依赖
## 环境要求
- Xcode 26+
- iOS 16.0+
- Swift 6.0
- [XcodeGen](https://github.com/yonaskolb/XcodeGen)
## 项目依赖
- `DuooomiBleSDK.xcframework` — 蓝牙通信 SDK
- `hermesvm.xcframework` — JS 运行时
依赖文件位于 `../duooomi-ble-sdk/artifacts/`,请确保该路径下存在对应的 xcframework。
## 快速开始
```bash
# 1. 安装 XcodeGen如果还没有
brew install xcodegen
# 2. 生成 Xcode 项目
xcodegen generate
# 3. 打开项目
open DuooomiTest.xcodeproj
```
在 Xcode 中设置你的 `DEVELOPMENT_TEAM`,然后运行到真机即可(蓝牙功能需要真机调试)。
## 项目结构
## 架构设计
```
├── project.yml # XcodeGen 配置
├── DuooomiTest/
── Info.plist
│ └── Sources/
├── DuooomiTestApp.swift # App 入口
├── ContentView.swift # 测试界面
└── SDKManager.swift # SDK 封装层
调用方 (宿主 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 |
### 请求-响应模式
```swift
// CheckedContinuation
let info = try await sdk.getDeviceInfo() // 10s
```
内部流程:
1. 注册 continuation (keyed by commandType)
2. 编码 JSON → 创建协议帧 → 写入 write characteristic
3. 设备回包 → 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 (推荐)
```swift
// Package.swift
dependencies: [
.package(path: "../duooomi-ios-sdk")
]
//
dependencies: [
.package(url: "https://github.com/xxx/duooomi-ios-sdk.git", from: "1.0.0")
]
```
### 使用示例
```swift
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 权限
```xml
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app needs Bluetooth to communicate with your device</string>
```
---
## 技术规格
- **平台**: iOS 16.0+
- **语言**: Swift 5.9+
- **依赖**: 零 (仅 CoreBluetooth 系统框架)
- **分发**: SPM / XCFramework
- **线程模型**: 公开 API 为 @MainActorBLE 操作在专用串行队列
## 与旧 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 (系统框架) |