Offline-first is more than caching the last response. The local store supports real user work while the network is unavailable, and synchronization later reconciles that work.
SwiftData models need stable identities, update timestamps, ownership, and deletion representation where remote synchronization requires it.
@Model
final class WorkoutDraft {
@Attribute(.unique) var id: UUID
var updatedAt: Date
var syncStateRaw: String
var deletedAt: Date?
}
Feature code should depend on a client, not ModelContext. That client can provide cached reads, local writes, and explicit refresh operations.
Define freshness honestly. A screen may show saved data immediately with a “last updated” state while refreshing. Empty local data and failed remote refresh should not be indistinguishable.
Deleting a synced record often requires a tombstone until the server acknowledges it. Physically removing it immediately can cause another device's old copy to return.
Conflict rules must be per domain. A user-edited title may use last-write-wins. Additive health events should deduplicate by semantic identity. Server-computed scores may always accept the newest algorithm version.
Test launches without network, local edits during a failed sync, repeated pulls, tombstone propagation, and reinstall recovery. SwiftData supplies persistence; offline-first behavior comes from the synchronization contract around it.