iOS 11核心蓝牙恢复无法正常工作

时间:2017-09-28 16:38:08

标签: ios bluetooth core-bluetooth

我有一个iOS应用程序,它使用Core Bluetooth连接到V.BTTN智能按钮。在iOS 10中,一切都运行得很好,但是自iOS 11问世以来,恢复过程似乎已经破裂。

当我使用之前配对的按钮启动我的应用程序时,操作系统实际上会调用centralManager:willRestoreState,并且随附的字典包含指向状态为已连接的CBPeripheral对象的指针。就像它在iOS 10中所做的那样。但是,我遇到的问题是当我在外围设备上调用discoverServices时,我从未在peripheral:didDiscoverServices方法中返回任何服务。实际上,该方法或任何其他方法都被调用。

我一直在搜索所有互联网,并且发现人们在按钮配对连接时遇到类似问题,但这些问题通常是外围对象的不正确生命周期管理问题。我相信我的一切设置正确而且不知所措。有谁知道这里会发生什么?

import Foundation
import CoreBluetooth

class SmartButtonManager: NSObject {

    //MARK: - Singleton

    static let sharedManager = SmartButtonManager()

    //MARK: - Properties

    var discoveredDevices: [CBPeripheral] {
        get {
            return perfs
        }
    }

    fileprivate(set) var isBluetoothOn = false

    //MARK: - Private Constants & Variables

    fileprivate var connectedButtonUUID: String?

    fileprivate let queue = DispatchQueue(label: "V.BTTN", qos: .background, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)

    fileprivate var manager: CBCentralManager?

    fileprivate var perfs = [CBPeripheral]()
    fileprivate var connectedPerf: CBPeripheral?

    fileprivate var isButtonReady = false

    //MARK: - Initialization

    override init() {
        super.init()
    }

    //MARK: - Configure

    func configure(withLaunchOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) {

        // create new central manager
        manager = CBCentralManager(delegate: self, queue: queue, options: [CBCentralManagerOptionRestoreIdentifierKey: managerIdentifier])
    }
}

//MARK: - V.BTTN

extension SmartButtonManager: CBCentralManagerDelegate, CBPeripheralDelegate {

    func scanForVbttn() {

        perfs.removeAll()
        manager?.scanForPeripherals(withServices: services, options: nil)
    }

    func centralManagerDidUpdateState(_ central: CBCentralManager) {

        switch central.state {

        case .poweredOff:
            print("[centralManagerDidUpdateState] CB BLE hardware is powered off")
            perfs.removeAll()
            isBluetoothOn = false

        case .poweredOn:
            print("[centralManagerDidUpdateState] CB BLE hardware is powered on. Start scanning for peripherals")
            isBluetoothOn = true

        case .unauthorized:
            print("[centralManagerDidUpdateState] CB BLE hardware is not authorized")

        case .unsupported:
            print("[centralManagerDidUpdateState] CB BLE hardware is not supported")
            isBluetoothOn = false

        default:
            print("[centralManagerDidUpdateState] CB BLE hardware state is unknown")
        }
    }

    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {

        print("[centralManager:didDiscover peripheral:advertisementData] CB BLE did discover peripheral with advertisement data: \(advertisementData)")

        guard let perfName = advertisementData[CBAdvertisementDataLocalNameKey] as? String else {
            print("[centralManager:didDiscover peripheral:advertisementData] peripheral name is unknown")
            return
        }

        if perfName.contains("V.ALRT") {

            peripheral.delegate = self
            perfs.append(peripheral)

            if connectedButtonUUID == perfName {
                connect(peripheral: peripheral)
            }
        }
    }

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {

        print("[central:didConnect peripheral:] CB BLE hardware did connect")
        handleDidConnect(toPeripheral: peripheral)
    }

    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {

        print("[central:didFailToConnect error:] CB BLE peripheral did failed to connect with error: \(String(describing: error))")
        connectedPerf = nil
        isButtonReady = false
        connectedButtonUUID = nil
    }

    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {

        print("[central:didDisconnectPeripheral peripheral:] CB BLE peripheral did disconnect")
        connectedPerf = nil
        isButtonReady = false
        connectedButtonUUID = nil
    }

    func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {

        print("[central:willRestoreState dict:] CB BLE hardware will restore state")
        print("\(dict)")

        guard let ps = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral] else {
            print("[central:willRestoreState dict:] No perfs to restore")
            return
        }

        print("[central:willRestoreState dict:] Will restore perfs")

        perfs = ps

        print("[central:willRestoreState dict:] Attempt to reconnect to V.BTTN")
        print("[central:willRestoreState dict:] perfs \(perfs)")

