首页 > 解决方案 > Reconnect Bluetooth 4.0 connection when user force quits the app

问题描述

I'm struggling with the problem which is, I can not reestablish the connection with my BLE Device when user force quits the app. My app works like this:

  1. First launch the app user should connect with the BLE Device - that works
  2. When app is in background the connections is still working
  3. If I turn off the BLE Device and turn on again the connection is reestablished - works
  4. When user goes out and turn back home the connection is reestablished and user gets notification that he is close to the Device - works

All of this is solved by turning Bluetooth 4.0 LE into iBeacon. When user is close enough an iBeacon starts sending signals and iOS app is triggered and user gets push notification an connection is reestablished.

The problem appears when user force quits the app. iBeacon still works because when user is connected to BLE the BLE device works as.. Bluetooth device, but when the connection is lost BLE works as iBeacon and advertise the iOS app. When user force quits the app the user gets local push notification that app is close to iBeacon so it means the app is relaunch for the couple of seconds (10s I think) but I can not with this reestablish connection.

This is the code for tigger the notification and as I hoped for BLE reconnection after fore quit.

   func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
    // Check if the user's INSIDE the region, not outside...

    guard state == CLRegionState.inside else { print("Error"); return }

    if state == CLRegionState.inside{

        let content = UNMutableNotificationContent()
        content.title = "Device found!"
        content.body = "You're close to your BLE Device, reestablish connection!"

        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
        let request = UNNotificationRequest(identifier: "1389402823904", content: content, trigger: trigger)

        UNUserNotificationCenter.current().add(request) { (err:Error?) in
        }

        centralManager.scanForPeripherals(withServices: nil, options: nil)
        let uuid = UUID(uuidString: "74278BDA-B644-4520-8F0C-720EAF059935")!
        guard let mainPeripheral = mainPeripheral else {return}
        centralManager.connect(mainPeripheral, options: nil)

        centralManager.retrievePeripherals(withIdentifiers: [uuid])
        DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
            self.centralManager.stopScan()
        }

    }

}

}

For the simplicity the uuid is hardcoded. As I understand the didDeterminateState is triggered when iBeacon sends signal and I am checking the CLRegionState if the region is inside which means close to the iBeacon the method for local notifications gets triggers, then I need to rescan for BLE devices so I am calling scanForPeripherals but not the connect method. ScanForPeripherals should call the didDiscover method:

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

    if peripheral.name == "DOPEY"{

        peripheral.delegate = self
        peripheral.discoverServices([HM_10_ServiceCharacteristic])
        mainPeripheral = peripheral
        centralManager.connect(peripheral, options: nil)

        centralManager.stopScan()

    }

}

When it founds DOPEY peripheral name the device should connect. For tests I am using HM-10 Bluetooth 4.0 LE. When user force quits the app it sends local push notification but does not reestablish the connection.

I am looking forward for any kind of help. Appreciate!

标签: iosswiftbluetooth-lowenergyibeacon

解决方案


无需重新扫描设备;identier您应该在第一次发现它时保存它的属性。然后,您可以使用retrievePeripherals(withIdentifiers:)来尝试获取对CBPeripheral. 如果成功,只需调用connect.

您的代码显示尝试执行此类操作,但您已使用(我认为是)您的服务 UUID,并且您没有对结果做任何事情(由于该声明retrievePeripherals(withIdentifiers:),您也无法访问此代码) guard.

你应该使用类似的东西:

func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
    // Check if the user's INSIDE the region, not outside...

    guard state == CLRegionState.inside else { print("Error"); return }


    let content = UNMutableNotificationContent()
    content.title = "Device found!"
    content.body = "You're close to your BLE Device, reestablish connection!"

    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
    let request = UNNotificationRequest(identifier: "1389402823904", content: content, trigger: trigger)

    UNUserNotificationCenter.current().add(request) { (err:Error?) in
    }

    if self.mainPeripheral == nil {
        let defaults = UserDefaults.standard
        if let uuidString = defaults.string(forKey:"peripheralID"),
           let uuid = UUID(uuidString: uuidString),
           let peripheral = centralManager.retrievePeripherals(withIdentifiers: [uuid]).first {
            self.mainPeripheral = peripheral
        }
    }


    if let mainPeripheral = mainPeripheral 
        centralManager.connect(mainPeripheral, options: nil)
    } else {
        //TODO: Scan for peripheral.  Note that you can't use `services:nil` in the background
    }

}


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

    if peripheral.name == "DOPEY" {
        let defaults=UserDefaults.standard
        defaults.set(peripheral.identifier.uuidString,forKey:"peripheralID")
        peripheral.delegate = self    
        mainPeripheral = peripheral
        centralManager.connect(peripheral, options: nil)
        centralManager.stopScan()
    }    
}

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

推荐阅读