I'm looking to maximise my Watch app's widget to be as up to date as possible.
If we imagined the app was a simple step counter, and we wanted to display the users count as up to date as possible. We can conclude:
- We don't care about widget timelines beyond the current entry as we can't predict the future!
- We need to refresh the count as often as possible
- The refresh should be very quick with a straightforward HealthKit query, no networking or heavy work needed.
- We will assume the user has the complication/widget on their active Watch face.
With the standard WidgetKit APIs we can expire the timeline after 15 minutes and in my experimentation a Watch app can usually update its widget timeline at that frequency if it's on the Watch face.
I'm experimenting with two methods to try and improve refreshes further
- A user's step count might not have recently changed when the timeline update is called. I was therefore looking into the HealthKit
enableBackgroundDelivery
API (which requires the HealthKit Background Delivery entitlement to be enabled) to get updates limited to once an hour from a HKObserverQuery, I can then call theWidgetCenter.shared.reloadAllTimelines()
from there. - WatchOS also support the BGAppRefreshTaskRequest(identifier:"") and .backgroundTask(.appRefresh) APIs. I can request updates once every 15 minutes here too and then call the
WidgetCenter.shared.reloadAllTimelines()
.
With option 1, this update opportunity is great as it will specifically update when there's new steps so even once an hour this would be helpful (A real shame to be limited to once an hour even if this used up WidgetKit standard reload budgets: FB13879817, FB11677132, FB10016177). But I can't determine if this update takes away one of the standard timeline expiration updates that already run 4 times an hour? Could I observe additional Health types to get additional updates? Do I need the Background Modes Capability as well as the HealthKit Background Delivery for this in Xcode or just the HealthKit one?
With option 2, I can't find a suitable option in the (short) list of supported background modes in Xcode. Does not selecting any mean my app will get 0 refreshes from this route and so should not be implemented in my use case?
With option 1, this update opportunity is great as it will specifically update when there's new steps so even once an hour this would be helpful
Yeah, I'd say option 1 is the right way to go in your case.
But I can't determine if this update takes away one of the standard timeline expiration updates that already run 4 times an hour?
If reloadAllTimelines
is called when your watchOS app is running in the foreground, the reload won't be counted against the budget; otherwise, it will. This is discussed in Keeping a widget up to date.
Could I observe additional Health types to get additional updates?
Yes, though an additional data type can change at a different pace, and hence your app can be woken up more frequently. This is somehow discussed in this post.
Do I need the Background Modes Capability as well as the HealthKit Background Delivery for this in Xcode or just the HealthKit one?
I don't think you need any background mode other than HealthKit Background Delivery
.
WatchOS also support the BGAppRefreshTaskRequest(identifier:"") and .backgroundTask(.appRefresh) APIs.
BGAppRefreshTaskRequest
is not supported in watchOS, as shown in the availability session here, and so Xcode doesn't have the Background fetch
or Background processing
for an watchOS app.
Best,
——
Ziqiao Chen
Worldwide Developer Relations.