swift 同一应用中的多个CBCentralManager未调用其更新状态函数

2ic8powd  于 2022-12-22  发布在  Swift
关注(0)|答案(1)|浏览(180)

There are a few Stack Overflow questions about this however they are either outdated or have mixed answers on whether 2 central managers are supported. I am using IOS 16.2, the problem also occurs on IOS 16.1. Xcode version 14.1.

PROBLEM: When attempting to initialize multiple CBCentralManagers, only the first CBCentralManager calls the centralManagerDidUpdateState() function to update its state, the other managers have their state always at unknown.
DESIRED STATE: All central managers call the centralManagerDidUpdateState() function of their respective delegates. In the code provided, the second central manager should print "SECOND MANAGER UPDATED STATE."
BACKGROUND: The reason I am using multiple central managers is because I am using a package that initializes its own Core Bluetooth central manager (https://github.com/NordicSemiconductor/IOS-nRF-Mesh-Library/blob/main/nRFMeshProvision/Classes/Bearer/GATT/BaseGattProxyBearer.swift#L109). I noticed the code wasn't doing what it was supposed to (and documented to do). The documentation states that the object in the link I provided above can be initialized within the didDiscover function of a user defined central manager's delegate (https://github.com/NordicSemiconductor/IOS-nRF-Mesh-Library/blob/main/Documentation.docc/nRFMeshProvision.md#provisioning). I investigated enough to find the problem lies in having multiple central managers. A workaround I can pursue is using only 1 central manager and passing it to the package, but that would require me to edit the source and perhaps lead to more cumbersome unsupported workarounds with it. The package was documented to be working in September of 2022, so I don't know if Apple changed the way Core Bluetooth CentralManagers behave to only allow 1 working at a time, or if there may be something wrong with my environment.

Here is my code to replicate the issue easily.

CODE:

ModelData, where the central managers are initialized:

import Foundation
import Combine
import CoreBluetooth

final class ModelData: {
let centralManager: CBCentralManager!
let secondCentralManager: CBCentralManager!

    init() {
        let centralManagerDelegate = CentralManagerDelegate()
        centralManager = CBCentralManager(delegate: centralManagerDelegate, queue: nil)
        let secondCMDelegate = SecondCentralManagerDelegate()
        secondCentralManager  = CBCentralManager(delegate: secondCMDelegate, queue: nil)
        print("initialized central managers")
    }

}

Central Manager Delegate:

import Foundation
import CoreBluetooth

class CentralManagerDelegate: NSObject, ObservableObject, CBCentralManagerDelegate  {

    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .unknown:
            print("Bluetooth Device is UNKNOWN")
        case .unsupported:
            print("Bluetooth Device is UNSUPPORTED")
        case .unauthorized:
            print("Bluetooth Device is UNAUTHORIZED")
        case .resetting:
            print("Bluetooth Device is RESETTING")
        case .poweredOff:
            print("Bluetooth Device is POWERED OFF")
        case .poweredOn:
            print("Bluetooth Device is POWERED ON")
        @unknown default:
            print("Unknown State")
        }
    }

}

Second Central Manager Delegate:

import Foundation
import CoreBluetooth

class SecondCentralManagerDelegate: NSObject, ObservableObject, CBCentralManagerDelegate  {
    func centralManagerDidUpdateState(\_ central: CBCentralManager) {
        print("SECOND MANAGER UPDATED STATE")
    }
}

The view calling the modelData:

struct HomeScreen: View {
@StateObject private var modelData = ModelData()
@State private var showDetail = true

    var body: some View {
    
    }

}

Output when running:

initialized central managers
Bluetooth Device is POWERED ON
qgelzfjb

qgelzfjb1#

你正在创建你的委托对象作为局部变量,由于CBCentralManager不保留它的委托,这些委托将在init返回时被释放。你可能很幸运,第一个委托在释放前有机会打印状态。第二个委托释放得太早了。
你可以很容易地确认这一点,通过添加一个deinit方法到你的委托类,打印一些东西-你会看到你的委托被取消初始化。
解决方案是使用属性保留对委托的强引用,就像处理CBCentralManager示例一样

final class ModelData: {
    let centralManager: CBCentralManager!
    let secondCentralManager: CBCentralManager!
    let centralManagerDelegate: CentralManagerDelegate
    let secondCMDelegate: SecondCentralManagerDelegate

    init() {
        centralManagerDelegate = CentralManagerDelegate()
        centralManager = CBCentralManager(delegate: centralManagerDelegate, queue: nil)
        secondCMDelegate = SecondCentralManagerDelegate()
        secondCentralManager  = CBCentralManager(delegate: secondCMDelegate, queue: nil)
    }

}

相关问题