2023 Swift
Dive deeper into SwiftData
Learn how you can harness the power of SwiftData in your app. Find out how ModelContext and ModelContainer work together to persist your app’s data. We’ll show you how to track and make your changes manually and use SwiftData at scale with FetchDescriptor, SortDescriptor, and enumerate. To get the most out of this session, we recommend first watching "Meet SwiftData" and "Model your schema with SwiftData" from WWDC23.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 15 snippets
Trip model with cascading relationships
final class Trip {
var destination: String?
var end_date: Date?
var name: String?
var start_date: Date?
(.cascade)
var bucketListItem: [BucketListItem] = [BucketListItem]()
(.cascade)
var livingAccommodation: LivingAccommodation?
} Initializing a ModelContainer
// ModelContainer initialized with just Trip
let container = try ModelContainer(for: Trip.self)
// SwiftData infers related model classes as well
let container = try ModelContainer(
for: [
Trip.self,
BucketListItem.self,
LivingAccommodation.self
]
) Using ModelConfiguration to customize ModelContainer
let fullSchema = Schema([
Trip.self,
BucketListItem.self,
LivingAccommodations.self,
Person.self,
Address.self
])
let trips = ModelConfiguration(
schema: Schema([
Trip.self,
BucketListItem.self,
LivingAccommodations.self
]),
url: URL(filePath: "/path/to/trip.store"),
cloudKitContainerIdentifier: "com.example.trips"
)
let people = ModelConfiguration(
schema: Schema([Person.self, Address.self]),
url: URL(filePath: "/path/to/people.store"),
cloudKitContainerIdentifier: "com.example.people"
)
let container = try ModelContainer(for: fullSchema, trips, people) Creating ModelContainer in SwiftUI
@main
struct TripsApp: App {
let fullSchema = Schema([
Trip.self,
BucketListItem.self,
LivingAccommodations.self,
Person.self,
Address.self
])
let trips = ModelConfiguration(
schema: Schema([
Trip.self,
BucketListItem.self,
LivingAccommodations.self
]),
url: URL(filePath: "/path/to/trip.store"),
cloudKitContainerIdentifier: "com.example.trips"
)
let people = ModelConfiguration(
schema: Schema([
Person.self,
Address.self
]),
url: URL(filePath: "/path/to/people.store"),
cloudKitContainerIdentifier: "com.example.people"
)
let container = try ModelContainer(for: fullSchema, trips, people)
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container)
}
} Using the modelContainer modifier
@main
struct TripsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: Trip.self)
}
} Referencing a ModelContext in SwiftUI views
struct ContentView: View {
var trips: [Trip]
(\.modelContext) var modelContext
var body: some View {
NavigationStack (path: $path) {
List(selection: $selection) {
ForEach(trips) { trip in
TripListItem(trip: trip)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
modelContext.delete(trip)
} label: {
Label("Delete", systemImage: "trash")
}
}
}
.onDelete(perform: deleteTrips(at:))
}
}
}
} Enabling undo on a ModelContainer
@main
struct TripsApp: App {
(\.undoManager) var undoManager
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: Trip.self, isUndoEnabled: true)
}
} Enabling autosave on a ModelContainer
@main
struct TripsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: Trip.self, isAutosaveEnabled: false)
}
} Fetching objects with FetchDescriptor
let context = self.newSwiftContext(from: Trip.self)
var trips = try context.fetch(FetchDescriptor<Trip>()) Fetching objects with #Predicate and FetchDescriptor
let context = self.newSwiftContext(from: Trip.self)
let hotelNames = ["First", "Second", "Third"]
var predicate = #Predicate<Trip> { trip in
trip.livingAccommodations.filter {
hotelNames.contains($0.placeName)
}.count > 0
}
var descriptor = FetchDescriptor(predicate: predicate)
var trips = try context.fetch(descriptor) Fetching objects with #Predicate and FetchDescriptor
let context = self.newSwiftContext(from: Trip.self)
predicate = #Predicate<Trip> { trip in
trip.livingAccommodations.filter {
$0.hasReservation == false
}.count > 0
}
descriptor = FetchDescriptor(predicate: predicate)
var trips = try context.fetch(descriptor) Enumerating objects with FetchDescriptor
context.enumerate(FetchDescriptor<Trip>()) { trip in
// Operate on trip
} Enumerating with FetchDescriptor and SortDescriptor
let predicate = #Predicate<Trip> { trip in
trip.bucketListItem.filter {
$0.hasReservation == false
}.count > 0
}
let descriptor = FetchDescriptor(predicate: predicate)
descriptor.sortBy = [SortDescriptor(\.start_date)]
context.enumerate(descriptor) { trip in
// Remind me to make reservations for trip
} Fine tuning enumerate with batchSize
let predicate = #Predicate<Trip> { trip in
trip.bucketListItem.filter {
$0.hasReservation == false
}.count > 0
}
let descriptor = FetchDescriptor(predicate: predicate)
descriptor.sortBy = [SortDescriptor(\.start_date)]
context.enumerate(
descriptor,
batchSize: 10000
) { trip in
// Remind me to make reservations for trip
} Fine tuning enumerate with batchSize and allowEscapingMutations
let predicate = #Predicate<Trip> { trip in
trip.bucketListItem.filter {
$0.hasReservation == false
}.count > 0
}
let descriptor = FetchDescriptor(predicate: predicate)
descriptor.sortBy = [SortDescriptor(\.start_date)]
context.enumerate(
descriptor,
batchSize: 500,
allowEscapingMutations: true
) { trip in
// Remind me to make reservations for trip
} Resources
Related sessions
-
13 min -
19 min -
9 min -
11 min -
9 min