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

SwiftUI中如何在BluetoothViewModel内设置ConnectedPeripheral环境对象?

解决方案:在SwiftUI中复用CBPeripheral实例

1. 创建ConnectedPeripheral包装类(作为EnvironmentObject)

定义一个符合ObservableObject的包装类,用来持有全局的CBPeripheral实例,通过SwiftUI环境对象实现全应用共享:

import CoreBluetooth
import SwiftUI

class ConnectedPeripheral: ObservableObject {
    // @Published标记属性,变化时自动通知视图更新
    @Published var peripheral: CBPeripheral?
    
    // 封装更新方法,让逻辑更清晰
    func update(with newPeripheral: CBPeripheral?) {
        self.peripheral = newPeripheral
    }
}

2. 在应用入口注入环境对象

在App结构体中初始化ConnectedPeripheral和BluetoothViewModel,将两者注入视图环境,确保所有子视图可访问:

@main
struct BluetoothDemoApp: App {
    @StateObject private var connectedPeripheral = ConnectedPeripheral()
    @StateObject private var bluetoothViewModel: BluetoothViewModel
    
    init() {
        let peripheral = ConnectedPeripheral()
        self._connectedPeripheral = StateObject(wrappedValue: peripheral)
        self._bluetoothViewModel = StateObject(wrappedValue: BluetoothViewModel(connectedPeripheral: peripheral))
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(connectedPeripheral)
                .environmentObject(bluetoothViewModel)
        }
    }
}

3. 在BluetoothViewModel中更新环境对象

让BluetoothViewModel持有ConnectedPeripheral的引用,在蓝牙连接/断开回调中直接更新其peripheral属性:

import CoreBluetooth
import SwiftUI

class BluetoothViewModel: NSObject, CBCentralManagerDelegate, ObservableObject {
    private let centralManager: CBCentralManager
    private let connectedPeripheral: ConnectedPeripheral
    
    // 初始化时传入共享的ConnectedPeripheral实例
    init(connectedPeripheral: ConnectedPeripheral) {
        self.connectedPeripheral = connectedPeripheral
        self.centralManager = CBCentralManager(delegate: nil, queue: DispatchQueue.main)
        super.init()
        centralManager.delegate = self
    }
    
    // 蓝牙设备连接成功回调
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        connectedPeripheral.update(with: peripheral)
        // 设置peripheral代理,处理服务/特征发现
        peripheral.delegate = self
        peripheral.discoverServices(nil)
    }
    
    // 蓝牙设备断开回调
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        connectedPeripheral.update(with: nil)
    }
    
    // 扫描蓝牙设备逻辑
    func startScanning() {
        centralManager.scanForPeripherals(withServices: nil, options: nil)
    }
}

// 扩展实现CBPeripheralDelegate,处理服务/特征相关逻辑
extension BluetoothViewModel: CBPeripheralDelegate {
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        guard let services = peripheral.services else { return }
        for service in services {
            peripheral.discoverCharacteristics(nil, for: service)
        }
    }
}

4. 在其他视图中使用共享的CBPeripheral

在任意子视图中,通过@EnvironmentObject获取ConnectedPeripheral,直接使用其peripheral属性:

struct DeviceControlView: View {
    @EnvironmentObject private var connectedPeripheral: ConnectedPeripheral
    
    var body: some View {
        VStack(spacing: 20) {
            if let peripheral = connectedPeripheral.peripheral {
                Text("当前连接设备: \(peripheral.name ?? "未知设备")")
                Button("发送指令") {
                    sendCommand(to: peripheral)
                }
            } else {
                Text("未连接蓝牙设备")
            }
        }
        .padding()
    }
    
    private func sendCommand(to peripheral: CBPeripheral) {
        // 实现向设备发送数据的逻辑,例如写入特征值
        guard let service = peripheral.services?.first(where: { $0.uuid == YourServiceUUID }),
              let characteristic = service.characteristics?.first(where: { $0.uuid == YourCharacteristicUUID }) else {
            return
        }
        let data = "Hello Bluetooth".data(using: .utf8)
        peripheral.writeValue(data!, for: characteristic, type: .withResponse)
    }
}

5. 监听@Published属性变化(可选)

如果选择在BluetoothViewModel中添加@Published var peripheral: CBPeripheral?,可通过以下方式监听变化:

方式1:在视图中用.onReceive监听

struct ContentView: View {
    @EnvironmentObject private var viewModel: BluetoothViewModel
    
    var body: some View {
        Text("蓝牙连接状态")
            .onReceive(viewModel.$peripheral) { newPeripheral in
                if let peripheral = newPeripheral {
                    print("设备已连接: \(peripheral.name ?? "未知")")
                } else {
                    print("设备已断开")
                }
            }
    }
}

方式2:在ViewModel内部用Combine监听

class BluetoothViewModel: NSObject, CBCentralManagerDelegate, ObservableObject {
    @Published var peripheral: CBPeripheral?
    private var cancellables = Set<AnyCancellable>()
    
    override init() {
        super.init()
        // 监听peripheral属性变化
        $peripheral
            .sink { [weak self] newPeripheral in
                guard let self = self else { return }
                if let peripheral = newPeripheral {
                    peripheral.delegate = self
                    peripheral.discoverServices(nil)
                }
            }
            .store(in: &cancellables)
    }
    
    // ...其他蓝牙逻辑
}

内容的提问来源于stack exchange,提问作者lcj

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 06:50:32