Defining the Model with @Model
In SwiftData, we use the
@Model macro to define our data model schema. By decorating your model with this macro, SwiftData auto-magically transforms your stored properties into persisted ones.
Consider a simple user model:
In this example, the
User model with a
username and an
age property is defined. SwiftData will automatically create a schema for this model and handle the persistence for you. You can take this project as a sample to build your app with SwiftData.
Basic and Complex Value Types
SwiftData supports a wide range of value types, from simple ones such as Strings, Ints, and Floats, to more complex types like Enums, Structs, and Codables. Collections of these value types are also supported.
For instance, you might have a
User model that has an array of favourite book titles:
Schema Macros for Control
SwiftData offers schema macros that enable developers to control how properties are inferred. Here's a quick overview of the primary schema macros you can use:
@Attribute: Allows you to add attributes to your model schema.
.unique: Models uniqueness constraints, enabling upsert operations.
originalName: String: Allows you to rename a variable without losing data.
@Relationship: Controls the choice of inverses and delete propagation rules.
@Transient: Instructs SwiftData to ignore certain stored properties.
These macros provide granular control over how your models are handled, making it easier to enforce constraints and manage relationships.
Let’s take our
User model for example. We can specify that the
username property must be unique, so SwiftData knows that is we attempt to add another value to the persisted storage, with the same username value, it will instead update the existing one instead of inserting a new one.
We would do this as follows:
In SwiftData, we model relationships between types (classes) through references, enabling you to create links between your model types. Let's consider a blogging app, where an
Author can have multiple
In this scenario, if the
Post model references the
Author model, then the
Author model must also be decorated with the
Relationship Delete Rule
When deleting a model that references other models, you need to describe the rule to apply. Here are the options:
[.cascade](<https://developer.apple.com/documentation/swiftdata/relationshipdeleterule/cascade>): Deletes any related models.
[.deny](<https://developer.apple.com/documentation/swiftdata/relationshipdeleterule/deny>): Prevents the deletion of a model if it contains references to other models.
[.noAction](<https://developer.apple.com/documentation/swiftdata/relationshipdeleterule/noaction>): Makes no changes to any related models.
[.nullify](<https://developer.apple.com/documentation/swiftdata/relationshipdeleterule/nullify>): Nullifies the related model’s reference to the deleted model.
To note that we want an author to be deleted if we delete a post that references it, we could add the
.cascade delete rule, but that its probably not what we’d want in this scenario, so we’ll use
Containers and Context: The Building Blocks of Persistence
Two crucial concepts in SwiftData are ModelContainer and ModelContext, which together create the persistent backend for your model types.
The ModelContainer is responsible for creating the persistent backend for your model types. To create a ModelContainer, specify the list of model types you want to store. You can also configure it further by specifying the URL where you want the data stored.
SwiftUI's view and scene modifiers also make it easier to establish the container in the views environment.
You need at least one model container to use SwiftData, but you can use as many containers as you need for different view hierarchies.
ModelContext is your interface for interacting with your model. It observes all changes to your models and provides actions you can perform, like saving, deleting, or undoing changes. In SwiftUI, you'll typically get your ModelContext from your view's environment:
Outside the view hierarchy, you can access a shared Main Actor context:
Or instantiate a new context for a model container:
SwiftData offers various ways to fetch and modify data, all driven by the ModelContext. With Swift's native
FetchDescriptor, you can sort and filter the data you fetch.
An example predicate looks like this:
In this case, we're fetching all the posts with “SwiftUI” in their title.
The true beauty of SwiftData lies in its seamless integration with SwiftUI. The
@Query property wrapper lets you load and filter anything in the database right within SwiftUI:
SwiftData supports the new
@Observable feature, creating automatic dependencies between a view and its data and binding model data to the UI.
Modifying data is as simple as calling
context.delete(object), or using the standard property setters. What’s great is that you also don’t need to explicitly call
try context.save() after making modifications to persist changes - SwiftData autosaves changes based on certain UI changes or user actions. Keep in mind though that in cases where you know you want data to persist immediately, you can use
try context.save() to ensure the data has been committed to storage.
Adding SwiftData to an App
To start using SwiftData, import the framework:
Next, create a container for your app and add the
@Model decorator to your model type. You can then use
@Query in SwiftUI to retrieve your data. Accessing the context is as easy as using the
@Environment property wrapper:
With that, you're all set to start leveraging SwiftData's power in your application!
In essence, SwiftData streamlines persistence in SwiftUI apps, allowing developers to focus more on building outstanding user experiences. It's another testament to how Apple continues to evolve its frameworks in a developer-friendly manner. Happy coding!
@Modelto the models you want to persist in storage, as well as any referenced models.
@Attribute(.unique)to mark a property as unique.
@Relationship(.cascade)or any other delete rule to control how deletion of referenced objects is managed.
- Initialise a container using
let container = ModelContainer([Author.self, Post.self])or adding the
.modelContainer(for: Post.self)to your view (generally in your App file), specifying the model types you want to persist.
- Create a
modelContextenvironment variable in your SwiftUI view or other (e.g. MVVM) class.
- Create a
@Queryvariable to fetch your data within your SwiftUI view, including your desired predicates and sort options.
- Use the
modelContextvariable to insert, delete or modify any of the stored objects.
- If you require persistence immediately, call