Swift中如何将BLEManager代理接收的数据存储到外部类中?
实现结论
完全可以实现,这是Swift中非常典型的关注点分离实践,核心思路是让BLEManager仅作为蓝牙数据的发布者,额外创建独立的数据存储层统一管理所有用户相关数据,两者通过Combine框架的发布订阅能力关联,逻辑和你熟悉的JS事件监听机制非常类似。
具体实现方案
- 保留BLEManager的蓝牙扫描、连接、数据解析逻辑,仅通过
@Published属性对外发布接收到的体重数据,不持有任何业务相关的存储数据 - 新增独立的健康数据存储类,统一管理用户生物特征数据、蓝牙采集的体重数据,以及两者关联的计算逻辑
- 通过Combine框架的发布订阅能力,在需要的场景订阅BLEManager的数据更新事件,同步更新到统一数据存储层
代码示例
1. BLEManager(仅保留蓝牙逻辑)
这个类不需要做大量改造,明确它的定位是蓝牙数据的生产者即可:
import CoreBluetooth import Combine class BLEManager: ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate { private var myCentral: CBCentralManager! // 仅作为数据发布出口,不做业务存储 @Published var data = 0.0 init() { myCentral = CBCentralManager(delegate: self, queue: nil) } // ...扫描、连接等蓝牙相关逻辑保持不变 func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { // 解析原始数据得到体重 let parsedWeight = 10.0 // 替换为实际解析逻辑 data = parsedWeight } }
2. 统一健康数据存储层
把你已有的生物特征结构体和体重数据统一放在这里,相关计算逻辑也内聚到该类:
// 你已有的生物特征结构体 struct UserBiometrics { var age: Int var height: Double // 单位:米 var gender: String } class UserHealthDataStore: ObservableObject { // 统一存储所有用户相关数据 @Published var userBiometrics: UserBiometrics @Published var latestWeight: Double = 0.0 init(biometrics: UserBiometrics) { self.userBiometrics = biometrics } // 体重和生物特征关联的计算逻辑示例:计算BMI func calculateBMI() -> Double { guard latestWeight > 0, userBiometrics.height > 0 else { return 0 } return latestWeight / pow(userBiometrics.height, 2) } }
3. 数据订阅关联
你选择在View的onAppear中订阅是完全可行的,需要注意要持有订阅对象避免订阅被提前释放,写法参考如下:
import SwiftUI import Combine struct WeightMeasureView: View { // 注入共享的BLEManager和数据存储实例,不要重复创建 @EnvironmentObject var bleManager: BLEManager @EnvironmentObject var healthStore: UserHealthDataStore // 持有订阅引用,类似JS中保存事件监听的引用 @State private var weightSubscription: AnyCancellable? var body: some View { VStack(spacing: 16) { Text("当前体重:\(healthStore.latestWeight, specifier: "%.1f") kg") Text("BMI:\(healthStore.calculateBMI(), specifier: "%.1f")") } .onAppear { // 订阅蓝牙体重更新 weightSubscription = bleManager.$data .receive(on: DispatchQueue.main) // 确保在主线程更新UI .sink { [weak healthStore] newWeight in healthStore?.latestWeight = newWeight } } .onDisappear { // 页面消失时取消订阅,避免内存泄漏 weightSubscription?.cancel() } } }
常见问题说明
你提到的publisher传递问题通常是因为实例不共享导致的,建议在App入口或者上层父View通过environmentObject注入两个类的共享实例,保证整个链路拿到的是同一个发布者和存储实例,就可以解决订阅无效的问题。
内容的提问来源于stack exchange,提问作者NeMaksym
相关产品推荐
相关产品推荐

