In order to provide support for bluetooth background mode, you need to add UIBackgroundModes key in info.plist file with array value containing one of the following strings:
- bluetooth-central – if your application connects to BLE devices,
- bluetooth-peripheral – if your application accepts connections from other BLE devices and shares data.
To understand how bluetooth communication works and how to use CoreBluetooth library read my previous posts:
- How to communicate with Bluetooth Low Energy devices on iOS
- Swift – Bluetooth Low Energy communication using Flow Controllers
Bluetooth in background mode works almost in the same way as in foreground, however there are some tricky parts. Here you can find some tips.
1. Await keyword (C# only)
Do not use await keyword and async methods while working with BLE in background. It behaves nondeterministic, sometimes it works, sometimes it doesn’t. In order to provide reliable mechanism use methods from CoreBluetooth and attach proper event handlers.
CoreBluetooth event handlers don’t return Task, so they are not awaitable. Therfore when you call method with await keyword inside event handler, it will return once the line with await is reached. System will think that you finished your job and will end background task assigned to your application.
Use this.manager.ConnectPeripheral(peripheral). It doesn’t timeout, so you can invoke it and forget, after successful connection ConnectedPeripheral event will be triggered even if you are in background mode.
2. Make requests while going to background
You need to invoke bluetooth related methods before or while going to background. In most common cases you will need to invoke StartScan or ConnectPeripheral. You can do it in method AppDelegate.DidEnterBackground.
3. Test when device is unplugged from USB
Application behaves differently while being connected to USB port and debugger. If you want to test it properly unplug phone from USB port. Unfortunately, background mode is not 100% reliable (for better support read 5. point), so make sure that essential things in your application don’t assume you will receive something in background mode. Consider also power management – BLE may be limited in case of low energy.
4. Append application state in logs
Debugging background mode is quite challenging. To make it easier just append in logs for each bluetooth operation your application state. It will give you an idea what happens in background and what in foreground.
5. Restore application if terminated
In most cases you don’t need to implement state restoration and relaunching application if terminated, however if you want to rely on background you will have to implement State Preservation and Restoration which is described in Core Bluetooth Background Processing for iOS Apps.
Without this feature, your background mode will work only if your application wasn’t killed by user or system. Remember that system can terminate application in background whenever it needs resources.
In most cases, the system does not relaunch apps after they are force quit by the user. One exception is location apps, which in iOS 8 and later are relaunched after being force quit by the user. In other cases, though, the user must launch the app explicitly or reboot the device before the app can be launched automatically into the background by the system. When password protection is enabled on the device, the system does not launch an app in the background before the user first unlocks the device.
6. Handle bluetooth states
Make sure you implemented handling for bluetooth state changes. Remember that user can turn off bluetooth anytime. Use CBCentralManager.UpdatedState event to handle changes.
7. Error handling
Make sure you implemented error handling properly. Do not assume that service or characteristic will be always discovered. Validate each data received from BLE peripheral. Provide also reconnect meachanism in case of unexpected disconnection. Without awaiting peripheral connection or active scanning, your application won’t awake again from background.
Receiving each advertising packet in background won’t be possible, because scan option CBCentralManagerScanOptionAllowDuplicatesKey is ignored. This may be important if you rely on data included in advertising packet. In order to receive information from device, you will need to establish connection with BLE peripheral.
I also noticed that if BLE peripheral was discovered in foreground, it won’t be discovered again in background unless you restart scanning before going background (invoke StopScan and StartScan).
9. Transitions between foreground and background
Make sure nothing breaks while switching between background and foreground, sample cases to consider:
- connection was established in background and application enters foreground,
- connection was established in foreground and application enters background,
- peripheral connection was requested, application entered background, turn bluetooth off and on,
- start scan, discover peripheral, go out of range, go background, get peripheral in range, check out if it’s discovered (case from 8. point).
There is a lot of edge cases, so you need to investigate your case and test them.
10. Watch out for background limits
In background mode make sure your operations will finish within 10 seconds, otherwise system may terminate further processing. If you need to perform long-term operations, make sure you intercept getting close to time limit according to this article: iOS Backgrounding with Tasks.