Search code examples
iosswiftbluetooth-lowenergycore-bluetooth

BLE peripheral gets disconnected from iPhone app after a couple of hours


I currently have an app that connects to a BLE device and is able to exchange packet data, send messages to other nearby BLE devices, notify when connects/disconnects happen. The problem is when the phone gets locked and the app gets suspended/put to sleep by the system. When it is a short duration, the app gets woken up and notifies of new messages. But when a few hours have passed the device gets disconnected completely. The whole point of the app is to receive these messages regardless if the phone has been locked for like 2-4+ hours (edge case). I don't know the exact time it gets disconnected.... I am only able to conduct a test of a couple hours, or after I wake up from a night's sleep and I can't simulate this on XCode.

This app is using open source stuff, and a similar but different app is able to keep a BLE connection for way longer. This other app uses location updates permissions and mine does not. Some users have said that their background connection is reliable even if they have location set to while using app only. I ran a test and this app stays connected for longer while mine does not, even if no gps/location/or message notifications updates happen.

Can I achieve the same connection reliability without using location updates permission? What if I implement 'background refresh' of some sort or 'acts as bluetooth LE accessory'? If my app receives at least 1 notification every hour will that prevent this? I read up on Apple's "Adding Support for State Preservation and Restoration" docs, it's not well documented but do I do that? What option would you suggest?


Solution

  • Core Bluetooth provides very good support for long-term background operations. It handles both the case where the app is stopped by iOS while suspended (due to memory or other resource pressure) and where the device itself disconnects for some reason (out of range, power loss etc).

    To enable this to work you need to ensure you have opted in to Core Bluetooth state restoration. This is described in this Apple document - Even though it is an old document, it is still valid.

    The general strategy is:

    • Enable the Core Bluetooth background entitlement in your Xcode project
    • When you create your CBCentralManager, give it an identifier via CBCentralManagerOptionRestoreIdentifierKey
    • When you get a call to your central manager's didDisconnect delegate method, immediately call connect on the peripheral.
    • If the peripheral becomes connectable while your app is still running, you will get a call to the didConnect delegate method and you process as usual
    • If your app has been terminated and the peripheral becomes connectable, iOS will relaunch your app in the background. Once you have recreated your CBCentralManager with the same restoration identifier, you will get a call to the didConnect delegate method and you process as usual