ionic-team / ionic-team/capacitor-geolocation
bug(ios): one transient CoreLocation error permanently kills location delivery — all later getCurrentPosition calls time out until app relaunch
- Dominant language
- Swift
- Stars
- 12
- Forks
- 19
- PR merge metrics
- No merged PRs in 30d
Description
### 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)
1. `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.
2. In `IONGLOCManagerWrapper` (ion-ios-geolocation), `locationManager(_:didFailWithError:)` sets `currentLocation = nil`, and the `currentLocationPublisher` pipeline turns that into a **failure** (`tryMap` throws `IONGLOCLocationError.locationUnavailable`).
3. In `GeolocationPlugin.swift` → `bindLocationPublisher()`, that error is handled with:
```swift
.catch { ... self?.callbackManager?.sendError(.positionUnavailable)
return Empty().eraseToAnyPublisher() }
```
`Empty()` **completes** the stream, so the Combine subscription (`locationCancellable`) is permanently finished.
4. `locationInitialized` is **never reset**, so the guard in `bindLocationPublisher()` (`guard !locationInitialized else { return }`) prevents any re-subscription for the rest of the process lifetime.
5. 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
1. iOS device, app using `@capacitor/geolocation` 8.2.0, location permission granted.
2. 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`.
3. Move somewhere with good GPS, call `getCurrentPosition()` repeatedly.
4. **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.
Contributor guide
Research direction
Start in GeolocationPlugin.swift at bindLocationPublisher() and trace locationCancellable, locationInitialized, and the .catch path. Then inspect IONGLOCManagerWrapper.didFailWithError and its currentLocationPublisher behavior. Confirm the failure, completion, and later valid-fix sequence on iOS; done means transient CoreLocation errors no longer prevent later getCurrentPosition calls from receiving a valid location.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100