DuooomiBleConfig 新增 frameIntervalMs,BleProtocolService 构造期注入。 顺手清死代码 FrameConstants.frameIntervalNanos、修 sendFrames 0.035 字面量 (统一走 config)。demo 加可调输入框 + UserDefaults 持久化。 越界 warn 不抛错,使用点 min/max 兜底。 设计稿:docs/superpowers/specs/2026-05-20-frame-interval-config-design.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
149 lines
4.9 KiB
Swift
149 lines
4.9 KiB
Swift
import SwiftUI
|
||
import DuooomiBleSDK
|
||
|
||
/// Demo 入口:初始化 SDK 并注入到视图树。
|
||
@main
|
||
struct DemoApp: App {
|
||
@StateObject private var viewModel = DemoViewModel()
|
||
|
||
var body: some Scene {
|
||
WindowGroup {
|
||
NavigationView {
|
||
WrapperTestView(viewModel: viewModel)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// ViewModel 包装 SDK callback API,通过 DuooomiBleSDKDelegate 驱动 UI 更新。
|
||
final class DemoViewModel: NSObject, ObservableObject, DuooomiBleSDKDelegate {
|
||
|
||
// MARK: - SDK Config(demo 内可改,持久化到 UserDefaults)
|
||
|
||
@Published var configBrand: String {
|
||
didSet { defaults.set(configBrand, forKey: Self.keyBrand) }
|
||
}
|
||
@Published var configApiKey: String {
|
||
didSet { defaults.set(configApiKey, forKey: Self.keyApiKey) }
|
||
}
|
||
@Published var configScanPrefix: String {
|
||
didSet { defaults.set(configScanPrefix, forKey: Self.keyScanPrefix) }
|
||
}
|
||
@Published var configFrameIntervalMs: String {
|
||
didSet { defaults.set(configFrameIntervalMs, forKey: Self.keyFrameIntervalMs) }
|
||
}
|
||
|
||
// MARK: - SDK State (driven by delegate)
|
||
|
||
@Published var btState: ConnectionState = .idle
|
||
@Published var connectedDevice: DiscoveredDevice? = nil
|
||
@Published var discoveredDevices: [DiscoveredDevice] = []
|
||
@Published var deviceInfo: DeviceInfo? = nil
|
||
@Published var version: String = ""
|
||
@Published var isActivated: Bool = false
|
||
@Published var transferProgress: Int = 0
|
||
@Published var error: String? = nil
|
||
@Published var upgradeRecords: [UpgradeRecord] = []
|
||
|
||
@Published private(set) var sdk: DuooomiBleSDK
|
||
|
||
// MARK: - Internals
|
||
|
||
private let defaults = UserDefaults.standard
|
||
private static let keyBrand = "demo.config.brand"
|
||
private static let keyApiKey = "demo.config.apiKey"
|
||
private static let keyScanPrefix = "demo.config.scanPrefix"
|
||
private static let keyFrameIntervalMs = "demo.config.frameIntervalMs"
|
||
|
||
/// demo 调试默认值(仅当 UserDefaults 没存过时使用)。秘钥从 DemoSecrets 读,避免泄露到仓库。
|
||
private static let defaultBrand = DemoSecrets.brand
|
||
private static let defaultApiKey = DemoSecrets.apiKey
|
||
private static let defaultScanPrefix = DemoSecrets.scanNamePrefix
|
||
private static let defaultFrameIntervalMs = "35"
|
||
|
||
override init() {
|
||
let d = UserDefaults.standard
|
||
let brand = d.string(forKey: Self.keyBrand) ?? Self.defaultBrand
|
||
let apiKey = d.string(forKey: Self.keyApiKey) ?? Self.defaultApiKey
|
||
let scanPrefix = d.string(forKey: Self.keyScanPrefix) ?? Self.defaultScanPrefix
|
||
let frameIntervalMs = d.string(forKey: Self.keyFrameIntervalMs) ?? Self.defaultFrameIntervalMs
|
||
|
||
self.configBrand = brand
|
||
self.configApiKey = apiKey
|
||
self.configScanPrefix = scanPrefix
|
||
self.configFrameIntervalMs = frameIntervalMs
|
||
|
||
self.sdk = DuooomiBleSDK(config: .init(
|
||
apiKey: apiKey,
|
||
brand: brand,
|
||
scanNamePrefix: scanPrefix,
|
||
frameIntervalMs: Int(frameIntervalMs) ?? 35
|
||
))
|
||
|
||
super.init()
|
||
sdk.delegate = self
|
||
// 初次加载 SDK 持久化的 records(之前历史升级 / 待对账)
|
||
upgradeRecords = sdk.upgradeRecords
|
||
}
|
||
|
||
/// demo 内修改配置后调用:重新创建 SDK 实例并迁移 delegate。
|
||
func reloadSDK() {
|
||
let new = DuooomiBleSDK(config: .init(
|
||
apiKey: configApiKey,
|
||
brand: configBrand,
|
||
scanNamePrefix: configScanPrefix,
|
||
frameIntervalMs: Int(configFrameIntervalMs) ?? 35
|
||
))
|
||
new.delegate = self
|
||
self.sdk = new
|
||
// 重置展示状态
|
||
btState = .idle
|
||
connectedDevice = nil
|
||
discoveredDevices = []
|
||
deviceInfo = nil
|
||
version = ""
|
||
isActivated = false
|
||
transferProgress = 0
|
||
error = nil
|
||
upgradeRecords = new.upgradeRecords
|
||
}
|
||
|
||
// MARK: - DuooomiBleSDKDelegate
|
||
|
||
func sdk(_ sdk: DuooomiBleSDK, didChangeState state: ConnectionState) {
|
||
btState = state
|
||
}
|
||
|
||
func sdk(_ sdk: DuooomiBleSDK, didUpdateDevices devices: [DiscoveredDevice]) {
|
||
discoveredDevices = devices
|
||
}
|
||
|
||
func sdk(_ sdk: DuooomiBleSDK, didConnectDevice device: DiscoveredDevice?) {
|
||
connectedDevice = device
|
||
}
|
||
|
||
func sdk(_ sdk: DuooomiBleSDK, didUpdateDeviceInfo info: DeviceInfo?) {
|
||
deviceInfo = info
|
||
}
|
||
|
||
func sdk(_ sdk: DuooomiBleSDK, didUpdateVersion version: String) {
|
||
self.version = version
|
||
}
|
||
|
||
func sdk(_ sdk: DuooomiBleSDK, didChangeActivation isActivated: Bool) {
|
||
self.isActivated = isActivated
|
||
}
|
||
|
||
func sdk(_ sdk: DuooomiBleSDK, didUpdateProgress progress: Int) {
|
||
transferProgress = progress
|
||
}
|
||
|
||
func sdk(_ sdk: DuooomiBleSDK, didUpdateUpgradeRecords records: [UpgradeRecord]) {
|
||
upgradeRecords = records
|
||
}
|
||
|
||
func sdk(_ sdk: DuooomiBleSDK, didEncounterError error: String?) {
|
||
self.error = error
|
||
}
|
||
}
|