Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2023 Swift

WWDC23 · 16 min · 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 ↗

Transcript all transcripts

Chapters

Code shown on screen · 15 snippets

Trip model with cascading relationships swift · at 1:45 ↗
@Model
final class Trip {
    var destination: String?
    var end_date: Date?
    var name: String?
    var start_date: Date?
  
    @Relationship(.cascade)
    var bucketListItem: [BucketListItem] = [BucketListItem]()
  
    @Relationship(.cascade)
    var livingAccommodation: LivingAccommodation?
}
Initializing a ModelContainer swift · at 4:21 ↗
// 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 swift · at 5:41 ↗
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 swift · at 6:49 ↗
@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 swift · at 7:40 ↗
@main
struct TripsApp: App {
   var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: Trip.self)
    }
}
Referencing a ModelContext in SwiftUI views swift · at 7:50 ↗
struct ContentView: View {
    @Query var trips: [Trip]
    @Environment(\.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 swift · at 9:57 ↗
@main
struct TripsApp: App {
   @Environment(\.undoManager) var undoManager
   var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: Trip.self, isUndoEnabled: true)
    }
}
Enabling autosave on a ModelContainer swift · at 11:05 ↗
@main
struct TripsApp: App {
   var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: Trip.self, isAutosaveEnabled: false)
    }
}
Fetching objects with FetchDescriptor swift · at 11:54 ↗
let context = self.newSwiftContext(from: Trip.self)
var trips = try context.fetch(FetchDescriptor<Trip>())
Fetching objects with #Predicate and FetchDescriptor swift · at 12:14 ↗
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 swift · at 12:27 ↗
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 swift · at 13:18 ↗
context.enumerate(FetchDescriptor<Trip>()) { trip in
    // Operate on trip
}
Enumerating with FetchDescriptor and SortDescriptor swift · at 13:36 ↗
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 swift · at 14:01 ↗
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 swift · at 14:28 ↗
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