Skip to content

geolocator_watchos 0.2.0: accuracy, service status, workout settings - #12

Merged
MAUstaoglu merged 3 commits into
flutterwatch:mainfrom
iamoguzhanarslantas:geolocator-watchos-improvements
Oct 24, 2026
Merged

MAUstaoglu merged 3 commits into
flutterwatch:mainfrom
iamoguzhanarslantas:geolocator-watchos-improvements

Conversation

@iamoguzhanarslantas

Copy link
Copy Markdown
Contributor

Summary

geolocator_watchos 0.1.2: three members that were "Not supported on watchOS", AppleSettings reaching CoreLocation, and two fixes to how streams and single fixes share the location manager.

Not verified on a physical Apple Watch. Everything below was checked on the watchOS 27.0 Simulator and on the host only. I could not get a watch to show up in Xcode, so no device run has been done.

Added

  • getLocationAccuracy reports accuracyAuthorization: precise or reduced.
  • requestTemporaryFullAccuracy asks with a purpose key and waits for the answer. Without NSLocationTemporaryUsageDescriptionDictionary it throws PermissionDefinitionsNotFoundException, as geolocator does on iOS.
  • getServiceStatusStream starts with the current status, then reports each change CoreLocation signals.
  • AppleSettings passed to getPositionStream or getCurrentPosition now reach CoreLocation, read through toJson() so the package does not depend on geolocator_apple: activityType (for example ActivityType.fitness for a run) and allowBackgroundLocationUpdates.
  • Three FFI exports: geolocator_watchos_accuracy_authorization, _request_temporary_full_accuracy and _accuracy_requests_done. geolocator_watchos_start_updates takes the activity type and the background flag.

Fixed

  • getCurrentPosition no longer stops a position stream that is running, and cancelling one of two streams no longer stops the other. They share the one CLLocationManager, which stops when the last of them ends; a call during a stream waits for the stream's next fix without changing its settings.
  • A cached fix more than 5 seconds older than the request is no longer returned by getCurrentPosition, nor emitted first by a new stream: CoreLocation hands over its last fix first, however old.

Background flag

allowsBackgroundLocationUpdates turns on only when the Info.plist's UIBackgroundModes has location. CoreLocation asserts and ends the app otherwise, and it reads only UIBackgroundModes on watchOS too: with location under WKBackgroundModes the app ended on the Simulator, and with UIBackgroundModes it runs. So WKBackgroundModes does not count.

Verified

On the watchOS 27.0 Simulator (Apple Watch Series 12, 46 mm):

  • The background flag with each Info.plist key, as above.
  • getLocationAccuracy and getServiceStatusStream answer.
  • The example returns the simulated fix after the permission prompt.
  • The example builds with every ffiSymbols entry in the export table.

Off the watch:

  • 25 host tests (14 new): accuracy, the service-status stream, the AppleSettings fields, the activity-type mapping, the shared manager with two streams and a single fix during a stream, and the stale cached fix.
  • Every tool/ check, dart format as CI runs it, and pub publish --dry-run are clean.

Not verified

  • A physical Apple Watch. Nothing has run on a device.
  • The temporary full-accuracy prompt: it has not been seen asking.
  • Background updates with the wrist down, and what activityType changes in real GPS fixes. The Simulator's fixes are simulated.

Notes for review

  • Two commits: the 0.1.2 work, and the review fixes (the UIBackgroundModes-only check, the shared manager, the stale fix).
  • The README moves the three members out of "Not supported on watchOS" and adds a section on workouts and background updates.

🤖 Generated with Claude Code

iamoguzhanarslantas and others added 2 commits October 19, 2026 14:02
getLocationAccuracy reports accuracyAuthorization. requestTemporaryFullAccuracy
asks with a purpose key and waits for the answer; without
NSLocationTemporaryUsageDescriptionDictionary it throws
PermissionDefinitionsNotFoundException, as geolocator does on iOS.
getServiceStatusStream starts with the current status, then each change
CoreLocation signals.

AppleSettings now reach CoreLocation, read through toJson() so the package
does not depend on geolocator_apple: activityType (ActivityType.fitness for a
run) and allowBackgroundLocationUpdates, which turns on only when the
Info.plist has the location background mode, since CoreLocation treats it as
a fatal error otherwise.

Checked: 21 host tests (10 new), a Simulator build with every ffiSymbols
entry exported, and the example on the watchOS 27.0 Simulator returning the
simulated fix after the permission prompt. The temporary-accuracy prompt and
background updates are not yet checked on a physical watch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ared manager

