You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
add an adaptive two-column iPad and Mac Catalyst shell while preserving the iPhone tab experience and per-section navigation state
introduce platform-aware recording participation: new iPads opt in, upgraded iPads preserve their prior behavior, and Catalyst remains management-only
add an explicit “Join Existing iCloud Data” path without seeding data or enabling local recording
add a shared App Group presentation contract powering a Mac summary widget and an opt-in native menu-bar login item, and enable the share extension on Catalyst
make external SwiftData and CloudKit writes refresh glance surfaces reliably through history classification, coalesced publication, and a publisher shared with App Intents
add Catalyst packaging, entitlements, Hardened Runtime, CI verification, architecture rules, documentation, and regression coverage
Why
PR #160 establishes synchronized per-device recording controls. This follow-up uses that model to make iPad a first-class viewer and manager and to add a Mac Catalyst management experience without turning the Mac into another location-recording device.
User impact
iPhone remains tab-based and recording-enabled by default.
New iPad installs begin with automatic recording off; upgraded installs retain their existing policy.
Mac can browse and manage Where data, use the share extension, view today’s regions and year-to-date day counts, and optionally enable a native menu-bar glance.
“Join Existing iCloud Data” opens the real synced store without demo seeding or local recording.
Reliability
local writers are stamped so SwiftData persistent history can distinguish local saves from external CloudKit and share-extension imports
widget publication coalesces concurrent requests, retries failed writes, and shares one publisher between the app and App Intents
the menu-bar helper reads an atomic presentation artifact from the App Group and preserves last-good data with freshness and error state
Mac executables use Hardened Runtime, and CI verifies that the helper, widget, and share extension are embedded with valid helper metadata
unsigned Where-Catalyst build — passed, including embedded-binary validation for the helper, widget, and share extension
swift run bumper config .
swift run bumper test . — 16 tests passed
swift run bumper lint . --timings
./swiftformat --lint
./xcstrings --lint
./attribution --check
workflow YAML parse and git diff --check
A signed installed-app smoke test is still valuable for App Group access, CloudKit imports, SMAppService registration, widget discovery, and the where:// launch path.
Architecture note: keep WhereSurface as the published read model
I considered replacing the shared artifact with direct store access from the widget and native menu-bar processes. The conclusion is to keep the current boundary:
The Catalyst app already uses the real WhereServices / SwiftData store.
Widgets consume the broader WidgetSnapshot (all relevant day regions, totals, and appearances), so their near-term flexibility is not limited by the narrower WhereSurfaceSnapshot.
WhereSurface primarily lets the long-lived login helper remain a small, Foundation-only glance process that is independent of SwiftData schema loading, migrations, onboarding/scope ownership, RegionKit, and CloudKit.
Direct shared-store access is technically possible, but every secondary process would need one injected, read-only .localOnly container. The app must remain the only CloudKit synchronizer: Apple explicitly warns that an app and extension must not load the same store through multiple CloudKit-synchronizing containers, even across processes (TN3164).
For widgets, the published snapshot keeps timeline work bounded and follows Apple's recommendation that the containing app prepare shared widget data in advance (WidgetKit strategy).
If the menu-bar item later becomes an interactive history browser, a hybrid is worth revisiting: keep widgets snapshot-based, and give the helper a narrow read-only store API. For the current glance UI, the small additive wire contract buys more lifecycle and compatibility safety than direct access buys flexibility.
One follow-up did fall out of this review: atomic replacement prevents partial JSON, but Apple requires file coordination for custom files accessed across App Group processes. This PR will therefore coordinate both the shared JSON writer and readers with NSFileCoordinator (shared-file guidance).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Why
PR #160 establishes synchronized per-device recording controls. This follow-up uses that model to make iPad a first-class viewer and manager and to add a Mac Catalyst management experience without turning the Mac into another location-recording device.
User impact
Reliability
Validation
./test --all --no-generate— 1,587 tests passedWhere-Catalystbuild — passed, including embedded-binary validation for the helper, widget, and share extensionswift run bumper config .swift run bumper test .— 16 tests passedswift run bumper lint . --timings./swiftformat --lint./xcstrings --lint./attribution --checkgit diff --checkA signed installed-app smoke test is still valuable for App Group access, CloudKit imports,
SMAppServiceregistration, widget discovery, and thewhere://launch path.Stack
Depends on #160.