Skip to main content

How to use SwiftData in SwiftUI previews

About 2 minSwiftArticle(s)bloghackingwithswift.comcrashcourseswiftswiftdataxcodeappstore

How to use SwiftData in SwiftUI previews 관련

SwiftData by Example

Back to Home

How to use SwiftData in SwiftUI previews | SwiftData by Example

How to use SwiftData in SwiftUI previews

Updated for Xcode 15

SwiftData makes basic Xcode previews trivial, because they work immediately as long as you don't try to insert any data – any properties created using the @Query macro will quietly return no results. However, if you want some sample model data you need to tread a little more carefully, including creating your data using an in-memory data store.

As an example, here's a trivial User model with a small SwiftUI that accepts a user to edit:

@Model
class User {
    var name: String

    init(name: String) {
        self.name = name
    }
}

struct EditingView: View {
    @Environment(\.modelContext) var modelContext
    @Bindable var user: User

    var body: some View {
        Form {
            TextField("Name", text: $user.name)
        }
    }
}

In order to create a sample User for your preview, you must create a custom ModelConfiguration that stores data in memory only, use that to create a ModelContainer for the User model, then finally create your sample data and send it into your view.

Creating a ModelContainer can throw errors, but as this is a preview I think you're safe using try! here:

#Preview {
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    let container = try! ModelContainer(for: User.self, configurations: config)

    let user = User(name: "Test User")
    return EditingView(user: user)
        .modelContainer(container)
}

Important

If you attempt to create a model object without first having created a container for that object, your preview will crash. If you do all that and don't use the modelContainer() modifier to send your container into SwiftUI, running any code using the \.modelContext environment key will also crash your preview.

If you want SwiftData to be able to query your objects, you should insert them into your in-memory context. For example, you might use a SwiftUI view with @Query, like this:

struct ContentView: View {
    @Query(sort: [SortDescriptor(\User.name, comparator: .localizedStandard)]) var users: [User]

    var body: some View {
        NavigationStack {
            List(users) { user in
                Text(user.name)
            }
        }
    }
}

That query won't detect any SwiftData objects unless you have specifically inserted them into the context, so your preview should look like this:

#Preview {
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    let container = try! ModelContainer(for: User.self, configurations: config)

    for i in 1..<10 {
        let user = User(name: "Example User \(i)")
        container.mainContext.insert(user)
    }

    return ContentView()
        .modelContainer(container)
}

Just as with Core Data, it's usually a good idea to set up some kind of standard preview singleton you can work with in many views – something that creates the in-memory configuration, creates a container from it using whatever types are in your project, then inserts some standard sample data.

For example, you might do this:

@MainActor
class DataController {
    static let previewContainer: ModelContainer = {
        do {
            let config = ModelConfiguration(isStoredInMemoryOnly: true)
            let container = try ModelContainer(for: User.self, configurations: config)

            for i in 1...9 {
                let user = User(name: "Example User \(i)")
                container.mainContext.insert(user)
            }

            return container
        } catch {
            fatalError("Failed to create model container for previewing: \(error.localizedDescription)")
        }
    }()
}

이찬희 (MarkiiimarK)
Never Stop Learning.