iOS - периферийные устройства BLE не сканируются в фоновом режиме - PullRequest
0 голосов
/ 19 сентября 2018

Я включил «Использование аксессуаров Bluetooth LE» и «Действует как аксессуар Bluetooth LE» в настройках «Фоновые режимы», и они добавлены в Info.plist.а также я позволил своему приложению получить доступ к «обмену Bluetooth».

И мое приложение хорошо сканирует периферийные устройства BLE в режиме переднего плана, но когда оно переходит в фоновый режим, оно больше ничего не сканирует.Если он снова входит в режим переднего плана, он снова хорошо сканирует.

В частности, centralManagerDidUpdateState(_ central: CBCentralManager) всегда хорошо работает в любых режимах.но didDiscover не работает в фоновом режиме.

Я хочу, чтобы BLE-сканирование хорошо работало в любых режимах, и я думаю, что сделал все, что мог.

Соответствующий код

BLEManager.swift

import UIKit

import CoreBluetooth


final class BLEManager: NSObject, CBCentralManagerDelegate, CBPeripheralManagerDelegate {
    static let shared: BLEManager = .init()

    var centralManager: CBCentralManager?
    var peripheralManager: CBPeripheralManager?

    var scannedPeripherals: [CBPeripheral] = .init()
    var confirmedPeripheralStates: [String: CBPeripheralState] = .init()

    override init() {
        super.init()

        self.centralManager = .init(delegate: self, queue: nil)
        self.peripheralManager = .init(delegate: self, queue: nil)
    }

    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .poweredOn:
            self.startScan()
        default:
            central.stopScan()
        }
    }

    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        print("[BLE] state: \(peripheral.state.rawValue)")
    }

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

        central.connect(peripheral)

        self.confirm(peripheral)
    }

    func startScan() {
        guard let central = self.centralManager else {
            return
        }

        let isBackgroundMode: Bool = (UIApplication.shared.applicationState == .background)
        let cbuuids: [CBUUID] = self.scannedPeripherals.map { CBUUID(nsuuid: $0.identifier) }

        central.stopScan()
        central.scanForPeripherals(withServices: isBackgroundMode ? cbuuids : nil)
    }

    func confirm(_ peripheral: CBPeripheral) {
        let uuid: String = peripheral.identifier.uuidString

        // Prevent duplicate logging.
        if peripheral.state != self.confirmedPeripheralStates[uuid] {
            self.log(peripheral)
        }

        self.confirmedPeripheralStates[uuid] = peripheral.state
    }

    func log(_ peripheral: CBPeripheral, completion: (() -> Void)? = nil) {
        guard let name = peripheral.name,
              let state = self.getPeripheralStateAsString(peripheral.state) else {
                return
        }

        print("[BLE] \(name) was \(state) on \(self.getApplicationStateAsString())")
    }
}

extension BLEManager {
    func getApplicationStateAsString() -> String {
        switch UIApplication.shared.applicationState {
        case .active:
            return "active"
        case .inactive:
            return "inactive"
        case .background:
            return "background"
        }
    }

    func getPeripheralStateAsString(_ state: CBPeripheralState) -> String? {
        switch state {
        case .disconnected:
            return "disconnected"
        case .connecting:
            return "connecting"
        case .connected:
            return "connected"
        default:
            return nil
        }
    }
}


extension Array where Element: Equatable {
    mutating func appendAsUnique(_ element: Element) {
        if let index = self.index(where: { $0 == element }) {
            self.remove(at: index)
        }

        self.append(element)
    }
}

AppDelegate.swift

func applicationDidEnterBackground(_ application: UIApplication) {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

    BLEManager.shared.startScan()
}

Я что-то пропустил?

...