I have a watchOS app with a connected iOS app using Swift and SwiftUI. The watchOS app should read heart rate date in the background using HKOberserQuery and enableBackgroundDelivery(), send the data to the iPhone app via WCSession. The iPhone app then sends the data to a Firebase project. The issue I am facing now it that the app with the HKObserverQuery works fine when the app is in the foreground, but when the app runs in the background, the observer query gets triggered for the first time (after one hour), but then always get terminated from the watchdog timeout with the following error message: CSLHandleBackgroundHealthKitQueryAction scene-create watchdog transgression: app<app.nanacare.nanacare.nanaCareHealthSync.watchkitapp((null))>:14451 exhausted real (wall clock) time allowance of 15.00 seconds
I am using Xcode 16.3 on MacOS 15.4 The App is running on iOS 18.4 and watchOS 11.4
What is the reason for this this issue? I only do a simple SampleQuery to fetch the latest heart rate data inside the HKObserverQuery and then call the completionHandler. The query itself takes less than one second.
Or is there a better approach to read continuously heart rate data from healthKit in the background on watchOS? I don't have an active workout session, and I don't need all heart rate data. Once every 15 minutes or so would be enough.
In the use case you described, HKOberserQuery
+ enableBackgroundDelivery
is the right way to go.
When you use enableBackgroundDelivery
in watchOS though, 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. This is documented here.
When your app is woken up to run the update handler of the observer query, watchOS assigns a period of time for your app to run in the background. The error message you provided indicates that your app ran out of the time allowance.
In the case where you "do a simple SampleQuery to fetch the latest heart rate data inside the HKObserverQuery," 15 seconds
is quite enough, and so I am wondering if you have any code path that failed to call the completionHandler
passed to the updateHandler
of your observer query, which had watchOS believe that your observer query was running in the background for ever. You can double check if that is the case.
The other factor is Watch Connectivity. Your watchOS app uses WCSession
to send data to your companion iOS app when running in the background. The execution time is counted against the budget as well. For debugging purpose, you can temporarily disable the data transfer, and check if the crash still happens.
If you've reviewed all your code paths, and are confident that your code should not hit the budget, I’d suggest that you file a feedback report with the following information for the watchOS folks to investigate:
- The relevant code snippets, or even better, a minimal sample that reproduces the issue.
- A full crash report.
- A co-sysdiagnose. See the Instructions here for how to capture one.
If you do so, please share your report ID here for folks to track.
Best,
——
Ziqiao Chen
Worldwide Developer Relations.