I have a watchOS app that records workouts on a piece of Bluetooth LE fitness equipment.
It runs an HKWorkoutSession and, for the length of that session, stays connected to the
machine and subscribed to a characteristic that reports step data once a second. Each
notification is recorded into the workout's HKLiveWorkoutBuilder. The connection needs to
last as long as the workout — typically 20–60 minutes.
The watch app declares both:
WKBackgroundModes=workout-processingUIBackgroundModes=bluetooth-central
This works indefinitely while the app is in the foreground. When the app leaves the foreground — the user lowers their wrist or goes to the watch face — the peripheral disconnects after roughly three minutes, and the workout loses the rest of its data.
What we have been able to establish from logs at the moment of the disconnect:
- The app is not suspended. Notifications arrive at a steady 1 Hz with no gaps right up to the disconnect (271 and 272 consecutive notifications in two separate cases), so the workout session is keeping the app running as expected.
centralManager.stateis.poweredOn.centralManager(_:didDisconnectPeripheral:error:)is called witherror == nil, and the peripheral's state is.disconnected.- Nothing in our code calls
cancelPeripheralConnectionat that point; our own teardown runs afterwards, in response to the disconnect. - The user is still using the machine, and the machine is still notifying.
- Reproducible: two captures roughly three minutes and twenty seconds after the app backgrounded.
I understand from Get timely alerts from Bluetooth devices on watchOS (WWDC22) that background Bluetooth on watchOS is intended for timely alerts, with a limited number of background runtime opportunities, and that periodic data collection should use Background App Refresh instead. Our data arrives once a second for the whole workout, so I assume we are well outside what that mode is budgeted for, and that the disconnect is the system reclaiming the link.
My questions:
-
Does a running
HKWorkoutSessiongrant any exemption from the background Bluetooth limits?workout-processingclearly keeps the app executing — we receive notifications for minutes after backgrounding — but the connection is still torn down. Is Bluetooth's background budget independent of the workout runtime, and is that the intended behaviour? -
Is there a supported way to hold a BLE connection for the duration of a workout session? Reading a fitness machine's telemetry for an entire workout seems like a mainstream use of both frameworks together, but I cannot find a configuration that survives the app leaving the foreground.
-
Would a longer connection interval change this? The session recommends at least 150 ms. If the disconnect is driven by connection-event volume rather than notification count, a slower interval or a lower update rate may be the answer — and if so, is there a documented threshold to aim for?
-
error == nilon disconnect. The documentation says a nil error means the disconnect was requested locally viacancelPeripheralConnection, which we do not call here. Should a system-initiated reclaim of the link be reported this way, or is there a more specific error we should be looking for? We now log theerroronperipheral(_:didUpdateValueFor:error:)to catchLeGattNearBackgroundNotificationLimitandLeGattExceededBackgroundNotificationLimit, but have not yet observed either before a disconnect. -
Is there any entitlement path for this? We are aware of
com.apple.developer.bluetooth-central-backgroundand that it is limited to continuous glucose monitoring. Is there an equivalent route for fitness equipment, or is foreground-only the expected design for this class of app?
If continuous background Bluetooth is simply not available here, I would like to know that clearly, so I can design around the disconnect — reconnecting mid-workout rather than treating a dropped link as the end of the session.