Streaming is available in most browsers,
and in the Developer app.
-
What’s new in SwiftData
SwiftData makes it easy to add persistence to your app with its expressive, declarative API. Learn about refinements to SwiftData, including compound uniqueness constraints, faster queries with #Index, queries in Xcode previews, and rich predicate expressions. Join us to explore how you can use all of these features to express richer models and improve performance in your app. To discover how to build a custom data store or use the history API in SwiftData, watch “Create a custom data store with SwiftData” and “Track model changes with SwiftData history”.
Chapters
- 0:00 - Introduction
- 0:57 - Adopt SwiftData
- 2:11 - Customize the schema
- 2:43 - #Unique macro
- 3:37 - History API
- 4:29 - Tailor a model container
- 5:39 - Custom data stores
- 6:41 - Xcode previews
- 9:20 - Customize queries
- 10:18 - #Expression macro
- 11:56 - #Index macro
Resources
Related Videos
WWDC24
WWDC23
-
DownloadArray
-
-
1:32 - SampleTrips models decorated with @Model
// Trip Models decorated with @Model import Foundation import SwiftData @Model class Trip { var name: String var destination: String var startDate: Date var endDate: Date var bucketList: [BucketListItem] = [BucketListItem]() var livingAccommodation: LivingAccommodation? } @Model class BucketListItem {...} @Model class LivingAccommodation {...}
-
1:43 - SampleTrips using modelContainer scene modifier
// Trip App using modelContainer Scene modifier import SwiftUI import SwiftData @main struct TripsApp: App { var body: some Scene { WindowGroup { ContentView } .modelContainer(for: Trip.self) } }
-
1:53 - SampleTrips using @Query
// Trip App using @Query import SwiftUI import SwiftData struct ContentView: View { @Query var trips: [Trip] var body: some View { NavigationSplitView { List(selection: $selection) { ForEach(trips) { trip in TripListItem(trip: trip) } } } } }
-
2:16 - SampleTrips models decorated with @Model
// Trip Models decorated with @Model import Foundation import SwiftData @Model class Trip { var name: String var destination: String var startDate: Date var endDate: Date var bucketList: [BucketListItem] = [BucketListItem]() var livingAccommodation: LivingAccommodation? } @Model class BucketListItem {...} @Model class LivingAccommodation {...}
-
3:08 - Add unique constraints to avoid duplication
// Add unique constraints to avoid duplication import SwiftData @Model class Trip { #Unique<Trip>([\.name, \.startDate, \.endDate]) var name: String var destination: String var startDate: Date var endDate: Date var bucketList: [BucketListItem] = [BucketListItem]() var livingAccommodation: LivingAccommodation? }
-
3:36 - Add .preserveValueOnDeletion to capture unique columns
// Add .preserveValueOnDeletion to capture unique columns import SwiftData @Model class Trip { #Unique<Trip>([\.name, \.startDate, \.endDate]) @Attribute(.preserveValueOnDeletion) var name: String var destination: String @Attribute(.preserveValueOnDeletion) var startDate: Date @Attribute(.preserveValueOnDeletion) var endDate: Date var bucketList: [BucketListItem] = [BucketListItem]() var livingAccommodation: LivingAccommodation? }
-
4:35 - SampleTrips using modelContainer scene modifier
// Trip App using modelContainer Scene modifier import SwiftUI import SwiftData @main struct TripsApp: App { var body: some Scene { WindowGroup { ContentView() } .modelContainer(for: Trip.self) } }
-
4:52 - Customize a model container in the app
// Customize a model container in the app import SwiftUI import SwiftData @main struct TripsApp: App { var body: some Scene { WindowGroup { ContentView() } .modelContainer(for: Trip.self, inMemory: true, isAutosaveEnabled: true, isUndoEnabled: true) } }
-
5:13 - Add a model container to the app
// Add a model container to the app import SwiftUI import SwiftData @main struct TripsApp: App { var container: ModelContainer = { do { let configuration = ModelConfiguration(schema: Schema([Trip.self]), url: fileURL) return try ModelContainer(for: Trip.self, configurations: configuration) } catch { ... } }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(container) } }
-
5:59 - Use your own custom data store
// Use your own custom data store import SwiftUI import SwiftData @main struct TripsApp: App { var container: ModelContainer = { do { let configuration = JSONStoreConfiguration(schema: Schema([Trip.self]), url: jsonFileURL) return try ModelContainer(for: Trip.self, configurations: configuration) } catch { ... } }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(container) } }
-
6:58 - Make preview data using traits
// Make preview data using traits struct SampleData: PreviewModifier { static func makeSharedContext() throws -> ModelContainer { let config = ModelConfiguration(isStoredInMemoryOnly: true) let container = try ModelContainer(for: Trip.self, configurations: config) Trip.makeSampleTrips(in: container) return container } func body(content: Content, context: ModelContainer) -> some View { content.modelContainer(context) } } extension PreviewTrait where T == Preview.ViewTraits { @MainActor static var sampleData: Self = .modifier(SampleData()) }
-
8:15 - Use sample data in a preview
// Use sample data in a preview import SwiftUI import SwiftData struct ContentView: View { @Query var trips: [Trip] var body: some View { ... } } #Preview(traits: .sampleData) { ContentView() }
-
8:50 - Create a preview query using @Previewable
// Create a preview query using @Previewable import SwiftUI import SwiftData #Preview(traits: .sampleData) { @Previewable @Query var trips: [Trip] BucketListItemView(trip: trips.first) }
-
9:55 - Create a predicate to find a Trip based on search text
// Create a Predicate to find a Trip based on Search Text let predicate = #Predicate<Trip> { searchText.isEmpty ? true : $0.name.localizedStandardContains(searchText) }
-
10:06 - Create a Compound Predicate to find a Trip based on Search Text
// Create a Compound Predicate to find a Trip based on Search Text let predicate = #Predicate<Trip> { searchText.isEmpty ? true : $0.name.localizedStandardContains(searchText) || $0.destination.localizedStandardContains(searchText) }
-
10:46 - Build a predicate to find Trips with BucketListItems that are not in the plan
// Build a predicate to find Trips with BucketListItems that are not in the plan let unplannedItemsExpression = #Expression<[BucketListItem], Int> { items in items.filter { !$0.isInPlan }.count } let today = Date.now let tripsWithUnplannedItems = #Predicate<Trip>{ trip // The current date falls within the trip (trip.startDate ..< trip.endDate).contains(today) && // The trip has at least one BucketListItem // where 'isInPlan' is false unplannedItemsExpression.evaluate(trip.bucketList) > 0 }
-
12:41 - Add Index for commonly used KeyPaths or combination of KeyPaths
// Add Index for commonly used KeyPaths or combination of KeyPaths import SwiftData @Model class Trip { #Unique<Trip>([\.name, \.startDate, \.endDate #Index<Trip>([\.name], [\.startDate], [\.endDate], [\.name, \.startDate, \.endDate]) var name: String var destination: String var startDate: Date var endDate: Date var bucketList: [BucketListItem] = [BucketListItem var livingAccommodation: LivingAccommodation }
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.