Local Persistence: SwiftData vs. Core Data
iOS Development — Architecture & Data
Chapter 4 · Local Persistence: SwiftData vs. Core Data
TaskFlow's own tasks vanish every time the app relaunches — nothing was ever actually saved to disk. This chapter covers Apple's real, current answer for local persistence, SwiftData, honestly contrasted against the older framework it builds on, Core Data.
Core Data: Real, Established, Genuinely Complex
Core Data is Apple's long-established real object-graph and persistence framework — genuinely powerful,
and genuinely more complex to set up than most of what this course has covered so far. A typical Core
Data model requires a separate .xcdatamodeld editor file, NSManagedObject
subclasses, and manual NSPersistentContainer setup before a single object can be saved.
SwiftData: Apple's Real, Current Answer
Introduced at WWDC 2023 alongside iOS 17 and Swift 5.9 — the same real release that brought the Observation framework (Fundamentals Chapter 6) — SwiftData is Apple's modern, Swift-native persistence framework, built on Apple's own established Core Data persistence engine but exposed through a genuinely different, dramatically simpler real API.
@ModelA real macro turning a plain Swift class into a persisted model — no separate schema editor file needed.
@QueryA real property wrapper fetching and automatically observing persisted data directly inside a SwiftUI view.
ModelContainerManages the real schema and on-disk storage — set up once, typically at the app's own root.
ModelContextTracks in-memory changes and saves them — SwiftData's real equivalent of Core Data's NSManagedObjectContext.
Turning Task into a Real, Persisted Model
Task is now a real class, not the struct Fundamentals used — SwiftData's own
@Model macro requires a reference type, since persisted objects need a real, stable identity
across fetches and edits, the same underlying reason NSManagedObject has always been a class
in Core Data too. This is a genuine, deliberate exception to Fundamentals Chapter 4's own "prefer structs"
guidance — persistence is exactly the kind of case that guidance already flagged as needing shared,
mutable identity.
Wiring Up the ModelContainer
The real .modelContainer(for:) modifier sets up SwiftData's own on-disk storage for
Task once, at the app's own root — every view inside WindowGroup can then reach
a shared ModelContext automatically, without passing it down by hand.
Fetching with @Query
@Query behaves like a real, persisted cousin of Fundamentals Chapter 6's own
@State — a view using it redraws automatically whenever the underlying data changes, with no
manual refresh call needed, whether that change came from this view's own modelContext.insert(...)
or from anywhere else in the app.
| Core Data | SwiftData | |
|---|---|---|
| Real Introduction | 2005 (macOS), long-established on iOS | iOS 17, Swift 5.9, WWDC 2023 |
| Model Definition | A separate .xcdatamodeld editor file | A plain Swift class with @Model |
| Fetching | NSFetchRequest, real, verbose setup | @Query, a single property wrapper |
| Best Real Fit | Existing Core Data apps, advanced/legacy needs | New apps' own straightforward persistence |
Hands-On Exercises
Write a real @Model class JournalEntry with var text: String and var date: Date, plus a real initializer, and a view using @Query private var entries: [JournalEntry] to display all entries in a List.
Write a real function deleteTask(_ task: Task, context: ModelContext) using SwiftData's own context.delete(task) method, and explain, in your own words, why this call doesn't need an explicit "save" step for the deletion to persist to disk.
Explain, in your own words, why SwiftData's @Model requires a class rather than a struct, connecting your answer directly to Fundamentals Chapter 4's own value-type/reference-type distinction and its real "when to reach for a class instead" guidance.
Chapter 4 Quick Reference
- Core Data — real, established, genuinely more complex; still the right real fit for existing apps and some advanced needs
- SwiftData (iOS 17/Swift 5.9, WWDC 2023) — built on Core Data's own engine, exposed through a dramatically simpler real API
@Modelturns a plain class into a persisted model — no separate schema file@Queryfetches and automatically observes persisted data, like a persisted cousin of@StateModelContainer(schema/storage) andModelContext(in-memory changes/saving) are SwiftData's real core pieces@Modelclasses are genuinely a deliberate exception to "prefer structs" — persistence needs real, stable, shared identity