📝 SwiftData — 4/30 Days ModelContainer & ModelContext (Lifecycle & Concurrency)
📝 SwiftData — ModelContainer & ModelContext (Lifecycle & Concurrency)
📦 ModelContainer
A container that manages your model schema and storage (backed by SQLite by default)
Created once (usually in
Appstruct)Can have multiple
ModelContexts`let container = try ModelContainer(for: [User.self, Task.self])
🧠 ModelContextRepresents a scratch space for reading/writing model instances
Think of it as an in-memory workspace
Must call
try context.save()to persist changes to disk
Creating Contexts
Main UI context (automatically injected in SwiftUI via
@Environment(\.modelContext))Background context for heavy work
@Environment(\.modelContext) var context // UI contextTask.detached {let backgroundContext = ModelContext(container)let user = User(name: "Async User")backgroundContext.insert(user)try? backgroundContext.save()}
1. Multiple Contexts
A
ModelContainercan have multiple concurrentModelContexts`Useful for parallel work or background tasks
2. Thread Safety
ModelContextis not thread-safeUse each context only on the actor it was created on
Always create a separate background context when working off the main actor
3. Merging Changes
Contexts are isolated
When one saves, others get notified and must refresh
Use:
try context.fetch(FetchDescriptor<User>())context.refreshAllObjects()
Similar to Core Data’s merge behavior
4. Conflict ResolutionIf two contexts edit the same object → last writer wins
To avoid data loss:
Refresh objects before saving
Use background contexts for isolated work
Consider versioning if needed
5. Background Work
Use background contexts to do large inserts/updates without blocking UI
Example:Task.detached { let ctx = ModelContext(container) for i in 0..<1000 { ctx.insert(User(name: "User \(i)")) } try? ctx.save()}
6. Persistence & Crashes
Unsaved changes are lost if the app terminates/crashes
Always call
save()after important changes
7. Multiple Containers
You can create multiple
ModelContainers in one appThey are completely isolated (separate schemas and storage)
()) context.refreshAllObjects() Similar to Core Data’s merge behavior 4. Conflict Resolution If two contexts edit the same object → last writer wins To avoid data loss: Refresh objects before saving Use background contexts for isolated work Consider versioning if needed 5. Background Work Use background contexts to do large inserts/updates without blocking UI Example: Task.detached { let ctx = ModelContext(container) for i in 0..<1000 { ctx.insert(User(name: "User \(i)")) } try? ctx.save() } 6. Persistence & Crashes Unsaved changes are lost if the app terminates/crashes Always call save() after important changes 7. Multiple Containers You can create multiple ModelContainers in one app They are completely isolated (separate schemas and storage) 💡 Best Practices Always save() on critical points (onDisappear, backgrounding) Refresh contexts after background saves Use @MainActor for UI-bound contexts Use background contexts for long operations Keep ModelContainer as a singleton-like shared resource

