Extended Runtime API - Health Monitoring

In the WWDC 2019 session "Extended Runtime for WatchOS apps" the video talks about an entitlement being required to use the HR sensor judiciously in the background. It provides a link to request the entitlement which no longer works: http://developer.apple.com/contect/request/health-monitoring

The session video is also quite hard to find these days.

  • Does anyone know why this is the case?
  • Is the API and entitlement still available?
  • Is there a supported way to run, even periodically, in the background on the Watch app (ignoring the background observer route which is known to be unreliable) and access existing HR sensor data

Your third question seems to be answered in this post. Please take a look and feel free to follow up here.

I am unclear why exactly the video was removed, but videos were removed from time to time if the content isn't quite relevant as the system evolves. If the post mentioned above doesn't help, please share what you would like to achieve so folks can weigh in.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Thank you for the response. Unfortunately as this isn't for a research app SensorKit is probably Out.

There seems to be a lot of developers all trying to achieve similar things and running into various problems around background access to HealthKit data on WatchOS.

  1. This post discusses using a Workout session to maintain background access. This is a very poor user experience as far as battery life goes but many apps are resorting to it for lack of better alternative.

  2. This post discusses getting crashes on WatchOS 26 for exhausting CPU time with background HKObserverQueries, I too am seeing this despite calling the completion handler immediately and not running any processing code, is there any solution?

  3. Related to the scenario in point 2, are we also required to implement @WKExtensionDelegateAdaptor(ExtensionDelegate.self) var extensionDelegate and call tasks completed or is the completion handler in the observer sufficient?

func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
        for task in backgroundTasks {
          task.setTaskCompletedWithSnapshot(false)
        }
    }
  1. In various places such as here I see discussion about if the app has a complication on the watch face and as such, it gets 4 updates an hour. This I find confusing... Pre-WatchOS 9 with ClockKit complications this makes sense, but since migrating to WidgetKit, I now get my widget extension woken for timeline updates and not my app. This is fine but not helpful for my use case and so has caused a regression in my app as I no longer get Watch app background updates from this... I hope I've made the differences clear here?
  2. Related to previous point, my understanding with this code try await healthStore.enableBackgroundDelivery(for: HKQuantityType(.heartRate),frequency: .immediate) is that although I ask for immediate, I'll be limited to 1 update an hour for heart rate on WatchOS, do I still get that update if I don't have a complication on the Watch face? If I do have a complication on the active watch face, does that update have any impact on my complication WidgetKit timeline refreshes?

Overall I think there is a lot of confusion in the developer community with this topic and if not before, hopefully in the next cycle this is either all cleared up or even better some new slightly less restrictive APIs arrive.

