Skip to main content

Querying SwiftData objects in SwiftUI

About 3 minSwiftArticle(s)bloghackingwithswift.comcrashcourseswiftswiftdataxcodeappstore

Querying SwiftData objects in SwiftUI 관련

SwiftData by Example

Back to Home

Querying SwiftData objects in SwiftUI | SwiftData by Example

Querying SwiftData objects in SwiftUI

Updated for Xcode 15

Once you've designed your SwiftData models and injected them into your SwiftUI app using the modelContainer() modifier, the next step is to create some SwiftUI code to read out objects of your model and display them somehow.

Start by opening your ContentView.swift file and adding an import SwiftData line at the top to bring in all the SwiftData code we need.

Now add this new property to the ContentView struct:

@Query var destinations: [Destination]

That uses the @Query macro to read all Destination objects being managed by SwiftData. We don't have any destinations yet so that array will be empty, but we'll fix that later.

@Query is really smart: it will load all the destinations immediately when the view appears, but it will also watch the database for changes so that whenever any Destination object gets added, deleted, or changed, the destinations property will also be updated.

Tips

If you've used Core Data previously, this is equivalent to the @FetchRequest property wrapper.

Because all SwiftData model objects automatically conform to the Identifiable protocol, we can immediately write some SwiftUI code to show all our destinations in a list:

NavigationStack {
    List {
        ForEach(destinations) { destination in
            VStack(alignment: .leading) {
                Text(destination.name)
                    .font(.headline)

                Text(destination.date.formatted(date: .long, time: .shortened))
            }
        }
    }
    .navigationTitle("iTour")
}

Tips

We could have used List(destinations) rather than a List plus a ForEach, but we need the ForEach so we can add swipe to delete later.

Like I said earlier, that list will be empty because we haven't created any destinations yet. Obviously we want the user to be able to add their own destinations once the app is finished, but for now we can take a little shortcut and add some sample data.

Put this below the navigationTitle() modifier:

.toolbar {
    Button("Add Samples", action: addSamples)
}

And now add this method for it to run:

func addSamples() {
    let rome = Destination(name: "Rome")
    let florence = Destination(name: "Florence")
    let naples = Destination(name: "Naples")
}

That creates some instances of our Destination model, but Swift will warn us that they aren’t used – we are creating them, but we aren't actually telling SwiftData to store them.

To do that we need to learn an important SwiftData concept called the model context, which has the job of tracking all objects that are currently being used by our app. That's not every object, because it would be terrifically inefficient to load everything all at once. Instead, it's just objects that we're actively using right now.

When we used the modelContainer() modifier earlier that also created a model context for us, and placed that context into SwiftUI’s environment for us to use. This automatic model context always runs on Swift’s main actor, so it’s safe to use from our user interface.

We need to access that model context to add our objects to SwiftData's storage, and we can use SwiftUI’s @Environment property wrapper to get it. Add this property to ContentView now:

@Environment(\.modelContext) var modelContext

And now we can insert our new Destination objects into the model context by adding these three lines at the end of addSamples():

modelContext.insert(rome)
modelContext.insert(florence)
modelContext.insert(naples)

Run the app again then press Add Samples, and you should see the new destinations appear in our list – it works! Even better, if you go back to Xcode and press Cmd+R again to relaunch the app, you’ll see they are still there, because SwiftData automatically saved them for us.

This autosave behavior is enabled by default: as soon as our button code finishes executing SwiftData will save all our changes to its permanent storage, so our data is always safe.


이찬희 (MarkiiimarK)
Never Stop Learning.