Highlight
SwiftData is Apple’s native data persistence framework for Swift.
@ModelMacros directly define the data model in Swift code and automatically integrate with SwiftUI. Developers can obtain complete addition, deletion, modification, and iCloud synchronization capabilities without writing the cumbersome boilerplate code of Core Data.
Core Content
In the past, Core Data was almost the only option for data persistence on iOS. But it has a steep learning curve: you need to understand.xcdatamodeldConcepts like files, NSManagedObject, NSPredicate, NSFetchRequest, and switching back and forth between Swift and Objective-C APIs. A simple query may require more than a dozen lines of code, and the type is unsafe and errors cannot be checked at compile time.
SwiftData introduced at WWDC23 changes this situation.
SwiftData is based entirely on Swift code and has no external file formats. You write a regular Swift class and add@ModelMacro, it automatically has persistence capabilities. Value types such as strings, integers, and dates directly become data attributes, and references from other model classes automatically become relational attributes. unnecessary.xcdatamodeldFiles, no need to generate code, Schema is your Swift code itself.
Querying also becomes simpler. SwiftData introduces nativePredicateandFetchDescriptor, completely replacedNSPredicate. You can get autocompletion in Xcode and the compiler will check for type errors. A query is reduced from a dozen lines to two or three lines.
Integration with SwiftUI is out-of-the-box. Add one to the App scene.modelContainer()modifier, used in View@Queryand@Environment(\.modelContext), the data can be automatically bound to the UI. Modify model attributes and the interface will automatically refresh; insert or delete data and the list will automatically update.
SwiftData also has built-in platform capabilities such as CloudKit synchronization, Undo/Redo, and Widget support. Features that previously required a lot of extra work are now virtually free.
Data modeling
(01:09) The core of SwiftData is@ModelMacro. Add it to a class, and SwiftData will automatically analyze the storage properties of this class and generate the corresponding persistence schema.
import SwiftData
@Model
class Trip {
var name: String
var destination: String
var endDate: Date
var startDate: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
Key points:
@ModelCan only be used with classes because the model requires reference semantics- Value type attributes (String, Int, Date, struct, enum, Codable) automatically become attributes
- References to other model classes automatically become relationships
- Collection types (arrays) are also supported
(02:46) You can use@Attributeand@RelationshipAdd metadata:
@Model
class Trip {
@Attribute(.unique) var name: String
var destination: String
var endDate: Date
var startDate: Date
@Relationship(.cascade) var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
Key points:
@Attribute(.unique)Add uniqueness constraints to name -@Relationship(.cascade)Indicates that when a Trip is deleted, the associated bucketList items will also be deleted cascadingly- use
@TransientProperties that do not participate in persistence can be marked
Container and context
(03:43)ModelContainerIs the persistence backend,ModelContextIt is an interface for operating data.
There are two ways to initialize a container:
// Default configuration
let container = try ModelContainer([Trip.self, LivingAccommodation.self])
// Custom configuration
let container = try ModelContainer(
for: [Trip.self, LivingAccommodation.self],
configurations: ModelConfiguration(url: URL("path"))
)
In SwiftUI, use modifier to set the container:
import SwiftUI
@main
struct TripsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(
for: [Trip.self, LivingAccommodation.self]
)
}
}
Key points:
.modelContainer()Will automatically inject the container into the SwiftUI environment- Can configure URLs, CloudKit container identifiers, migration options, and more
- SwiftUI will also automatically propagate modelContext
(04:20) Get context in View:
import SwiftUI
struct ContextView: View {
@Environment(\.modelContext) private var context
}
Query data
(05:13) SwiftData uses nativePredicateMacro constructs query conditions, replacingNSPredicate:
let today = Date()
let tripPredicate = #Predicate<Trip> {
$0.destination == "New York" &&
$0.name.contains("birthday") &&
$0.startDate > today
}
Key points:
#PredicateIs a Swift macro that supports strong type checking- Xcode provides auto-completion
- Conditional expressions use native Swift syntax
(05:32) UseFetchDescriptorExecute query:
let descriptor = FetchDescriptor<Trip>(predicate: tripPredicate)
let trips = try context.fetch(descriptor)
(05:46) Add sorting:
let descriptor = FetchDescriptor<Trip>(
sortBy: SortDescriptor(\.Trip.name),
predicate: tripPredicate
)
let trips = try context.fetch(descriptor)
Key points:
FetchDescriptorSupports multiple options such as predicate, sort, prefetch relationship, result restrictions, etc. -SortDescriptorSupport native Swift types and keypath
Modify data
(06:15) ModelContext provides a complete addition, deletion and modification interface:
var myTrip = Trip(name: "Birthday Trip", destination: "New York")
// Insert a new object
context.insert(myTrip)
// Delete the object
context.delete(myTrip)
// Save changes
try context.save()
Key points:
- Create model objects just like normal Swift classes
-
insert()After the object starts to be tracked by the context - Attribute modifications will be automatically tracked and submitted the next time you save
- Support undo/redo
SwiftUI integration
(07:38)@QueryProperty wrappers make data binding extremely simple:
import SwiftUI
struct ContentView: View {
@Query(sort: \.startDate, order: .reverse) var trips: [Trip]
@Environment(\.modelContext) var modelContext
var body: some View {
NavigationStack() {
List {
ForEach(trips) { trip in
// ...
}
}
}
}
}
Key points:
@QueryAutomatically get data from context- Support sorting and filtering
- SwiftUI automatically refreshes the view when data changes
- SwiftData supports the new Observable feature, and property changes automatically trigger UI updates
Detailed Content
Migrating from Core Data
SwiftData and Core Data can coexist. Apple provides guidance for migrating from Core Data to SwiftData, including:
- Share the same persistent storage file
- Gradually migrate models
- Keep CloudKit in sync
Related documents: Adopting SwiftData for a Core Data app
Advanced usage of FetchDescriptor
FetchDescriptorIn addition to predicate and sort, it also supports:
- Prefetch relational objects to reduce database round-trips
- Limit the number of results
- Exclude unsaved changes
- Customized acquisition attributes
These options allow you to precisely control query behavior and optimize performance.
Platform integration
SwiftData works seamlessly with multiple Apple platform features:
- CloudKit: Automatic iCloud sync, no additional code required
- Widgets: Widget can directly read SwiftData data
- Undo/Redo: ModelContext built-in support
- Spotlight: Core Spotlight indexing can be integrated
Core Takeaways
1. Rewrite your notes/to-do app with SwiftData
- What to do: Migrate note-taking applications originally stored using UserDefaults or files to SwiftData
- Why it’s worth it: Get complete data query capabilities (filter by date, tags), automatic iCloud sync, Undo support
- How to start: Add the Note model
@Model,use@QueryTo replace the original array, useFetchDescriptorImplement search and filtering
2. Make a reader App that supports offline caching
- What to do: RSS reader or article collection app, supporting offline reading and synchronization
- Why it’s worth it: SwiftData’s CloudKit integration makes multi-device synchronization almost zero-cost, and FetchDescriptor’s prefetching feature optimizes the reading experience
- How to start: Define the Article model, use
@RelationshipAssociate Tag and use predicate to implement read/unread filtering
3. Make a travel planning app (similar to the Trip app in the Demo)
- What to do: Manage travel plans, packing lists, budget records
- Why it’s worth doing: SwiftData’s relational model is naturally suitable for this multi-entity association scenario, and SwiftUI integration makes interface development extremely fast.
- How to start: Create Trip, BucketListItem, LivingAccommodation and other models, use
@Relationship(.cascade)Manage association deletion
4. Add data persistence to existing SwiftUI App
- What to do: Persist the original state in memory to local
- Why it’s worth doing: Just add
@Modeland.modelContainer(), minimal changes, huge benefits - How to start: Identify the data model that needs to be persisted and add
@ModelMacro, configure the container at the App entry
Related Sessions
- Model your schema with SwiftData — Learn more about the details of data modeling with SwiftData, including complex relationships and migration strategies
- Dive deeper into SwiftData — SwiftData container configuration, context management, and advanced query techniques
- Build an app with SwiftData — Build a complete app from scratch with SwiftData
- SwiftUI — SwiftUI basics and new features
Comments
GitHub Issues · utterances