Permissions & background location
iOS grants location in levels, and the jump from When In Use to Always is the one that decides whether this SDK can do its job. The rules are the platform’s, not the SDK’s, and they are unforgiving in a specific way: you usually get one chance at the Always prompt.
Requesting
let status = try await BackgroundGeolocation.requestPermission()There is no requester object to construct (the Android SDK needs one; iOS does not). What the call does depends on where the user already is:
| Current state | What happens |
|---|---|
| Not determined | The standard prompt, offering When In Use. |
When In Use, locationAuthorizationRequest = Always | iOS shows the Always upgrade prompt — once per install. |
| Denied, or Always already granted | No prompt; the current status is returned. |
The returned
AuthorizationStatus is
always, whenInUse, denied, restricted or notDetermined.
The Always prompt is a one-shot
iOS shows the When-In-Use → Always upgrade prompt once. Decline it, and
calling requestPermission() again does nothing at all: no dialog, no error,
the same status back. From then on the only path to Always is the user going to
Settings themselves.
Two consequences worth designing around:
- Ask when the value is obvious. Not at first launch — right after the user does the thing that needs background tracking.
- Detect the dead end and explain it. If you asked and still have
whenInUse, show your own screen pointing at Settings → your app → Location → Always. The SDK does not pop that alert for you; the RN and Flutter SDKs have alocationAuthorizationAlertconfig for it, and this facade deliberately does not port it — a native app can present a better one.
Precise location
Since iOS 14 a user can grant approximate location, a coarse grid that is
useless for tracking: nearly every fix fails the accuracy gate. Read it from
getProviderState():
let provider = await BackgroundGeolocation.getProviderState()if provider.accuracyAuthorization == .reduced { // Ask for a temporary upgrade, or explain why the map will be wrong. let result = await BackgroundGeolocation.requestTemporaryFullAccuracy(purpose: "DeliverFullAccuracy")}purpose must be a key in your NSLocationTemporaryUsageDescriptionDictionary
— if it is not, iOS may never call back at all, and the SDK’s 30-second
watchdog resolves with the unchanged authorization rather than hanging.
Temporary full accuracy lasts until the app is next relaunched. It is a stopgap for a session, not a substitute for the user granting Precise.
What each level actually does
Always + Precise — everything in this documentation works: background fixes, force-quit relaunch, geofences.
When In Use — fixes arrive only while your app is in the foreground (plus a
short grace period). The engine, the queue and the filter all keep working;
the stream simply stops when the user leaves. Nothing announces it, which is
why onProviderChange is worth
subscribing to.
Approximate — fixes arrive but are coarse enough that the accuracy filter rejects most of them. A track that is mysteriously empty while permissions “look granted” is usually this.
Denied or Location Services off device-wide — start() still succeeds and
the engine still runs; it simply never receives a fix. Check
getProviderState() rather than assuming the SDK is broken.
Core Motion
NSMotionUsageDescription covers activity recognition, which the motion state
machine uses to notice you started moving. Denied, it falls back to speed and
the wake region — departures are detected later, but tracking still works.
The blue indicator
While tracking with Always authorization, iOS may show the blue
background-location pill. On the default session path this is not
configurable: the session that keeps delivery alive in the background implies
the indicator, and no API hides it.
showsBackgroundLocationIndicator
applies only to the legacy path
(useSessionEngine: false),
which trades reliability for a hidden indicator. When In Use always shows it,
whatever the engine.