Logo for tanaschita.com

Quick developer guide on SwiftData for iOS

Get an overview on Apple's SwiftData framework.

16 Oct 2023 · 4 min read

At WWDC23, Apple introduced SwiftData - a framework which focuses on persisting data by using declarative code. SwiftData uses Swift's new macro system and is designed to work with SwiftUI.

Let's directly jump in and look at the basics on how SwiftData works.

Sponsorship logo
Preparing for a technical iOS job interview - updated for iOS 17
Check out my book on preparing for a technical iOS job interview with over 200 questions & answers. Test your knowledge on iOS topics such as Swift, SwiftUI, Combine, HTTP Networking, Authentication, SwiftData & Core Data, Concurrency with async/await, Security, Automated Testing, Machine Learning and more.
LEARN MORE

Defining a schema with the @Model macro

The first step when trying to persist data in a data base is to define a model schema - which is a blueprint that outlines the structure and relationships within a database.

SwiftData introduces the @Model macro which lets us define our models directly with Swift. Let's look at a simple example:

@Model
class User {
let name: String
let notes: [Note]
}

When defining models with SwiftData, we can use all basic types as properties out of the box. We can also use custom types like structs and enums that conform to the Codable protocol.

SwiftData provides different ways to add meta data to properties which gives us control on how they are persisted. For example, we can use the @Attribute macro to add a uniqueness constraint to a property:

class User {
...
@Attribute(.unique) let id: String
}

By default, SwiftData persists all non-computed attributes. In case we want to exclude properties from persisting, we can use the @Transient macro:

class User {
@Transient var isValidated: Bool
}

To specify a relationship between two model classes, SwiftData provides the @Relationship macro. Check out this article to learn more about relationships in SwiftData (coming soon).

Model container

SwiftData's model container represents the persistence backend which we can use to customize persistence behaviour or to control migration. To use SwiftData, an app needs to setup at least one model container.

We can setup a model container as follows:

let modelContainer = try ModelContainer(for: User.self)

When initializing a model container, we pass in all models we'd like to store.

If needed, we can additionally pass in a configuration, for example to use a certain storage location or to save the data in memory by using ModelConfiguration:

let configuration = ModelConfiguration(inMemory: true)
let container = try ModelContainer(for: schema, configurations: [configuration])

Model context

SwiftData's model context allows us to track updates, fetch models, save or undo changes.

We can create a model context as follows:

let context = ModelContext(container)

Now, we can use it to execute an action, for example to save a user:

context.insert(user)
try context.save()

Alternatively to creating a model container as shown above, SwiftUI provides a view modifier to setup a model container on a SwiftUI view and use the environment to get the model context. Check out this article on how to use SwiftData view modifiers with SwiftUI (coming soon) to learn more.

Fetching data

To get stored data, we can create a fetch descriptor and use it to fetch the data we need. For example a simple request to fetch all users might look as follows:

let fetchDescriptor = FetchDescriptor<User>()
let users = try modelContext.fetch(fetchDescriptor)

Additionally, we can pass in a predicate and a sort descriptor to filter and sort the results:

let descriptor = FetchDescriptor<User>(
predicate: predicate,
sortBy: sortDescriptor)

Swift provides a new fully type checked Predicate type for fetching data which is a replacement for NSPredicate. For example:

let predicate = #Predicate<User> {
$0.id == someId
}

For SwiftUI, SwiftData additionally provides the @Query property wrapper which provides the view with the data. Checkout this article on how to work with SwiftData queries in SwiftUI (coming soon) to learn more.

Migration

When working with persistence, migration is often needed as soon as the schema changes. For example, if we need to rename some properties, change relationships and more.

SwiftData provides a VersionedSchema and SchemaMigrationPlan to define migration steps. Check out this article on how to migrate to a new schema with SwiftData (coming soon) to learn more.

Sponsorship logo
Preparing for a technical iOS job interview - updated for iOS 17
Check out my book on preparing for a technical iOS job interview with over 200 questions & answers. Test your knowledge on iOS topics such as Swift, SwiftUI, Combine, HTTP Networking, Authentication, SwiftData & Core Data, Concurrency with async/await, Security, Automated Testing, Machine Learning and more.
LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

swiftdata

persistence

swiftui

swift

ios

How to get a SwiftData model container and context in SwiftUI

Learn how to setup a SwiftData container and context directly in SwiftUI.

30 Oct 2023 · 4 min read

Latest articles and tips

© 2024 tanaschita.com

Privacy policy

Impressum