        for p in perfs {
            if p.name == connectedButtonUUID {

                print("[central:willRestoreState dict:] Connect to perf \(p)")
                handleDidConnect(toPeripheral: p)
                break
            }
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {

        guard let services = peripheral.services else {
            print("[peripheral:didDiscoverServices error:] CB BLE peripheral unable to discover services")
            return
        }

        print("[peripheral:didDiscoverServices error:] BLE peripheral did discover services")

        for s in services {
            print("[peripheral:didDiscoverServices error:] CB BLE Service \(s.description)")
            peripheral.discoverCharacteristics(nil, for: s)
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {

        print("[peripheral:didDiscoverCharacteristicsFor service:] CB BLE did discover characteristics for service \(service.uuid.description)")

        if compareCBUUID(uuid1: service.uuid, uuid2: CBUUID(string: BLE_VSN_GATT_SERVICE_UUID)) {

            guard let characteristics = service.characteristics else {
                return
            }

            for aChar in characteristics {

                // write the verification key
                if aChar.uuid.isEqual(CBUUID(string: BLE_VERIFICATION_SERVICE_UUID)) {

                    self.writeVerificationKey(forPeripheral: peripheral, characteristic: aChar)
                    self.enable(peripheral: peripheral)

                    break
                }
            }
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {

        //NOTE: This code has been omitted as there is little need to see this
    }

    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {

        print("[peripheral:didWriteValueFor characteristic:] characteristic: \(characteristic.uuid)")

        if let e = error {
            print("[peripheral:didWriteValueFor characteristic:] error: \(e)")
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {

        if !characteristic.isNotifying {
            print("[peripheral:didUpdateNotificationStateFor:][Characteristic is not notifiying so cancel the connection]")
            manager?.cancelPeripheralConnection(peripheral)
        }
    }

    //MARK: - Helpers

    fileprivate func writeVerificationKey(forPeripheral peripheral: CBPeripheral, characteristic: CBCharacteristic) {

        print("[peripheral:didDiscoverCharacteristicsFor service:] Write verification key")
        let data = NSData(bytes: [0x80,0xBE,0xF5,0xAC,0xFF] as [UInt8], length: 5)
        peripheral.writeValue(data as Data, for: characteristic, type: CBCharacteristicWriteType.withResponse)
    }

    fileprivate func handleDidConnect(toPeripheral peripheral: CBPeripheral) {

        if let n = peripheral.name {
            connectedButtonUUID = n
        }

        connectedPerf = peripheral

        peripheral.delegate = self
        peripheral.discoverServices(nil)
        isButtonReady = true
    }

    fileprivate func enable(peripheral: CBPeripheral) {

        guard let services = peripheral.services else {
            return
        }

        for service: CBService in services {

            if compareCBUUID(uuid1: service.uuid, uuid2: CBUUID(string: BLE_VSN_GATT_SERVICE_UUID)) {

                guard let characteristics = service.characteristics else {
                    return
                }

                for aChar in characteristics {

                    if aChar.uuid.isEqual(CBUUID(string: BLE_KEYPRESS_DETECTION_UUID)) {

                        // enable button
                        print("[peripheral:didDiscoverCharacteristicsFor service:] Enable short press")

                        let data = NSData(bytes: [0x02] as [UInt8], length: 1)
                        peripheral.writeValue(data as Data, for: aChar, type: CBCharacteristicWriteType.withResponse);

                    } else if aChar.uuid.isEqual(CBUUID(string: BLE_SILENT_NORMAL_MODE)) {

                        // enable button
                        print("[peripheral:didDiscoverCharacteristicsFor service:] Enable normal mode")

                        let data = NSData(bytes: [0x00] as [UInt8], length: 1)
                        peripheral.writeValue(data as Data, for: aChar, type: CBCharacteristicWriteType.withResponse);

                    } else if aChar.uuid.isEqual(CBUUID(string: BLE_FALL_KEYPRESS_DETECTION_UUID)) {

                        // enable fall detection
                        print("[peripheral:didDiscoverCharacteristicsFor service:] Enable fall detection")
                        peripheral.setNotifyValue(true, for: aChar)
                    }
                }
            }
        }

        checkBatteryLevel()
    }

    func disconnectVbttn() {

        guard let peripheral = connectedPerf else {
            return
        }

        setVbttnToStealthMode()
        manager?.cancelPeripheralConnection(peripheral)
        isButtonReady = false
    }

    fileprivate func compareCBUUID(uuid1: CBUUID, uuid2: CBUUID) -> Bool {

        if (uuid1.data as NSData).isEqual(to: uuid2.data) {
            return true
        }

        return false
    }

    func stopScanning() {
        manager?.stopScan()
    }
}

0 个答案:

没有答案