Learn to Build Your First iOS App

Subscribe below to receive a free 200-page guide that will teach you how to build your first iOS app.

Get a sample book packed with everything you need to start your development journey with Swift and SwiftUI.

    We respect your privacy. Unsubscribe at any time.
    SwiftUI · · 3 min read

    Using SwiftData with Preview in SwiftUI

    Using SwiftData with Preview in SwiftUI

    In the earlier tutorial, I have walked you through the basics of SwiftData, a new framework introduced in iOS 17 as a replacement for Core Data. If you have followed that tutorial, you should now be familiar with using SwiftData to save and manage data in a database. The built-in @Model macro and the @Query macro greatly simplify the process of defining data model and retrieving records from the database, making it extremely easy for developers to handle persistent data.

    The Preview feature in SwiftUI is highly valuable as it allows developers to instantly visualize the app’s user interface without the need to launch the simulator. However, using SwiftData with SwiftUI Preview requires some additional steps. In this tutorial, we will explore how to integrate SwiftData with SwiftUI Preview effectively.

    Note: If you haven’t read the SwiftData tutorial, I highly recommend checking it out first, as this tutorial references some of the materials covered in that tutorial.

    Revisiting the Data Model and SwiftData

    In the previous example, we have built a model class for ToDoItem like this:

    import Foundation
    import SwiftData
    
    @Model class ToDoItem: Identifiable {
        var id: UUID
        var name: String
        var isComplete: Bool
    
        init(id: UUID = UUID(), name: String = "", isComplete: Bool = false) {
            self.id = id
            self.name = name
            self.isComplete = isComplete
        }
    }

    SwiftData simplifies the process of defining a schema using code. You only need to mark the model class with the @Model macro. SwiftData will then automatically enable persistence for the data class.

    In order to drive the data operations (like update, insert, read, and delete), we also need to set up the model container. In the ToDoDemoAppApp.swift, we have attached the modelContainer modifier like below:

    struct ToDoDemoAppApp: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
            .modelContainer(for: ToDoItem.self)
        }
    }

    This configuration is essentially all you need before starting to work with SwiftData.

    Preview with SwiftData and In-memory Container

    In the Todo app demo, we have a ContentView that loads and displays the to-do item in the list view. Here is the sample code:

    struct ContentView: View {
        @Environment(\.modelContext) private var modelContext
    
        @Query var todoItems: [ToDoItem]
    
        var body: some View {
            NavigationStack {
                List {
                    ForEach(todoItems) { todoItem in
                        HStack {
                            Text(todoItem.name)
    
                            Spacer()
    
                            if todoItem.isComplete {
                                Image(systemName: "checkmark")
                            }
                        }
                    }
                }
    
                .navigationTitle("To Do List")
            }
        }
        }

    You can make the preview work by writing the preview code like this:

    #Preview {
        ContentView()
            .modelContainer(for: ToDoItem.self)
    }

    However, in this case, the preview only displays an empty Todo list because the container doesn’t have any data populated. If you desire to have some sample data, you can create a custom model container specifically for the preview. Here is an example:

    @MainActor
    let previewContainer: ModelContainer = {
        do {
            let container = try ModelContainer(for: ToDoItem.self,
                                               configurations: .init(isStoredInMemoryOnly: true))
    
            for _ in 1...10 {
                container.mainContext.insert(generateRandomTodoItem())
            }
    
            return container
        } catch {
            fatalError("Failed to create container")
        }
    }()
    
    func generateRandomTodoItem() -> ToDoItem {
        let tasks = [ "Buy groceries", "Finish homework", "Go for a run", "Practice Yoga", "Read a book", "Write a blog post", "Clean the house", "Walk the dog", "Attend a meeting" ]
    
        let randomIndex = Int.random(in: 0..<tasks.count)
        let randomTask = tasks[randomIndex]
    
        return ToDoItem(name: randomTask, isComplete: Bool.random())
    }

    We instantiate a ModelContainer with an in-memory configuration and populate the container with 10 random to-do items. To use this preview container, you simply modify the preview code and specify to use the previewContainer:

    #Preview {
        ContentView()
            .modelContainer(previewContainer)
    }

    Once you made the modification, the preview pane should show you the Todo list view with 10 random items.

    swiftdata-preview-demo

    Summary

    SwiftUI Preview is a valuable feature that allows developers to visualize their app’s user interface instantly, without the need to launch the simulator. This tutorial provides comprehensive guidance on effectively using SwiftData with SwiftUI Preview. You should learn how to create a custom container populated with sample data specifically for preview purposes.

    If you enjoy reading this tutorial and want to learn more about SwiftUI, don’t forget to check out our Mastering SwiftUI book for iOS 17 and Xcode 15.

    Read next