You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 15:54:04