Passive Read
Native iOS ==> Bluetooth framework
Overview
The ValidicBluetooth library supports passively reading from a given set of Bluetooth peripherals. Passive Bluetooth reading is a long-lived capability. Once enabled, the library will listen for and read from the specified peripherals. Reading will automatically occur once the peripheral is discovered regardless of app state. This includes if the application is currently in the foreground, in the background, or has been terminated due to memory pressure. Apps using this feature must have been started by the user at least once since the iOS device has booted otherwise the feature will not be active even if enabled.
Setup
Passive Bluetooth reading relies upon iOS Bluetooth state restoration and background processing support. Apps using passive reading need to set Bluetooth as a required background mode in the Info.plist. In Xcode, in the Info.plist
add a new key, Required background modes
, add an item with value App communicates using CoreBluetooth
.

If editing the XML source file, add bluetooth-central
background mode as follows:
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
</array>
Passive Readings
VLDBluetoothPassiveManager
is a singleton which coordinates the passive reading process. Passive reading is enabled by passing it an array of peripheral IDs:
VLDBluetoothPassiveManager.sharedInstance().peripheralIDs = [1, 2]
Or by passing an array of peripherals:
if let peripheral = VLDBluetoothPeripheral(forID: 1) {
VLDBluetoothPassiveManager.sharedInstance().setPeripherals([peripheral])
}
To disable passive reading, set the peripheralIDs
property to an empty array or nil
.
VLDBluetoothPassiveManager.sharedInstance().peripheralIDs = nil
To support state restoration, a method on the passive manager must be called on app launch. This should be done in the application:didFinishLaunchingWithOptions:
method of the AppDelegate as follows:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
VLDBluetoothPassiveManager.sharedInstance().restoreState()
return true
}
State restoration will automatically start the app in the background when a requested peripheral is discovered. Within application:didFinishLaunchingWithOptions:
, apps can determine if they were launched in background and avoid expensive operations.
Passive Reading Notifications
Passive readings are automatically sent to the Validic server. Session notifications are sent on upload success or failure. Passive Bluetooth specific notifications are sent when a device is discovered and becomes ready to read. Notifications are also sent for reading success and failure. These notifications specify the associated peripheral ID in the userInfo dictionary with a key, peripheralID
.
kVLDBluetoothPassiveDidReadNotification
is sent when records are read. The notification object is an array ofVLDInformRecords
read from the peripheral.kVLDBluetoothPassiveDidFailNotification
is sent when failing to read from a discovered peripheral. The notification object is anNSError
with details about the failure, ornil
.kVLDBluetoothPassiveIsReadyToReadNotification
is sent when the peripheral becomes ready to read. This notification is only sent out for devices that are discoverable before a measurement is taken. It is important to handle this notification in an app and provide feedback to the user that the device has been discovered and is ready to take a measurement.- Since Passive Bluetooth reading is usually performed in the background, a standard UI is not available.
- A local notification can be sent with sound to provide feedback to the user that the app is ready to read from the peripheral. If the user doesn't hear the sound, it indicates that the device has not been discovered.
The following code fragment registers an observer for the kVLDBluetoothPassiveIsReadyToReadNotification
notification and sends a local notification including the name of the peripheral. A similar process could be done for the kVLDBluetoothPassiveDidReadNotification
to confirm to the user that a reading was successfully taken.
NotificationCenter.default.addObserver(forName: NSNotification.Name.vldBluetoothPassiveIsReadyToRead, object: nil, queue: nil) { (notification) in
if UIApplication.shared.applicationState == .background {
if let peripheralID = notification.userInfo?["peripheralID"] as? UInt,
let peripheralName = VLDBluetoothPeripheral.init(forID: peripheralID)?.name()
{
let localNotification = UILocalNotification()
localNotification.alertBody = "Ready to read \(peripheralName)"
UIApplication.shared.presentLocalNotificationNow(localNotification)
}
}
}
Passive Read Considerations
There are situations where passive Bluetooth reading is stopped and requires the app to be relaunched. These situations include:
- When the user explicitly kills the app by swiping up on the app in the App Switcher, the app will not be restarted by iOS when a peripheral is discovered.
- If the iOS device is rebooted, state restoration is no longer in effect and iOS will not automatically restart the app when a peripheral is discovered. The app needs to be launched at least once after reboot. When the app is launched, the passive manager remembers what peripherals - if any - were being passively read and restarts the capability.
Passive Bluetooth reading has some additional considerations due to the behavior of the iOS Bluetooth stack and background support.
- Interactive, ie non-passive, Bluetooth operations will suspend passive Bluetooth processing until the interative operation completes.
- When scanning for peripherals in the background, iOS reduces the frequency and duration of time the phone listens for devices. This may cause some peripherals to not be discovered in the background. This varies by phone model and peripheral. Generally older phone models are more likely to miss discovery of a peripheral.
- Nonin pulse oximeters, ChoiceMMed pulse oximeters and scales are not usually detectable when in the background.
- The Pyle thermometer is sometimes not discovered on older phones when the app is in the background.
- iOS has additional heuristics to determine scanning frequency which may not be documented. If multiple apps on the phone are performing background scanning, scanning may become more infrequent.
- To prevent repeated attempted reads from a device for one actual reading, the passive manager waits after a successful read until the device stops broadcasting, then waits an additional few seconds before acknowledging the device again.
- If multiple readings are attempted in quick succession, the second reading may not be collected. Instead, the user should wait until the device powers off and then wait an additional 15 seconds or longer before attempting another reading.
Updated 18 days ago