Bug description
On iOS, after one transient CoreLocation failure, every subsequent Geolocation.getCurrentPosition() call in the app session fails with OS-PLUG-GLOC-0010 (timeout) — even when the device gets a GPS fix — until the app process is killed and relaunched.
Root cause (traced through the plugin + IONGeolocationLib source)
CLLocationManager.requestLocation() frequently fails with the transient kCLErrorLocationUnknown on a cold start / indoors with enableHighAccuracy: true. Apple's docs say this error should be ignored while the manager keeps trying.
- In
IONGLOCManagerWrapper (ion-ios-geolocation), locationManager(_:didFailWithError:) sets currentLocation = nil, and the currentLocationPublisher pipeline turns that into a failure (tryMap throws IONGLOCLocationError.locationUnavailable).
- In
GeolocationPlugin.swift → bindLocationPublisher(), that error is handled with:
.catch { ... self?.callbackManager?.sendError(.positionUnavailable)
return Empty<IONGLOCPositionModel, Never>().eraseToAnyPublisher() }
Empty() completes the stream, so the Combine subscription (locationCancellable) is permanently finished.
locationInitialized is never reset, so the guard in bindLocationPublisher() (guard !locationInitialized else { return }) prevents any re-subscription for the rest of the process lifetime.
- Net effect: every later
getCurrentPosition() registers its callback and requestSingleLocation() runs — didUpdateLocations even fires with a valid fix — but there is no subscriber left to deliver it, so the only thing that ever resolves the call is the timeout timer (locationTimeoutPublisher has its own, still-alive subscription).
The only recovery paths are relaunching the app, or appDidBecomeActive — which re-binds only when watchCallbacks is non-empty, so plain getCurrentPosition() users never recover.
Steps to reproduce
- iOS device, app using
@capacitor/geolocation 8.2.0, location permission granted.
- Trigger one transient CoreLocation failure (easiest: cold GPS start indoors with
enableHighAccuracy: true and a short-ish timeout; first call after boot/standby often does it) → call rejects with OS-PLUG-GLOC-0002.
- Move somewhere with good GPS, call
getCurrentPosition() repeatedly.
- Every call now rejects with
OS-PLUG-GLOC-0010 ("Could not obtain location in time") regardless of signal, until the app is force-killed and reopened.
Expected behavior
- A transient
kCLErrorLocationUnknown should not permanently break location delivery — either filter it out (keep waiting, per Apple docs) or re-establish the subscription on the next request (e.g. reset locationInitialized in the .catch, or model errors as values so the stream never terminates).
Real-world impact
Field app for construction workers: mandatory-GPS clock-in. Workers with the app open since morning got "no GPS" on every attempt for minutes and only succeeded after force-killing the app. We shipped a workaround (cap the plugin timeout, then fall back to WKWebView navigator.geolocation, which works fine in these "broken" sessions since it bypasses the plugin), but the plugin itself stays unusable after the first transient error.
Plugin version
@capacitor/geolocation: 8.2.0 (also affects 7.x by code inspection — same architecture since the IONGeolocationLib rewrite)
- Capacitor: 8.4.1
- iOS (WKWebView,
server.url remote)
Suggested fix
In bindLocationPublisher()'s .catch, reset locationInitialized = false (and drop locationCancellable) so the next request re-binds; or better, filter transient kCLErrorLocationUnknown in IONGLOCManagerWrapper.didFailWithError instead of nil-ing currentLocation, so the publisher never fails on a recoverable condition.
Bug description
On iOS, after one transient CoreLocation failure, every subsequent
Geolocation.getCurrentPosition()call in the app session fails withOS-PLUG-GLOC-0010(timeout) — even when the device gets a GPS fix — until the app process is killed and relaunched.Root cause (traced through the plugin + IONGeolocationLib source)
CLLocationManager.requestLocation()frequently fails with the transientkCLErrorLocationUnknownon a cold start / indoors withenableHighAccuracy: true. Apple's docs say this error should be ignored while the manager keeps trying.IONGLOCManagerWrapper(ion-ios-geolocation),locationManager(_:didFailWithError:)setscurrentLocation = nil, and thecurrentLocationPublisherpipeline turns that into a failure (tryMapthrowsIONGLOCLocationError.locationUnavailable).GeolocationPlugin.swift→bindLocationPublisher(), that error is handled with:Empty()completes the stream, so the Combine subscription (locationCancellable) is permanently finished.locationInitializedis never reset, so the guard inbindLocationPublisher()(guard !locationInitialized else { return }) prevents any re-subscription for the rest of the process lifetime.getCurrentPosition()registers its callback andrequestSingleLocation()runs —didUpdateLocationseven fires with a valid fix — but there is no subscriber left to deliver it, so the only thing that ever resolves the call is the timeout timer (locationTimeoutPublisherhas its own, still-alive subscription).The only recovery paths are relaunching the app, or
appDidBecomeActive— which re-binds only whenwatchCallbacksis non-empty, so plaingetCurrentPosition()users never recover.Steps to reproduce
@capacitor/geolocation8.2.0, location permission granted.enableHighAccuracy: trueand a short-ish timeout; first call after boot/standby often does it) → call rejects withOS-PLUG-GLOC-0002.getCurrentPosition()repeatedly.OS-PLUG-GLOC-0010("Could not obtain location in time") regardless of signal, until the app is force-killed and reopened.Expected behavior
kCLErrorLocationUnknownshould not permanently break location delivery — either filter it out (keep waiting, per Apple docs) or re-establish the subscription on the next request (e.g. resetlocationInitializedin the.catch, or model errors as values so the stream never terminates).Real-world impact
Field app for construction workers: mandatory-GPS clock-in. Workers with the app open since morning got "no GPS" on every attempt for minutes and only succeeded after force-killing the app. We shipped a workaround (cap the plugin timeout, then fall back to WKWebView
navigator.geolocation, which works fine in these "broken" sessions since it bypasses the plugin), but the plugin itself stays unusable after the first transient error.Plugin version
@capacitor/geolocation: 8.2.0 (also affects 7.x by code inspection — same architecture since the IONGeolocationLib rewrite)server.urlremote)Suggested fix
In
bindLocationPublisher()'s.catch, resetlocationInitialized = false(and droplocationCancellable) so the next request re-binds; or better, filter transientkCLErrorLocationUnknowninIONGLOCManagerWrapper.didFailWithErrorinstead of nil-ingcurrentLocation, so the publisher never fails on a recoverable condition.