To answer your question for me, my use case is wanting to review health data that the watch already automatically records (I don't want to trigger battery intensive additional sampling) and then with provide notifications or other features to the user from that. These updates could probably be done in a second of CPU time if only I could reliably access it...

This post discusses using a Workout session to maintain background access. This is a very poor user experience as far as battery life goes but many apps are resorting to it for lack of better alternative.

Yeah, but I'd say that, because of the limited resources, continuously running a watchOS app in background hasn't been a supported use case from day one. Folks who are exploring how to achieve that goal might consider shifting the direction.

Also to be clear, using an active workout session in a non-workout app to extend the background execution time risks an App Review rejection.

This post discusses getting crashes on WatchOS 26 for exhausting CPU time with background HKObserverQueries, I too am seeing this despite calling the completion handler immediately and not running any processing code, is there any solution?

In that case, I can ony suggest that yo file a feedback report, as mentioned in the post. If you already have one, please share so I can check the status. I see folks reporting this issue from time to time, but haven't yet seen them sharing their report.

Related to the scenario in point 2, are we also required to implement @WKExtensionDelegateAdaptor(ExtensionDelegate.self) var extensionDelegate and call tasks completed or is the completion handler in the observer sufficient?

Yes, in case your app is getting involved any background activity, you'd implement the handle method and set the task as completed. The SwiftUI equivalent is backgroundTask(_:action:). This is mentioned in Schedule and handle background refresh tasks.

In various places such as here I see discussion about if the app has a complication on the watch face and as such, it gets 4 updates an hour. This I find confusing... Pre-WatchOS 9 with ClockKit complications this makes sense, but since migrating to WidgetKit, I now get my widget extension woken for timeline updates and not my app. This is fine but not helpful for my use case and so has caused a regression in my app as I no longer get Watch app background updates from this... I hope I've made the differences clear here?

Having a complication on the active watch face adds the priority of the app, and impacts the execution time allocation policy on the system side, no matter the complication is based on ClockKit or WidgetKit.

Related to previous point, my understanding with this code try await healthStore.enableBackgroundDelivery(for: HKQuantityType(.heartRate),frequency: .immediate) is that although I ask for immediate, I'll be limited to 1 update an hour for heart rate on WatchOS, do I still get that update if I don't have a complication on the Watch face?

Yes. I am not quite sure if we document "1 update an hour" though. I'd expect that, without an active complication, you will get less than 4 updates per hour sometimes, especially when other apps that have higher priority (because they have an active complication and are used more frequently) need background execution time as well.

If I do have a complication on the active watch face, does that update have any impact on my complication WidgetKit timeline refreshes?

I don't think the WidgetKit complication timeline refresh is counted to the budget of the app's background execution time.

Overall I think there is a lot of confusion in the developer community with this topic

Indeed. An issue related to the background updates on watchOS can be hard to debug because it can happen randomly – The device situation, such as the battery level and other installed apps, impacts the background execution time allocation. Other than expecting that you can get an update in a period of time, having a fallback mechanism when you don't any update will be importnat as well.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Ok, thank you Ziqiao for the detailed & quick response, most appreciated.

Folks who are exploring how to achieve that goal might consider shifting the direction.

I agree... I just hope we can either clarify better alternative for those apps, or if not available, provide input to future APIs.

In that case, I can ony suggest that yo file a feedback report, as mentioned in the post. If you already have one, please share so I can check the status. I see folks reporting this issue from time to time, but haven't yet seen them sharing their report.

I have filed a feedback FB22115840 and created a sample project and provided a crash report. I think all the code is correct in terms of calling of completionHandler() and I've tried to simplify the project🤞🤞 really hope this is useful as would love to see a fix soon.

The SwiftUI equivalent is backgroundTask(_:action:).

I read the guidance but I still struggle to understand if on WatchOS, when using that API, you can avoid using a @WKExtensionDelegateAdaptor

Having a complication on the active watch face adds the priority of the app, and impacts the execution time allocation policy on the system side, no matter the complication is based on ClockKit or WidgetKit.

The difference I'm trying to highlight here is (AFAIK) that older ClockKit APIs wake your WatchOS app, WidgetKit reloads wake your WidgetExtension where you can do less from (no WatchConnectivity, no local notifications etc...), making this a regression.

Yes. I am not quite sure if we document "1 update an hour" though.

I got this from here. It's confusing because it says other than certain metrics you're limited to hourly, but then it mentions the bit you covered "Also, in watchOS, the background updates share a budget with WKApplicationRefreshBackgroundTask tasks. Your app can receive four updates (or background app refresh tasks) an hour, as long as it has a complication on the active watch face." So which is it I wonder....

I don't think the WidgetKit complication timeline refresh is counted to the budget of the app's background execution time.

Ok, so having a complication on the watch face implemented with WidgetKit gives you what ever reload allowance it gives you in the WidgetKit timeline, plus, totally separately you get up to 4 background refreshes per hour in your Watch App which would apply to the HealthKit HKObserver?

Thank you very much for all the help here, the thing I'm still trying to get to here is can my WatchOS app (not widget extension) get access to 4 updates per hour reliably.

Ok, so having a complication on the watch face implemented with WidgetKit gives you what ever reload allowance it gives you in the WidgetKit timeline, plus, totally separately you get up to 4 background refreshes per hour in your Watch App which would apply to the HealthKit HKObserver?

Yes, that is my understanding, assuming the device's battery level is fine. If you see the system doesn't behave that way, please start with a feedback report with a sysdiagnose.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Extended Runtime API - Health Monitoring
 
 
Q