Andromida/SWIFTDATA_CLOUDKIT_SYNC_REQUIREMENTS.md

89 lines
4.4 KiB
Markdown

# SwiftData to CloudKit Sync Requirements (Reusable)
Primary source of truth lives in Bedrock:
`/Users/mattbruce/Documents/Projects/iPhone/Andromida/Bedrock/Sources/Bedrock/Storage/SWIFTDATA_CLOUDKIT_SETUP_GUIDE.md`
This Andromida copy is app-facing reference material and should stay aligned with the Bedrock guide.
Use this checklist for any iOS app that uses SwiftData with CloudKit and requires near-real-time multi-device sync.
## 1) Capabilities and Entitlements
- Enable `iCloud` with `CloudKit` for the app target.
- Enable `Push Notifications` for the app target.
- Enable `Background Modes > Remote notifications`.
- Add App Group if app + widget share local SQLite.
Required entitlement keys:
- `com.apple.developer.icloud-container-identifiers`
- `com.apple.developer.icloud-services` including `CloudKit`
- `com.apple.developer.ubiquity-kvstore-identifier` (if KVS is used)
- `aps-environment` (must resolve per config)
- `com.apple.security.application-groups` (if widget/app group storage is used)
## 2) Build Configuration
Use xcconfig variables so environments are explicit and portable:
- Debug: `APS_ENVIRONMENT = development`
- Release: `APS_ENVIRONMENT = production`
Entitlements should reference variables, not hard-coded values, where possible.
## 3) Model and Schema Constraints (SwiftData + CloudKit)
- Avoid `@Attribute(.unique)` in CloudKit-mirrored models.
- Ensure all stored properties have defaults or are optional.
- Keep relationships optional for CloudKit compatibility.
- Use additive schema evolution only (add fields/models; do not remove/rename/change types in place).
## 4) Runtime Sync Behavior
- Prefer Bedrock `SwiftDataCloudKitSyncManager` as the reusable remote observer/lifecycle component.
- Prefer Bedrock `SwiftDataStore` + `SwiftDataCloudKitStore` to avoid app-specific pulse boilerplate.
- Observe `.NSPersistentStoreRemoteChange` to detect remote merges.
- On remote change, call Bedrock `processObservedRemoteChange(modelContext:modelContainer:)` before refetch.
- Rebuild long-lived contexts only when safe (`hasChanges == false`) to avoid dropping unsaved local edits.
- Implement protocol reload hook (`reloadData`) to run your store-specific fetch step.
- Keep iOS-on-Mac pulsing loop in the root scene lifecycle (`active` only, cancel on `background`).
- Keep a foreground fallback refresh as a safety net; gate it with Bedrock `hasReceivedRemoteChange(since:)`.
- Emit structured logs for remote sync events (event count + timestamp) for debugging.
## 5) UI Freshness Requirements
- UI must re-render on each remote merge, even for batched updates.
- Keep an observable refresh version/counter and increment on each successful reload.
- Ensure list/detail views do not rely on stale assumptions when models are updated remotely.
- Make high-frequency interaction rows fully tappable to reduce missed user actions.
## 6) Verification Matrix
Test all cases on two physical devices with the same Apple ID and same app flavor:
1. Single toggle on Device A appears on Device B while both apps are open.
2. Rapid batch toggles on Device A all appear on Device B without manual pull-to-refresh.
3. Device B in background receives updates after foregrounding (without force quit).
4. Airplane mode recovery syncs correctly after reconnection.
5. Simultaneous edits resolve predictably (CloudKit last-writer-wins).
## 7) Observability and Console Checks
- Device logs: filter by sync logger category (for example `CloudKitSync`).
- CloudKit Console: validate record updates in the app container private database.
- If pushes are delivered but UI is stale, investigate context freshness and view invalidation, not transport.
- If APNs is unreliable on Mac runtime, validate that pulse logs appear every interval while active.
## 8) Reuse Checklist for New Apps
Before shipping any new SwiftData+CloudKit app:
- [ ] Capabilities: iCloud/CloudKit + Push + Remote Notifications are enabled
- [ ] Entitlements include `aps-environment` and correct container IDs
- [ ] xcconfig defines `APS_ENVIRONMENT` per configuration
- [ ] Remote change observer uses Bedrock `processObservedRemoteChange(...)` + reloads data
- [ ] Store conforms to Bedrock `SwiftDataCloudKitStore`
- [ ] Foreground fallback is gated by Bedrock `hasReceivedRemoteChange(since:)`
- [ ] UI has deterministic invalidation on remote reload
- [ ] Two-device batch-update test passes without manual refresh
- [ ] CloudKit Console verification documented in README/PRD