Skip to content

fix(Where): refresh region welcomes on foreground activation - #314

Merged
kyleve merged 11 commits into
mainfrom
codex/app-wide-region-welcome
Sep 17, 2026
Merged

kyleve merged 11 commits into
mainfrom
codex/app-wide-region-welcome

Conversation

@kyleve

@kyleve kyleve commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Posted by an AI agent on kve's behalf.

Summary

  • User-facing: Resolve the current region on every logged-in foreground activation, regardless of the selected tab, and present the welcome card over the visible tab.
  • User-facing: On iOS 26.1 and later, show a delayed tab-bar status while acquiring a fix. Keep denied or restricted access and disabled Precise Location actionable through Settings; preserve the failure reason at accessibility text sizes and provide a full-width 44-point action target. Let transient and confidence failures disappear quietly.
  • User-facing: Use the dynamically enabled system accessory on iOS 26.1 and later. iOS 26.0 still performs the lookup and presents the welcome card, but no longer shows acquisition or recovery status.
  • Internal: Replace optional one-shot fixes with typed, nonthrowing acquisition and resolution outcomes. Require a fix no more than 60 seconds old, at or below 1 km horizontal uncertainty, and confidently inside one tracked region boundary.
  • Internal: Coalesce concurrent one-shot callers while isolating cancellation, retain valid coarse passive evidence, reject negative accuracy, and record only bounded reason/age/accuracy telemetry. System-facing one-shot controls use a production driver and a conforming test fake; the coordinator has a single idle/pending request state. The one-shot fix targets 100 m accuracy while the live decision retains its 1 km cap.
  • Build: Pin snapshot rendering to Xcode build 27A266a, matching the current CircleCI Xcode 27.0 image.

Why

The welcome lookup was owned by the Locations tab and keyed to that view's visibility. A retained tab hierarchy could therefore return from a long trip without triggering a new fix, while passive Core Location callbacks could be too stale or uncertain for an immediate region decision.

Review focus

  • The strict boundary rule rejects a fix when its uncertainty radius reaches the attributed region's boundary.
  • A canceled caller leaves other coalesced one-shot callers running; the underlying request stops only after the last waiter cancels or the bounded request times out.
  • MainTabs owns the state machine and keys resolution to active-scene entry plus the welcome preference, independent of tab selection.
  • The accessory uses SwiftUI's enabled-state API on iOS 26.1 and later. The iOS 26.0 compatibility inset has been removed without changing the deployment target. An undismissed welcome is revalidated on the next foreground activation and cleared if that lookup fails.

Compatibility

No persistence or backup migration is required. Passive Visits and significant-change monitoring remain unchanged, and valid passive fixes above 1 km remain available as historical evidence. Automatic-recording authorization remains a prerequisite for the welcome.

Backlog reconciliation

Moves the foreground-refresh item from Where/TODOs.md to Completed issues with the implemented lifecycle and confidence behavior.

Testing

  • ./test --no-generate --no-build WhereCoreTests WhereUITests — 1,109 tests passed after the review fixes.
  • ./test --no-generate --no-build --snapshots --only WhereUISnapshotTests/LocationsViewSnapshotTests — passed.
  • ./test --snapshots --no-generate --no-build --only 'WhereUISnapshotTests/RootViewSnapshotTests/root()' --only 'WhereUISnapshotTests/MainTabsSnapshotTests/mainTabs()' --review — 20 app-shell configurations passed together on the final no-record run.
  • ./test --no-generate --only 'WhereCoreTests/CoreLocationSourceTests' — 10 tests passed with the final source.
  • ./test --snapshots --review — all 50 snapshot tests passed on Xcode 27A266a without reference changes.
  • ./test --all — 2,029 iOS unit tests passed on Xcode 27A266a.
  • ./test WhereUITests — 501 tests passed after removing the iOS 26.0 fallback.
  • ./test --snapshots --only 'WhereUISnapshotTests/MainTabsSnapshotTests/mainTabs()' --review — passed after removing the fallback, without reference changes.
  • python3 -m unittest discover -s Tools/Tests -p 'test_*.py' — 64 tests passed.
  • python3 .circleci/Tests/test_test_artifacts.py — 7 tests passed.
  • ./swiftformat --lint
  • ./sf-symbols --lint
  • ./xcstrings --lint
  • git diff --check

Enable the system tab accessory only while live-region status is visible, so idle and welcome states do not reserve an empty bar. Disable welcome lookup in unrelated root fixtures and give large-text calendar snapshots time to reach their final scroll position.

/// How long to wait for a one-shot fix before giving up and recording no
/// captured location. Kept short so a manual entry's Save isn't held up.
#if DEBUG

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Hmm, I dont like this; can we put this behind a protocol with a no-op in production or something? Or is this a better pattern?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Posted by an AI agent on kve's behalf.

Addressed in d6ee7fb9. I replaced the optional test overrides with CurrentLocationRequestDriving: the production driver still calls CLLocationManager, and the CoreLocationSource tests use a conforming fake. This keeps the test seam out of the request coordinator without making production a no-op.

manager = CLLocationManager()
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyKilometer

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Thinking about this more, we may want a desired accuracy of a lower amount for when you're near a border frequently (eg, NY vs NJ).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Posted by an AI agent on kve's behalf.

Addressed in 171c37bd. The foreground one-shot now requests 100 m accuracy to improve confidence near region borders. The 1 km hard acceptance cap and 10-second timeout are unchanged; Core Location may still return a coarser fix, which the existing confidence gate handles.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This is getting pretty complicated, worth a TLA proof and/or a real state machine maybe?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Posted by an AI agent on kve's behalf.

Addressed in 2146baf7. The one-shot coordinator now has a single idle/pending state that owns its waiters and timeout, with a request ID guarding stale timeout completion. I added cancellation, timeout/retry, and synchronous-callback tests. I used the explicit state machine rather than a TLA model for this bounded lifecycle.

CircleCI's Xcode 27.0 image and the local installation now use build 27A266a. The previous beta-build pin rejected the runner before the iOS build started. All 50 snapshot suites and 2,029 iOS unit tests pass on the new build without reference changes.
Keep the native dynamically enabled tab accessory on iOS 26.1 and later. iOS 26.0 continues resolving and presenting welcomes without an acquisition-status accessory.

Validated: ./test WhereUITests (501 passed); ./test --snapshots --only WhereUISnapshotTests/MainTabsSnapshotTests/mainTabs() --review (passed); ./swiftformat --lint; git diff --check.
@kyleve
kyleve enabled auto-merge (squash) September 17, 2026 18:09
@kyleve
kyleve merged commit 537fadc into main Sep 17, 2026
8 checks passed
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.

1 participant