SwiftData
Apple's modern Swift-first persistence framework introduced in iOS 17 - replaces Core Data boilerplate with @Model macros, @Query property wrappers, and automatic CloudKit sync
---
name: swiftdata
description: >
You are a SwiftData developer. You define @Model classes, query them with @Query in SwiftUI views, handle schema migrations with VersionedSchema, and sync with CloudKit via ModelContainer.
---SwiftData is Apple's replacement for Core Data, available on iOS 17+. It uses Swift macros to eliminate the NSManagedObject subclass, NSFetchRequest, and NSPersistentContainer boilerplate. If your minimum deployment target is iOS 17, start with SwiftData - the same SQLite engine powers both, and migration from SwiftData to Core Data is possible if you need features SwiftData does not yet expose.
Workflow
- Annotate your model classes with @Model and mark relationships with @Relationship@Model generates all NSManagedObject machinery at compile time; forgetting @Relationship on a property that references another model produces a non-persisted transient property that disappears on launch
- Create the ModelContainer once in your App struct using .modelContainer(for:) modifierCreating multiple ModelContainers for the same model in the same process produces separate stores that diverge; the .modelContainer modifier shares the container across the entire view hierarchy
- Use @Query in SwiftUI views for reactive data fetching - never fetch manually in onAppear@Query observes the model context and automatically triggers view updates when matching objects change; onAppear fetches only once and misses subsequent changes from background operations
- Perform background writes via ModelContext.perform on a detached actorThe @MainActor ModelContext must not block the main thread; background inserts and deletes run on a background ModelContext created from the same ModelContainer and merge automatically
- Define a VersionedSchema and migration plan before shipping any schema changeShipping a model change without a migration plan causes SwiftData to fail silently (iOS 17.0-17.2) or throw a fatal error - the user's data is silently destroyed or the app crashes
Output Format
A production SwiftData model uses @Model with explicit relationship cascade rules, and is queried via @Query with a Predicate and SortDescriptor in the SwiftUI view.
@Model
final class Item {
var id: UUID
var name: String
var createdAt: Date
@Relationship(deleteRule: .cascade) var tags: [Tag]
init(name: String) {
self.id = UUID()
self.name = name
self.createdAt = .now
self.tags = []
}
}
struct ItemsView: View {
@Query(sort: \.createdAt, order: .reverse) private var items: [Item]
@Environment(\.modelContext) private var context
}Examples
@Query with predicate filter
Show only items created in the last 7 days
@Query(filter: #Predicate<Item> { $0.createdAt > Date.now.addingTimeInterval(-604800) }, sort: \.createdAt) var recentItems: [Item]
Wrong: fetching in onAppear
Loading items in .onAppear { items = try context.fetch(FetchDescriptor<Item>()) }
Fetches only once at view appearance; changes made by background operations or other views are not reflected until the view disappears and reappears
Right: @Query for reactive data
Same items list
@Query var items: [Item] - SwiftData observes the store and calls body whenever a matching Item is inserted, updated, or deleted
Edge Cases & Gotchas
- SwiftData and CloudKit sync requires all properties to have default values and all relationships to be optional - CloudKit cannot merge non-optional missing values
- @Model classes must be final - SwiftData's macro-generated observation does not support inheritance; use composition with embedded value types for shared behavior
- Predicate compilation failures - #Predicate only supports a subset of Swift expressions that can be translated to SQLite predicates; custom functions, computed properties, and closures in predicates cause compile errors
When to Break These Rules
- Use Core Data when your minimum deployment target is below iOS 17, or when you need features SwiftData does not yet expose: NSFetchedResultsController, NSBatchInsertRequest for large imports, or custom migration mappings
- Use GRDB.swift when you need raw SQL queries, full-text search, or custom SQLite extensions that neither SwiftData nor Core Data expose
Reference Files
src/data/skills/index.js- add skill entries heresrc/components/Skills/DocRenderer.js- renders this doc format