BLACK FRIDAY: Save 50% on all my books and bundles! >>

How to use @Query to read SwiftData objects from SwiftUI

Paul Hudson    @twostraws   

Updated for Xcode 16.4

SwiftData provides the @Query macro for querying model objects from a SwiftUI view, optionally providing a sort order, a filter predicate, and either a custom animation or a custom transaction to handle changing results smoothly. Even better, @Query automatically stays up to date every time your data changes, and will reinvoke your SwiftUI view so it stays in sync.

As an example, we could define two related models for a Movie and Director, like this:

@Model
class Director {
    var name: String
    var movies: [Movie]

    init(name: String, movies: [Movie]) {
        self.name = name
        self.movies = movies
    }
}

@Model
class Movie {
    var title: String
    var director: Director

    init(title: String, director: Director) {
        self.title = title
        self.director = director
    }
}

And now we could display a list of all movies, sorted by their title, like this:

struct AuthorsView: View {
    @Query(sort: \Movie.title) var movies: [Movie]

    var body: some View {
        NavigationStack {
            List(movies) { movie in
                Text(movie.title)
                Text("Director: \(movie.director.name)")
            }
            .navigationTitle("Movie Time")
        }
    }
}

As you can see, SwiftData automatically provides us with the director names property, even though that's provided through a relationship. SwiftData loads these relationships lazily – if there's a relationship you don't use, it won't be fetched.

Important: When you use @Query to load SwiftData objects in your view, that query is run immediately when your view is displayed. This means you should be careful to avoid loading large amounts of data, because it might cause your user interface to freeze temporarily.

There are lots of ways of customizing @Query, such as providing a filter using #Predicate. For example, we might write a filter to show only movies by James Cameron:

@Query(filter: #Predicate<Movie> { movie in
    movie.director.name == "James Cameron"
}, sort: \Movie.title) var movies: [Movie]

You can also get more fine-grained control over the sort order, either by asking for it to be reversed:

@Query(sort: \Movie.title, order: .reverse) var movies: [Movie]

Or by providing an array of sort descriptors, where they get applied in order:

@Query(sort: [SortDescriptor(\Movie.title), SortDescriptor(\Movie.releaseYear, order: .reverse)]) var movies: [Movie]

That sorts movies alphabetically by their title, but if any movies have the same name they'll be sorted by their release year descending.

Tip: Your views can have as many @Query properties as you need, although if there's more than three I'd start to wonder if there were a more efficient approach.

For more advanced purposes, you can provide a custom FetchDescriptor that you've configured with extra options such as a fetch limit or offset. This takes a little thinking because some fetch descriptor options are available only after the descriptor is initialized.

I've found the easiest way to do this is using a static property for the descriptor, so I can reference it freely in the query. For example, if I wanted to show the 10 most recent movies I'd use a reverse release year sort plus a fetch limit of 10, like this:

static var descriptor: FetchDescriptor<Movie> {
    var descriptor = FetchDescriptor<Movie>(sortBy: [SortDescriptor(\.releaseYear, order: .reverse)])
    descriptor.fetchLimit = 10
    return descriptor
}

@Query(descriptor) var latestMovies: [Movie]

Important: Regardless of how it's created, the @Query macro works only inside SwiftUI views. Swift won't stop you using it elsewhere, it just won't work!

Save 50% in my Black Friday sale.

SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further for less! Get my all-new book Everything but the Code to make more money with apps, get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn Swift Testing, design patterns, and more.

Save 50% on all our books and bundles!

BUY OUR BOOKS
Buy Everything but the Code Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Interview Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.4/5

 
Unknown user

You are not logged in

Log in or create account