Andromida/SWIFTDATA_CLOUDKIT_SYNC_REQUIREMENTS.md

4.4 KiB

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