Repository navigation
Description
Bug Report
Plugin(s)
@capacitor/geolocation 8.2.4 (iOS). The same code is in 9.0.0-next.3.
Capacitor Version
@capacitor/core and @capacitor/ios 8.5.3; iPhone 16 Pro simulator, iOS 18.3.
Platform(s)
iOS
Current Behavior
Two problems, both from how GeolocationPlugin.swift handles the .notDetermined status.
1. requestPermissions() (and getCurrentPosition()) never settle after "Allow Once" lapses.
- Call
requestPermissions()and choose Allow Once, thengetCurrentPosition(): works. - Send the app to the background (for example, open Apple Maps). About 10 seconds later iOS ends the one-time grant and the status becomes
.notDetermined. - The plugin's
authorisationStatusPublishersink sees.notDeterminedand callsrequestLocationAuthorisationby itself. The app is not in use, so locationd drops it:locationd: {"msg":"#AuthPrompt #Warning Dropping authorization request placed while insufficiently in-use", "ClientKeyPath":"i<bundle id>:", "requestType":2, "inUseLevel":0} - Back in the app, call
checkPermissions()(returnsprompt), thenrequestPermissions(). Because the status is already.notDetermined,requestPermissionsonly adds the call torequestPermissionsCallbacksand waits for the publisher. The publisher never fires again, since nothing changes, and no new authorisation request is made. The promise never resolves or rejects.getCurrentPositionparks the same way (default: breakinhandleLocationRequest), and itstimeoutdoes not help because the timer only starts inrequestSingleLocation, after authorisation.
Every later call hangs too, until the app process is restarted.
2. A permission prompt appears without any call.
Once any plugin method has been called in the process, the sink stays subscribed. Whenever the status changes to .notDetermined while the app is active, the sink requests authorisation, and the system prompt appears over whatever screen is open, with no JS call behind it:
locationd: {"msg":"Showing #AuthPrompt", "requestType":2, "Client":"i<bundle id>:"}
Expected Behavior
requestPermissions()shows the prompt (or resolves) whenever the status is.notDeterminedat the time of the call.- The plugin only asks for authorisation on behalf of a pending call.
Code Reproduction
await Geolocation.requestPermissions({ permissions: ['coarseLocation'] }); // choose "Allow Once"
await Geolocation.getCurrentPosition(); // works
// background the app for ~10 s, return
await Geolocation.checkPermissions(); // { coarseLocation: 'prompt' }
await Geolocation.requestPermissions({ permissions: ['coarseLocation'] }); // never settlesAdditional Context
The patch we ship (patch-package), which fixes both cases in our testing:
@@ requestPermissions
if locationService?.authorisationStatus == .notDetermined {
shouldSetupBindings()
callbackManager?.addRequestPermissionsCallback(capacitorCall: call)
+ requestLocationAuthorisation(type: .whenInUse)
} else {
@@ bindAuthorisationStatusPublisher
case .notDetermined:
- self.requestLocationAuthorisation(type: .whenInUse)
+ if self.hasPendingCallbacks() {
+ self.requestLocationAuthorisation(type: .whenInUse)
+ }
@@ handleLocationRequest
case .restricted: callbackManager?.sendError(.permissionRestricted)
+ case .notDetermined: requestLocationAuthorisation(type: .whenInUse)
default: break
@@
+ func hasPendingCallbacks() -> Bool {
+ guard let callbackManager else { return false }
+ return !callbackManager.requestPermissionsCallbacks.isEmpty
+ || !callbackManager.locationCallbacks.isEmpty
+ || !callbackManager.watchCallbacks.isEmpty
+ }
Activity