I'm working on an iPhone app that continuously monitors device tilt with Core Motion. When the device has been held tilted forward past a threshold angle for a sustained period, the app raises a local notification.
The detection has to keep running while my app is in the background. The situation I need to detect is, by definition, one where the user is looking at some other app — if my app were in the foreground, there would be nothing to detect. So a foreground-only implementation would not implement the feature at all.
While testing this I ran into a behavior I would like to understand properly before I rely on it.
What I observe
CMMotionManager device-motion updates to a backgrounded app stop within a few seconds of the app leaving the foreground — unless a Core Location session is running at the same time. With location updates started under When In Use authorization and allowsBackgroundLocationUpdates = true, the device-motion callbacks continue for the whole time the app is backgrounded. Stop the location session, and they stop again.
I built a focused sample to measure this. It starts device-motion updates at 10 Hz on a background OperationQueue and counts every callback, records the count on didEnterBackground, and on willEnterForeground logs how many arrived during the interval against how many would be expected at 10 Hz. Measured on an iPhone running iOS 26.5.2, launched from the Home screen with no debugger attached:
location ON | background 137s | received 1366 / expected ~1373 (99.5%)
location OFF | background 129s | received 2 / expected ~1286 (0.16%)
Both callbacks in the second run arrived immediately after the transition to the background; nothing arrived over the remaining two minutes.
One thing that cost me a test cycle, in case it saves someone else one: the difference only shows up when the app is launched from the Home screen. With the Xcode debugger attached the app is not suspended, and both cases deliver callbacks for the entire interval.
The location session in the sample is configured as low as I can make it, since the app never reads the coordinates:
manager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
manager.distanceFilter = 3000
manager.activityType = .other
manager.pausesLocationUpdatesAutomatically = false
manager.requestWhenInUseAuthorization()
// started from the foreground, once authorization is granted
manager.allowsBackgroundLocationUpdates = true
manager.startUpdatingLocation()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// Intentionally empty. This sample does not use the location values.
}
My questions
Is continuous CMDeviceMotion delivery to a backgrounded app dependent on an active Core Location session? Is that intended and expected behavior on current iOS versions, or an implementation detail I should not be relying on?
If it is expected behavior, what configuration would you recommend for an app in this situation? Specifically, is kCLLocationAccuracyThreeKilometers with a large distanceFilter sufficient to sustain the session, or does reliable delivery require a higher accuracy or a smaller distance filter?
Is there another supported API or background execution mechanism that delivers continuous device-motion or accelerometer data to a backgrounded app? I am aware of CMSensorRecorder for retrospective retrieval, but I need to react in near real time. I would like to be sure I am not overlooking a more appropriate API.
Environment: iOS 18.0 and later, iPhone only, Swift / SwiftUI. I have the focused sample project available if it would be useful.
Thanks very much for any help.
0
0
5