Watch observability
Use getActiveWatches() to inspect the watches that currently own
native position or heading subscriptions:
getActiveWatches() is a synchronous, point-in-time snapshot. It does not
request permission, start a provider, retain callbacks, or subscribe to future
changes. Entries are sorted by token and contain:
token: the value accepted byunwatch().kind:positionforwatchPosition()orheadingforwatchHeading().
The snapshot covers the main package import. It does not include Compat API
watches, one-shot position or heading requests, or Background Location.
Android watches that finish because of maxUpdates disappear from the next
snapshot automatically. Web snapshots include active browser position watches;
unsupported web heading requests are not reported as active.
Cleanup semantics
unwatch(token)removes a matching position or heading watch. Repeating it, or passing an unknown token, is safe and has no effect.stopObserving()removes every position and heading watch, including development-tool and browser watches. It does not cancel one-shot position requests or Background Location. On iOS it also leaves one-shot heading requests running. On Android, the current native heading manager discards pending one-shot heading requests and their timeouts without invoking a callback; avoid callingstopObserving()whilegetHeading()is pending.useWatchPosition()owns its token and callsunwatch()during React effect cleanup. Low-levelwatchPosition()callers own cleanup themselves.- On Android, adding or removing a position watch restarts the one shared native
position request when another watch remains. On iOS, it reconfigures the
shared
CLLocationManager; it restarts only when the selected Core Location mode changes.
Watch Manager v2 default
Multiple position watches share native resources. Options are merged to serve the most demanding active acquisition, while callback delivery is evaluated against each subscription's own thresholds. A one-shot request or a second watch may make native acquisition more demanding, but cannot lower an existing watch's delivery threshold or move its last-delivered baseline.
Android
Active position watches share one Fused Location or platform request. The native request uses:
- the most demanding accuracy;
- the smallest
interval,fastestInterval,distanceFilter,maxUpdateAge, andmaxUpdateDelayvalues; waitForAccurateLocationwhen any watch requests it;- coarse granularity when any watch explicitly requires coarse, otherwise fine when any watch explicitly requires fine, otherwise permission granularity.
maxUpdates remains per subscription. Reaching the limit removes only that
watch, then stops or restarts the shared request as needed. For callback
delivery, the first native position establishes each watch's baseline. Later
positions must satisfy that watch's own interval and distanceFilter, both
measured from its last delivered position; suppressed native updates do not move
the baseline or count toward maxUpdates. fastestInterval still contributes
to the shared native request, while interval is the per-watch minimum callback
period. Position watch registration, removal, native restart decisions, and
callback delivery are serialized on Android's main looper. unwatch() prevents
delivery from subsequent native updates, but a callback already handed to the
asynchronous JS bridge may still finish. Heading watches use the Android sensor
manager separately and apply each subscription's headingFilter independently.
iOS
Position watches and pending one-shot position requests share one
CLLocationManager. The manager uses:
- the most precise requested accuracy and smallest distance filter;
- the highest-ranked activity type used by the current implementation;
- automatic pausing only when every explicit preference allows it;
- the background indicator or significant-change mode when any consumer asks for it.
Changing significant-change mode stops and restarts Core Location. Other
position option changes reconfigure the existing manager. Each position watch
receives its first native position, then applies its own distanceFilter from
the last position delivered to that watch. A nearby update accepted for a more
demanding watch or one-shot request no longer leaks into a less frequent watch.
Core Location does not expose the Android interval option. Heading watches
share the same manager's heading sensor separately and apply each subscription's
headingFilter independently.
Web
Each position watch maps to its own navigator.geolocation.watchPosition()
call. Browser watches are not merged. The package applies each watch's
distanceFilter in JavaScript.
Migration note
The v1.x native managers delivered every shared native position to every watch,
so adding a more demanding watch or one-shot request could increase callbacks
for existing consumers. In v2, code that intentionally relied on that leakage
must lower the affected watch's own interval or distanceFilter. Continue to
call unwatch(token) for component-local cleanup and reserve stopObserving()
for session-wide cleanup.
