Instruction
SwiftData: Modernizing Data Persistence for SwiftUI
SwiftData, a Swift-native persistence framework, is designed exclusively with the Swift programming language. Tailored to seamlessly integrate with SwiftUI, it offers a streamlined and accelerated process for persisting user data within SwiftUI applications.
Distinguishing itself from the long-standing Core Data framework, SwiftData serves as a modern Swift-native and user-friendly persistence solution. While Core Data relies on older types rooted in its historical Objective-C foundation, SwiftData leverages the native types of Swift. Additionally, it takes advantage of contemporary features like Swift concurrency and Swift macros.
Despite both SwiftData and Core Data employing similar technologies for managing the underlying database, the coding experience with SwiftData is notably more straightforward, aligning better with Swift’s syntax than Core Data. For projects currently utilizing Core Data, transitioning to SwiftData is a relatively uncomplicated process, allowing for the preservation of the existing database structure. You’ll find detailed migration instructions here.
SwiftData Essentials: A Simple Walkthrough
Creating SwiftData Model
Defining the data model is a crucial first step when integrating SwiftData into your app. It uses tools like macros and property wrappers to easily persist and retrieve your data models. To enable the storage of instances of a model class with SwiftData, import the SwiftData framework and mark the class with the @Model macro. This macro ensures that SwiftData is able to preserve and track all changes for this model by adding conformance to both PersistentModel and Observable protocols. Here’s an example of a normal class model converted into SwiftData model:
import SwiftData
@Model
class JokeAuthor: Identifiable {
var id = UUID()
var name: String
var country: String
init(id: UUID = UUID(), name: String = "", country: String = "") {
self.id = id
self.name = name
self.country = country
}
}
Although the framework’s default settings for properties are usually enough, you might want to customize some of them for certain cases. For instance, if you want to make sure the value of a property is unique across all instances of that model, you can use the Attribute(_:originalName:hashModifier:) macro. Here’s a simple example where you make sure your id property from the last JokeAuthor model is unique:
@Attribute(.unique) var id = UUID()
Putting SwiftData Model into Use
You created your SwiftData model successfully. Now, you need to tell SwiftData to persist this model in runtime. To do this, you use the modelContainer(for:inMemory:isAutosaveEnabled:isUndoEnabled:onSetup:) view modifier at the very top level of your view hierarchy, which is mostly your ContentView():
@main
struct AppMain: App {
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(for: [JokeAuthor.self])
}
}
}
Note: You can define more specifications by configuring your container in a particular way. For instance, you can select whether or not the storage is read-only, or whether or not it’s limited to memory. Alternatively, you can also use CloudKit to sync your model across all your devices.
SwiftData CRUD Operations
Reading
Now that both your app and SwiftData recognize the model type for persistence throughout your app, you can access this model from any part of your app. To achieve this, add the Query property wrapper to a property with the same type as the one you added to your modelContainer.
@Query(sort: \JokeAuthor.country, order: .forward) var jokeAuthors: [JokeAuthor]
Take note of how the Query feature allows you to perform various actions on the fetched model, such as filtering, sorting, or ordering the data before using it in your application. In the example above, the Query parameter sorts the model based on the author’s country in ascending order.
Creating
Initially, your model will have no data. To populate your model, you utilize an environment property wrapper offered by SwiftData called modelContext. The model context is responsible for managing in-memory model data and coordinating with the model container to ensure successful data persistence. Begin by specifying the context:
@Environment(\.modelContext) private var context
Subsequently, you add data to your model using the insert method, like so:
context.insert(newAuthor)
Updating
Updating data in SwiftData is a seamless process that doesn’t need direct interaction with the modelContext. By simply modifying the instances retrieved through the Query property wrapper, the data is automatically updated. In the provided example, the jokeAuthors array reflects the updated data, effortlessly handling the updating process of this model.
@Query(sort: \JokeAuthor.country, order: .forward) var jokeAuthors: [JokeAuthor]
Deleting
Deleting data in SwiftData follows a straightforward approach, similar to creating. Using the delete method, you can effortlessly remove instances from your model. In the given example, the author instance is passed to the delete method, triggering the removal of the corresponding data.
context.delete(author)
SwiftData’s seamless integration with these operations simplifies the management of your model, providing a hassle-free experience. Now, it’s time to put these SwiftData operations into action within the JoyJotter app you’ve been working on in the previous lessons. You’ll create, update, and delete data using SwiftData to enhance the functionality and persistence of your JoyJotter app.