Streaming is available in most browsers,
and in the Developer app.
-
Get timely alerts from Bluetooth devices on watchOS
Find out how Bluetooth devices can send timely and relevant alerts to Apple Watch. We'll show you how to take advantage of periodic data in complications, explore background peripheral discovery, and help you learn how to use characteristic monitoring in watchOS. We'll also share best practices and design guidance for creating a great Bluetooth accessory.
Resources
Related Videos
WWDC21
-
Download
Yann Ly-Gagnon: Hi. my name is Yann. I'm a core Bluetooth engineer. Today, I want to talk to you about timely alerts for your Bluetooth devices on Apple Watch.
First, we will review how to update a complication while your watchOS App is in the background.
Then, we will dive into how to listen for timely alerts on your watchOS App.
We will also see new ways you can discover peripherals on watchOS 9.
Finally, we will provide best practices and recommendations to help you design your Bluetooth accessory.
Let's jump into our first topic: how to update a complication in the background for your watchOS App. Last year, in watchOS 8, we introduced a way to update complications with your Bluetooth accessory during Background app refresh.
This is great for data that can be updated periodically, like in this example showing me the current air temperature.
As a quick refresher, last year watchOS allows you to update your complication and use Background app refresh, which runs periodically in the background. Whenever Background app refresh happens, it allows your app to reconnect to your Bluetooth peripheral, retrieves data, and then disconnects from your peripheral. For more details about this, watch the video called "Connect Bluetooth devices to Apple Watch." But what if a time-sensitive event happens on your Bluetooth peripheral that the user wants to know about? In watchOS 9, we are introducing a way to listen for alerts from your Bluetooth accessories in the background.
Here's how it works. You will connect your device when your app is running, and start monitoring a characteristic.
When your app stops running, Core Bluetooth maintains the connection to your device on your behalf, and continues listening for changes to your characteristic.
When your device changes the value of that characteristic, your app will get runtime to process that event. You could post a local notification or send a network request, for example. This is intended to provide users with time-sensitive information they care about.
Let's say I have a food thermometer. I can set a desired cook temperature to get alerted when I should remove my food from the oven. As the temperature approaches the desired temperature, the thermometer changes a characteristic's value, and the app posts a local notification that the food is almost ready.
When the food is done, I receive the desired notification.
And if the temperature keeps rising, I get one final notification.
First, let's review how to configure the Background modes.
Add Bluetooth-central to UIBackgroundModes in your Watch app's Info.plist.
In Xcode it's called "Required background modes," and you should add "App communicates using CoreBluetooth." Note that those Info.plist entries are the same as your app has for iOS if you want to use background execution as a central.
You will need to edit your watchApp info.plist manually and not rely on the iOS "Signing capabilities." Let's look at the code. Assuming you're already connected, you found a GATT service and just discovered a GATT characteristic. You will get the didDiscoverCharacteristicFor callback.
Inside the callback, you can decide to get notifications every time the value changes.
This is the same API as in watchOS 8, with the difference that it will also work while your app is in the background.
Then implement the delegate method to handle changes to the characteristic's value with didUpdateValueFor.
Once the characteristic changes, you can post a local notification here, send a network request, or whatever makes sense for your app. This method will be called both in the foreground and in the background, so make sure you perform the correct action in both cases.
Now, let's talk about some situations you need to consider.
First, on the topic of Bluetooth reconnections. If your device goes out of range, the Bluetooth connection will disconnect after a timeout.
If this happens, your app will briefly get background runtime to call "connectPeripheral" in order to attempt reconnection. This is the same as what happens on iOS. As soon as the device is in range again, Core Bluetooth will reconnect to it.
Now, let's talk about some limits. These limits are important to maintain optimal battery life for Apple Watch users.
If your device is on the edge of Bluetooth range and repeatedly disconnects while in Background BLE connection, the reconnection range will be reduced. This means only devices close to the Apple Watch will reconnect.
Those limits are counted on a rolling window of 24 hours and are reset whenever the user interacts on your App. Another limitation is regarding the number of background runtime opportunities for timely alerts.
Only monitor characteristics that will change when something critical to the user happens. If you need to gather periodical data from your device, this should be done with Background app refresh.
When your app is about to exceed the limit, the notification LeGattNearBackgroundNotificationLimit will be posted. It is a good practice for your app to monitor that error and realize that the user isn't interacting with the watchOS app.
If this alert is important, it might be the right time to find another way to communicate with your user, such as through a network request or UI changes on your Bluetooth peripheral.
After the limit is exceeded, the notification called LeGattExceededBackgroundNotificationLimit will be posted.
After this point, your app will no longer receive background runtime and will revert back to watchOS 8 behavior, where there will be no background connection and only background app refresh.
You can retrieve those two notifications in the error field of the GATT Notification Update. For background BLE connection, we recommend using the error to know when the limit is reached instead of counting down. For watchOS 9, the background runtime limit is set to 5. Both of these limits are reset whenever the user interacts with your app. They also reset 24 hours after the limit was reached in case there has been no user interaction with your app. Note: These limits only apply to Bluetooth Background LE connections. Background app refresh will continue to happen if your complication is on the active watch face, regardless of these limits.
The amount of time you get to process each event is very short. There may not be enough time to do extremely complex processing, but enough to alert the user something important is happening. Finally, listening for timely alerts in the background requires Apple Watch Series 6 or later. Listening for alerts isn't the only thing you can do in the background. In watchOS 9, you can discover peripherals while your app is in the background.
Let's say I have a Bluetooth medical device and a watchOS app that detects any timely alerts from it. To conserve power, the peripheral doesn't advertise until it detects a serious condition.
Therefore, there's no connection between the device and Apple Watch yet. Here, the watchOS App will scan for a unique Service UUID from the medical device.
Now, when the medical device detects something is serious, it starts advertising. The Apple Watch discovers this peripheral and launches the app in the background.
The app can then alert the user of the condition detected.
Here's how it works: The Watch app will initiate a scan for peripherals, and Core Bluetooth will continue scanning in the background.
Once the peripheral's advertisement is detected, the app is given background runtime and can initiate a connection.
Let's dive into the code to make this happen. The API hasn't changed from watchOS 8, but the scan will be honored even the app is in the background.
Call "scanForPeripherals" with the service UUID you want to watch for. You can do this while your app is in foreground, and it will continue while the app is in the background. Note that if you ask for the option "allowDuplicatesKey," it will only be available when the app is in foreground. Now, let's talk about some limits. There is a limit on the number of times your app will be given background runtime between app launches. This limit is combined with the background runtime we saw previously when a GATT characteristic changes. Also, scanning for peripherals in the background requires Apple Watch Series 6 or later.
In summary, we can now scan for a limited number of Bluetooth service UUID while the watch is scanning in the background. Now let's talk about how to design your accessory to make the most of these features.
There is a power tradeoff you need to consider when designing your Bluetooth accessory.
If power consumption is a concern, you should opt for a topology where your device can go into deep sleep and only advertise relevant information when an alert happens. The tradeoff is, you will have extra latency with the Bluetooth discovery time at every timely alerts, but you will be able to save more power.
This is the topology provided in the example with the medical device.
On the other hand, if you need low latency for your timely alerts, but the power is not so much a concern, you can consider using Background LE connection and send your alerts with GATT indications. Note that there is a limit of two Bluetooth connections for each app.
This is the topology we saw in the example for temperature sensor.
In order for your users to have the best experience with timely alerts, consider adding as much processing and intelligence on your peripheral to filter the data that is time critical versus non-time critical.
Coming back to our temperature example, instead of transmitting every temperature, you can send only the relevant events or when the temperature changes. The benefit of this approach is, if you properly separate the time-critical events from periodic data, both your peripheral and the Apple Watch user will save on power, thus an overall better experience.
When your device disconnects, we recommend advertising to re-establish the connection. The advertisement interval depends on the requirement of your Bluetooth peripheral, such as how fast it needs to reconnect, battery life, et cetera. In the accessory guidelines, we offer a few different values you can use. For example, if your device is battery constrained, you can use a value of 1022.5 milliseconds.
Another example: if you advertise at a rate of 20 milliseconds, it should allow for a detection within a second in ideal conditions.
You could design such that this high advertisement rate can be used only while a critical event happens.
Now let's talk about connection interval. If you opt for a topology where your device remains connected in the background, we highly recommend using a long connection interval, such as at least 150 milliseconds. This will allow to save battery on your peripheral and provides best user experience on Apple Watch. Bluetooth 5.3 is coming to Apple Watch, along with connection sub-rating. This would allow to increase the connection interval while the Bluetooth peripheral is idle and quickly change to smaller connection interval when you need lower latency.
Here is a table showing the differences between platforms. These are the currently supported configurations for Bluetooth Low Energy. Last year we introduced Background app refresh for watchOS as a new background execution mode. This year, if you own a Series 6 and above, we improved the background execution with timely alerts as we described today.
Thanks for watching!
-
-
3:41 - Listen for alerts
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { peripheral.setNotifyValue(true, for: characteristic) } func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { if let newData = characteristic.value { // Post a local notification. } }
-
9:15 - Discover peripherals
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { central.scanForPeripherals(withServices: [myCustomUUID]) }
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.