当用户强行退出应用时,重新连接蓝牙4.0连接

时间:2018-12-12 20:30:31

标签: ios swift bluetooth-lowenergy ibeacon

我正在努力解决以下问题:当用户强制退出应用程序时,无法与BLE设备重新建立连接。 我的应用程序是这样的:

  1. 首先启动,应用程序用户应与BLE设备连接-起作用
  2. 当应用在后台运行时,连接仍然有效
  3. 如果我关闭BLE设备并再次打开,则重新建立连接-有效
  4. 当用户外出并返回家中时,连接会重新建立,并且用户会收到有关他靠近设备的通知-可以正常使用

所有这些都可以通过将Bluetooth 4.0 LE转换为iBeacon来解决。当用户足够接近时,iBeacon将开始发送信号,并触发iOS应用,并且用户会收到推送通知,并重新建立连接。

当用户强制退出应用程序时出现问题。 iBeacon仍然可以工作,因为当用户连接到BLE时,BLE设备可以用作蓝牙设备。但是当连接断开时,BLE可以用作iBeacon并发布iOS应用程序。 当用户强行退出应用程序时,用户会收到本地推送通知,通知该应用程序靠近iBeacon,这意味着该应用程序将重新启动几秒钟(我认为是10秒钟),但是我无法通过此重新建立连接。

这是触发通知的代码,正如我希望在退出之前重新连接BLE一样。

   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()
        }

    }

}

}

为简单起见,uuid是硬编码的。 据我了解,当iBeacon发送信号时会触发didDeterminateState,并且我正在检查CLRegionState是否在该区域内,这意味着靠近iBeacon的本地通知方法会触发,因此我需要重新扫描BLE设备,因此我正在调用scanForPeripherals,而不是connect方法。 ScanForPeripherals应该调用didDiscover方法:

    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()

    }

}

找到DOPEY外设名称时,设备应连接。 对于测试,我使用的是HM-10蓝牙4.0 LE。 当用户强制退出应用程序时,它将发送本地推送通知,但不会重新建立连接。

我期待任何帮助。 谢谢!

2 个答案:

答案 0 :(得分:3)

无需重新扫描设备;首次发现它时,应保存其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])
}

答案 1 :(得分:1)

如果用户不杀死应用程序,则无需让BLE充当信标。我的意思是,一旦外围设备断开连接,以便在可用时再次重新连接,您只需要在断开连接回调中再次调用connect,如果您的应用程序具有在后台作为中央运行的功能,则可以完美运行。这种行为似乎比iBeacon区域监视更可靠。
如果用户终止了您的应用程序,问题就开始了。您必须知道,这种行为是Apple设计的行为,我的意思是,如果用户杀死了您的应用程序,则它可能不想被某些设备的连接所困扰。
在这些情况下,唯一的解决方案似乎是使设备充当iBeacon。在这种情况下,如果检测到外围设备,则您的应用会在后台启动一小段时间。事实是,仅当您设置设备的特定服务CBUUID时,后台扫描才有效。

  

已指定蓝牙中央背景模式的应用为   允许在后台扫描。也就是说,他们必须   通过在中指定服务来显式扫描一个或多个服务   serviceUUIDs参数。 CBCentralManager扫描选项被忽略   在后台扫描时。

为什么不只使用retrievePeripheralWithIdentifiers?与设备建立连接后,将设置此UUID。
扫描是异步的,那么为什么要在调用扫描方法后尝试连接到外围设备?
您应该选择一种方式