Функции CoreBluetooth не работают из Singleton

Итак, в настоящее время у меня установлено соединение Bluetooth между iPad и iPhone. Я создал свой тестовый код в ViewController, и все работает нормально. Теперь я переместил его в 2 класса менеджеров: один для CBCentralManager и один для CBPeripheralManager, выше тех, что в классах, которые я сделал для BluetoothManager, который является одноэлементным классом и содержит некоторую информацию о подключенных в данный момент устройствах.

Однако при этом я сталкиваюсь с проблемой, похоже, что вызов centralManager.connect() на самом деле не работает. Я отладил весь свой код, и после этой строки, кажется, ничего не происходит, и я не могу понять, почему это так или где я на самом деле ошибаюсь.

Класс CentralManager

import Foundation
import CoreBluetooth

class CentralManager: NSObject {
    private var centralManager: CBCentralManager!
    var peripherals: [CBPeripheral] = []

    override init() {
        super.init()

        centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main)
    }
}

// MARK: - CBCentralManager Delegate Methods
extension CentralManager: CBCentralManagerDelegate {

    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .poweredOn:
            centralManager.scanForPeripherals(withServices: [BLEConstants.serviceUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
        default:
            break
        }
    }

    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        if !peripherals.contains(peripheral) {
            peripheral.delegate = self
            peripherals.append(peripheral)
            centralManager.connect(peripheral, options: nil)
        }
    }

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        peripheral.discoverServices([BLEConstants.serviceUUID])
    }

    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        guard let peripheralIndex = peripherals.index(of: peripheral), BluetoothManager.shared.deviceCharacteristic[peripheral] != nil else { return }

        peripherals.remove(at: peripheralIndex)
        BluetoothManager.shared.deviceCharacteristic.removeValue(forKey: peripheral)
    }

}

// MARK: - CBPeripheral Delegate Methods
extension CentralManager: CBPeripheralDelegate {

    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        for service in peripheral.services! {
            if service.uuid == BLEConstants.serviceUUID {
                peripheral.discoverCharacteristics(nil, for: service)
            }
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        for characteristic in service.characteristics! {
            let characteristic = characteristic as CBCharacteristic

            if BluetoothManager.shared.deviceCharacteristic[peripheral] == nil {
                BluetoothManager.shared.deviceCharacteristic[peripheral] = characteristic
            }
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {

    }

}

Класс PeripheralManager

class PeripheralManager: NSObject {
    private var peripheralManager: CBPeripheralManager!

    override init() {
        super.init()

        peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
    }

}

// MARK: - Manage Methods
extension PeripheralManager {

    func updateAdvertising() {
        guard !peripheralManager.isAdvertising else { peripheralManager.stopAdvertising(); return }

        let advertisingData: [String: Any] = [CBAdvertisementDataServiceUUIDsKey: BLEConstants.serviceUUID,
                               CBAdvertisementDataLocalNameKey: BLEConstants.bleAdvertisementKey]
        peripheralManager.startAdvertising(advertisingData)
    }

    func initializeService() {
        let service = CBMutableService(type: BLEConstants.serviceUUID, primary: true)

        let characteristic = CBMutableCharacteristic(type: BLEConstants.charUUID, properties: BLEConstants.charProperties, value: nil, permissions: BLEConstants.charPermissions)
        service.characteristics = [characteristic]

        peripheralManager.add(service)
    }

}

// MARK: - CBPeripheralManager Delegate Methods
extension PeripheralManager: CBPeripheralManagerDelegate {

    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        if peripheral.state == .poweredOn {
            initializeService()
            updateAdvertising()
        }
    }

    func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
        for request in requests {
            if let value = request.value {
                let messageText = String(data: value, encoding: String.Encoding.utf8)
                print(messageText ?? "")
            }
            self.peripheralManager.respond(to: request, withResult: .success)
        }
    }

}

Класс BluetoothManager

class BluetoothManager {
    static let shared = BluetoothManager()
    private var centralManager: CentralManager!
    private var peripheralManager: PeripheralManager!

    var deviceCharacteristic: [CBPeripheral: CBCharacteristic] = [:]
    var connectedPeripherals: [CBPeripheral] { return centralManager.peripherals }

    func setup() {
        centralManager = CentralManager()
        peripheralManager = PeripheralManager()
    }

}

а потом в моем ViewController didLoad звоню BluetoothManager.shared.setup()

Кто-нибудь знает, почему устройства, похоже, не подключаются друг к другу или, может быть, функции делегирования после этого просто не вызываются?

Это может быть просто догадка, но объекты создаются с помощью метода init в Swift, а не с помощью setup. Вы уверены, что ваш BluetoothManager инициализирован правильно?

fishinear 09.08.2018 18:49

@fishinear true, но я вызываю функцию настройки в ViewController, когда вызывается didLoad, поэтому я в основном создаю для нее свою собственную функцию инициализации, поскольку синглтон создает сам себя, и я не могу контролировать, когда это произойдет. Я выполнил настройку, потому что в обычном запуске ни одна из функций делегата не вызывалась

NoSixties 10.08.2018 10:35
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
1 033
2

Ответы 2

Процесс начинается, когда статическая переменная shared инициализируется с помощью BluetoothManager(). Я не уверен, когда это происходит в Swift, это либо в самом начале программы, либо когда вы впервые используете BluetoothManager.setup. Инициализация переменной вызывает метод init() в BluetoothManager. Это создаст экземпляр CentralManager, и будет вызван его метод init(). Это создаст экземпляр CBCentralManager, который запустит процесс Bluetooth.

Затем вы вызываете setup(), который создает экземпляр нового CentralManager с собственным CBCentralManager. Я могу представить, что с двумя CBCentralManager что-то пошло не так.

Чтобы решить эту проблему, не используйте setup(), а вместо этого инициализируйте переменные в init().

Чтобы отладить такую ​​ситуацию, установите точки останова во всех методах init(). Создайте деструкторы и поместите в них точки останова. Технически вам в любом случае нужны деструкторы, потому что вам нужно удалить себя как делегата из объекта CBCentralManager.


Также обратите внимание, что вы вызываете scanForPeripherals только из centralManagerDidUpdateState. CBCentralManager может уже быть в состоянии poweredOn при запуске, это может произойти, когда другое приложение использует Bluetooth в то же время - или когда ваш первый объект CBCentralManager уже запустил его. В этом случае centralManagerDidUpdateState никогда не будет вызван.

Я просто создал настройку, чтобы контролировать, когда настраиваются менеджеры. Однако я обнаружил проблему при настройке startAdvertising. Я передал ServiceUUID как объект, а не как массив UUID's, поэтому он работал не так, как ожидалось.

NoSixties 14.08.2018 10:34

Вы уверены, что ваш синглтон правильно инициализирован?

Попробуй это:

import Foundation

private let singleton = Singleton()

class Singleton {

  static let sharedInstance : Singleton = {
    return singleton
  }()

  let cnetralManager = = CBCentralManager(delegate: self, queue: DispatchQueue.main)
}

Другие вопросы по теме