Review fixes on top of the 0.1.2 work, still unreleased:

* allowsBackgroundLocationUpdates turns on only when UIBackgroundModes has
  "location". The check also accepted WKBackgroundModes, but CoreLocation
  reads only UIBackgroundModes on watchOS too: with "location" under
  WKBackgroundModes, setting the flag asserted and ended the app (seen on
  the watchOS 27.0 Simulator; with UIBackgroundModes it runs).
* getCurrentPosition no longer stops a position stream that is running, and
  cancelling one of two streams no longer stops the other: they share the
  one CLLocationManager, which stops when the last of them ends, and a call
  during a stream waits for the stream's next fix.
* A cached fix more than 5 seconds older than the request is not returned by
  getCurrentPosition, nor emitted first by a new stream.

Checked: 25 host tests (4 new); on the Simulator, the background flag with
each plist key, getLocationAccuracy and getServiceStatusStream, and the
example's fix after the change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mehmet-nettle

This comment was marked as duplicate.

@MAUstaoglu MAUstaoglu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for this, @iamoguzhanarslantas. The new tests are good, and the watchOS availability work is careful. A few things need to change before we can merge.

Must-fix

  1. watchos/Classes/geolocator_watchos_ffi.m:150: CLActivityTypeMaritime is declared only in the watchOS 27 SDK, so the plugin no longer compiles with Xcode 26, which flutter-watchos still supports. Please guard it with #if __WATCH_OS_VERSION_MAX_ALLOWED >= 270000. At :152, fall back to CLActivityTypeOtherNavigation, as geolocator_apple does, rather than Other.

  2. lib/geolocator_watchos.dart:218-222, 260: one shared location manager serves both single fixes and streams.

    • A getCurrentPosition during a distance-filtered stream times out after 30 s if the wearer stands still.
    • A second stream overwrites the first stream's accuracy and background settings.

    Please mirror geolocator_apple: a separate one-time manager for single fixes, and reuse of the running stream. Add tests for both cases.

  3. lib/geolocator_watchos.dart:250: the 5 s staleness filter also applies to streams. Upstream filters only single fixes (GeolocationHandler.m:225), and reduced-accuracy fixes can legitimately be much older.

  4. README.md:81 links flutter_watch_workout, which doesn't exist in this repo or on pub.dev.

Nice-to-have

  • .m:167: an activity type of 0 keeps the previous value. Please reset it to Other.
  • lib:350 calls the blocking +locationServicesEnabled on every fix. Let the callback carry the kind instead (AUTHORING §2b).
  • The public GeolocatorWatchosBackend changed shape, so this should be 0.2.0 with a Breaking CHANGELOG entry.
  • The activity-type test duplicates the mapping table. Consider testing the real AppleSettings through a geolocator_apple dev_dependency.
  • README:
    • mention that allowBackgroundLocationUpdates defaults to true;
    • point to App Review guideline 2.5.4 for background location;
    • say that the temporary full-accuracy prompt and wrist-down updates aren't verified on a device yet.

Note: our CI doesn't compile the .m files yet, so please build the example with both Xcode 26 and 27. We'll start the CI run once you push the update. Thanks again!

@MAUstaoglu
MAUstaoglu dismissed mehmet-nettle’s stale review October 23, 2026 20:47

Posted from the wrong account; superseded by the maintainer review.

From the review of PR flutterwatch#12:

* CLActivityTypeMaritime is declared only from the watchOS 27 SDK on, so
  its use is behind #if __WATCH_OS_VERSION_MAX_ALLOWED >= 270000, and the
  fallback is CLActivityTypeOtherNavigation, as in geolocator_apple. The
  package builds with Xcode 26 again.
* getCurrentPosition has a CLLocationManager of its own, as in
  geolocator_apple: native starts it at the call's accuracy, skips cached
  fixes more than 5 seconds old, and stops it after the first fix it
  accepts. A call during a distance-filtered stream no longer times out
  while the wearer stands still, nor changes the stream's settings.
* getPositionStream called while a stream runs returns that stream, with
  the settings it started with, as geolocator_apple does; a second stream
  overwrote the first one's accuracy and background settings.
* The 5-second age filter applies only to single fixes, as upstream: a
  stream emits every fix native delivers, counted by a new
  geolocator_watchos_position_count, so a fix at the same place is one too.
* A stream without AppleSettings gets CLActivityTypeOther instead of the
  previous stream's type.
