2024 App ServicesSwift
WWDC24 · 14 min · App Services / Swift
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”.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 17 snippets
SampleTrips models decorated with @Model
// Trip Models decorated with @Model
import Foundation
import SwiftData
class Trip {
var name: String
var destination: String
var startDate: Date
var endDate: Date
var bucketList: [BucketListItem] = [BucketListItem]()
var livingAccommodation: LivingAccommodation?
}
class BucketListItem {...}
class LivingAccommodation {...} 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)
}
} SampleTrips using @Query
// Trip App using @Query
import SwiftUI
import SwiftData
struct ContentView: View {
var trips: [Trip]
var body: some View {
NavigationSplitView {
List(selection: $selection) {
ForEach(trips) { trip in
TripListItem(trip: trip)
}
}
}
}
} SampleTrips models decorated with @Model
// Trip Models decorated with @Model
import Foundation
import SwiftData
class Trip {
var name: String
var destination: String
var startDate: Date
var endDate: Date
var bucketList: [BucketListItem] = [BucketListItem]()
var livingAccommodation: LivingAccommodation?
}
class BucketListItem {...}
class LivingAccommodation {...} Add unique constraints to avoid duplication
// Add unique constraints to avoid duplication
import SwiftData
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?
} Add .preserveValueOnDeletion to capture unique columns
// Add .preserveValueOnDeletion to capture unique columns
import SwiftData
class Trip {
#Unique<Trip>([\.name, \.startDate, \.endDate])
(.preserveValueOnDeletion)
var name: String
var destination: String
(.preserveValueOnDeletion)
var startDate: Date
(.preserveValueOnDeletion)
var endDate: Date
var bucketList: [BucketListItem] = [BucketListItem]()
var livingAccommodation: LivingAccommodation?
} 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)
}
} 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)
}
} 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)
}
} 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)
}
} 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 {
static var sampleData: Self = .modifier(SampleData())
} Use sample data in a preview
// Use sample data in a preview
import SwiftUI
import SwiftData
struct ContentView: View {
var trips: [Trip]
var body: some View {
...
}
}
#Preview(traits: .sampleData) {
ContentView()
} Create a preview query using @Previewable
// Create a preview query using @Previewable
import SwiftUI
import SwiftData
#Preview(traits: .sampleData) {
var trips: [Trip]
BucketListItemView(trip: trips.first)
} 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)
} 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)
} 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
} Add Index for commonly used KeyPaths or combination of KeyPaths
// Add Index for commonly used KeyPaths or combination of KeyPaths
import SwiftData
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
} Resources
Related sessions
-
9 min -
14 min -
17 min