Architecture: Offline-First Architecture
Structure for apps where the local store is the source of truth and the network syncs in the background. See skills/storage/ios/offline_sync.md and the System Design Expert.
Overview
The UI reads and writes the local store. Mutations are queued in an outbox and synced when connectivity allows; remote changes are pulled as deltas and merged with a defined conflict policy.
Write Path (optimistic)
Read / Pull Sync
Building Blocks
- Local store as source of truth — UI never blocks on the network.
- Outbox — pending mutations with status (
pending/syncing/failed) + backoff retry. - Optimistic updates + rollback — apply locally; reconcile/rollback on server response.
- Conflict policy — explicit: last-write-wins (server timestamps), field-merge, or CRDTs.
- Stable client ids (UUID) — offline-created records reconcile cleanly.
- Delta sync via cursor/
updatedAt— fetch only what changed.
State Per Record
swift
enum SyncStatus: String { case pending, syncing, synced, failed }
struct Note: Identifiable { let id: UUID; var text: String; var syncStatus: SyncStatus }The UI can surface pending/failed (e.g. a small "not synced" indicator).
Trade-offs
- Strong UX and resilience, at the cost of conflict-handling complexity.
- Choose the simplest conflict strategy that fits the data; reserve CRDTs for true collaborative editing.