* The native callback carries a kind (position, authorization, accuracy),
  so the service status stream asks the blocking locationServicesEnabled
  only on an authorization change, not on every fix.
* 0.2.0 with a Breaking section: GeolocatorWatchosBackend, which is
  public, changed shape.
* The host tests pass geolocator_apple's real AppleSettings, a dev
  dependency, instead of a copy of its toJson().
* README: no link to flutter_watch_workout, which is not in this repo;
  AppleSettings turns background updates on by default; App Review
  Guideline 2.5.4; what is not yet checked on a physical watch.

Checked: 26 host tests; the example built for the watchOS 27.0 Simulator
with all 13 ffiSymbols exported, and on the Simulator getCurrentPosition
answered a simulated location, then a moved one. The .m compiles with the
SDK ceiling set to 26 and to 27; it was not built with Xcode 26 itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iamoguzhanarslantas

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Pushed 2536ff2, now 0.2.0:

Must-fix

  1. CLActivityTypeMaritime is behind #if __WATCH_OS_VERSION_MAX_ALLOWED >= 270000, with CLActivityTypeOtherNavigation as the fallback. I only have Xcode 27.1 here. I compiled the .m against the watchOS 27 SDK with the ceiling forced to 260000 and to 270000, and both compile. A real Xcode 26 build is still needed.
  2. getCurrentPosition now has its own CLLocationManager, as in geolocator_apple. It skips fixes older than 5 s and stops after the first one it accepts. getPositionStream returns the running stream with its original settings. Tests cover both cases you listed, plus two concurrent single fixes.
  3. The 5 s filter is native and applies to the single-request manager only. Streams emit every delivered fix, counted by a new geolocator_watchos_position_count.
  4. The README no longer links flutter_watch_workout.

Nice-to-have

  • Activity type 0 resets to CLActivityTypeOther.
  • The callback carries a kind (position / authorization / accuracy). The service-status stream calls locationServicesEnabled only on authorization changes, and a test checks that a fix does not call it.
  • Version 0.2.0 with a Breaking section.
  • geolocator_apple is a dev_dependency, and the tests pass the real AppleSettings.
  • README: allowBackgroundLocationUpdates defaults to true, Guideline 2.5.4, and the temporary-accuracy prompt and wrist-down updates are listed as not verified on a device.

Checked: 26 host tests. The example builds for the watchOS 27.0 Simulator with all 13 ffiSymbols exported. On the Simulator, getCurrentPosition returned a simulated location, then the new one after it moved.

🤖 Generated with Claude Code

@MAUstaoglu MAUstaoglu changed the title geolocator_watchos 0.1.2: accuracy, service status, workout settings geolocator_watchos 0.2.0: accuracy, service status, workout settings Oct 24, 2026

@MAUstaoglu MAUstaoglu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @iamoguzhanarslantas. Every point is addressed. We syntax-checked the .m with Xcode 26.6 and 27.0, for device and simulator, and it compiles on both; the previous revision didn't on Xcode 26. Approving.

@MAUstaoglu
MAUstaoglu merged commit e861536 into flutterwatch:main Oct 24, 2026
2 checks passed
iamoguzhanarslantas pushed a commit to iamoguzhanarslantas/plugins that referenced this pull request Oct 26, 2026
CI ran on Ubuntu and never compiled packages/*/watchos. In flutterwatch#12 a symbol
declared only in the watchOS 27 SDK would have broken every Xcode 26 build
with CI green (flutterwatch#17).

tool/check_native_sources.sh compiles every tracked .m, .mm and .c the way
the CLI does (clang -target arm64-apple-watchos26.0[-simulator] -fobjc-arc
-fmodules -I<each header directory>), with -fsyntax-only, and type-checks
SwiftUI view sources with swiftc and a copy of the CLI's registration shim
(tool/native/plugin_views_shim.swift, CLI 0.1.1). An error or a warning
fails; a deprecation is counted, as in_app_purchase_watchos uses StoreKit 1
on purpose. The four firebase packages, whose Package.swift names a SwiftPM
package, are listed and skipped: only a SwiftPM build provides those modules.

The new `native` job runs it on macos-26 with Xcode 26.0.1 and 26.6, and on
xcode-27 with Xcode 27.0, then runs the check's own tests there. Its
fixtures cover the plan (--list, also on Ubuntu), an undeclared symbol and a
warning (fail), and a clean source with a deprecation and a view that uses
the shim (pass).

Locally, on plugins main, all 38 compiles are clean on Xcode 26.6 and 27.0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants