What’s the crack with state preservation and state restoration?
CoreBluetooth works in background, but what happen if your app get killed by the system, or the user turn off and on again the iOS device?
Basically you will lost the connection and it will not be restored.
CoreBluetooth offers an opportunity to avoid that and makes the communication with your Bluetooth devices more reliable and robust, it is called state preservation and restoration. In few words the responsability to keep the connection alive is passed to the system and once your app is restored you can continue to work with the peripheral.
From Apple documentation:
Some apps may need to use the Core Bluetooth framework to perform long-term actions in the background. As an example, imagine you are developing a home security app for an iOS device that communicates with a door lock (equipped with Bluetooth low energy technology). The app and the lock interact to automatically lock the door when the user leaves home and unlock the door when the user returns — all while the app is in the background. When the user leaves home, the iOS device may eventually become out of range of the lock, causing the connection to the lock to be lost. At this point, the app can simply call the connectPeripheral:options: method of the CBCentralManager class, and because connection requests do not time out, the iOS device will reconnect when the user returns home.
Now imagine that the user is away from home for a few days. If the app is terminated by the system while the user is away, the app will not be able to reconnect to the lock when the user returns home, and the user may not be able to unlock the door. For apps like these, it is critical to be able to continue using Core Bluetooth to perform long-term actions, such as monitoring active and pending connections.
…but really, how it works?
Apple documentation is not very clear and doesn’t give us a lot of example as we would like, by reading it I had the feeling that there were a lot of missing use cases and “IF”es, so since I’m writing a Bluetooth library that uses Combine (LittleBluetooth) and started to implement state restoration, I’ve decide to make some tests in different scenarios.
While in the process of testing state restoration/preservation I will use LittleBluetooth library main concepts are valid also when using directly CoreBluetooth.
- Sample application with capability Uses Bluetooth LE accessories enabled
- LittleBluetooth library
- Arduino Nano BLE 33 with a firmware that helps me to understand what’s going on by logging if the device is connected, notifying etc
We will focus just on the central role.
I will not dig into details about how implement state restoration, for that there is the Apple Documentation, but you must be aware that to enable it:
CBCentralManagerinstance must have a unique identifier, passed into the option dictionary in the init method of
CBCentralManagerassociated the key
- When restoration is triggered you will need to intercept the dictionary passed in func application(_ application:didFinishLaunchingWithOptions launchOptions: ) →Bool get the value for the key
UIApplication.LaunchOptionKey.bluetoohCentralsiterate through the identifiers returned and instantiate a new
CBCentralManagerwith that identifier.
- Set the delegate and you will start to receive the restored information (if any) from the callback
CBCentralManagerDelegatein a form of a dictionary.
If you handle only one central you can just instantiate it with same identifier without reading the identifier from the dictionary.
I will trigger crashes using
kill(getpid(), SIGKILL)to better simulate a kill from the system.
State preservation/restoration behaves differently depending on the circumstances that closed the application .
Specifically if a user kill the app, state restoration will never be triggered by the system, but only after you will launch the application manually and you must be prepared for that.
Note that state restoration works only if one of this situation has been interrupted:
- connection with a peripheral ( connected, connecting, or after a connect command)
In other cases no callback will be received by the
CBCentralManager instance, so basically if your app has been disconnected from the peripheral before killing it and you didn’t respond by any means to that event nothing will be restored.
What happens if I’m scanning and my app get killed by the system while scanning?
What happens if I launch a connection while the device is not turned on and my app get killed by the system?
What happens if I’m connected and my app get killed by the system?
What happens if I’m connected and my app get killed by the system and the peripheral move out range or is turned off?
What happens if I’m connected listening to a characteristic and my app get killed by the system?
An important concept can be taken from here. It’ s always up to you to decide what you want to do when restoration kicks in, the system will only provides you the last known state.
There is nothing special under the hood. If a peripheral disconnect while your app is not running, the system will reopen the application, as soon as you instanciate a
CBCentralManger and set its delegate you will start to receive the last callback received from the system, from here you must decide if you want to schedule another connection or leave the peripheral disconnected. In the first case as soon as the peripheral is available a connection will be established.
Same for a peripheral that is notifying, if the system kills your app while is being notified, the system will keep the connection with the subscription active, when the system decide it will open your application again by giving you back the peripheral and as soon as you set the peripheral delegate you will start to receive the values from the subscribed characteristic.
The library I’m building it does more. On one hand you can set a restore handler and an autoconnection handler with the two set your app will always try to keep your peripheral connected.
On the other hand you can avoid using the handlers and attach subscribers to the publishers of connection events and state restoration, in this case, as it does Core Bluetooth, you must decide what to do with those informations.
If you want to check my library is opensource and published on Github at this link.