Skip to main content

How to sort SwiftData queries using key paths or SortDescriptor

About 2 minSwiftArticle(s)bloghackingwithswift.comcrashcourseswiftswiftdataxcodeappstore

How to sort SwiftData queries using key paths or SortDescriptor 관련

SwiftData by Example

Back to Home

How to sort SwiftData queries using key paths or SortDescriptor | SwiftData by Example

How to sort SwiftData queries using key paths or SortDescriptor

Updated for Xcode 15

Sorting SwiftData queries is either done with a key path for simple sorts, or an array of SortDescriptor for more complex sorts. Some query variants – e.g. creating a FetchDescriptor by hand – only support the array approach, whereas using @Query supports both.

For the simplest approach using @Query, you can specify your sort order as a key path on the model you’re querying. So, if we had a Movie model, we could load all movies sorted alphabetically by their name like this:

@Query(sort: \Movie.name) var movies: [Movie]

And if you wanted a descending alphabetical sort, we’d use this:

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

You can even dig into relationship data, for example sorting by the name of a movie’s director:

@Query(sort: \Movie.director.name) var movies: [Movie]

This sort key path can be any directly comparable property of your model, so we could sort by movie name, movie release date, director age, or anything similar.

Important

Although you can make your models conform to Comparable and use them in these sort orders, it will not work as intended. Behind the scenes SwiftData will insert your model to its own table with a primary key integer, then sort by that integer – you’re effectively sorting by when each object was inserted into your context.

To get more advanced sorting, you can specify an array of SortDescriptor. This approach works much the same with both @Query and FetchDescriptor, which does make life a little easier. The advantage of this approach is that you can specify multiple sort orders to have them applied in order: if the first two fields sort the same for two objects, then they are sorted by the second field, then the third, and so on.

For example, we could sort a list of movies alphabetically, then release date reverse, then finally by director name:

@Query(sort: [SortDescriptor(\name), SortDescriptor(\Movie.releaseDate, order: .reverse), SortDescriptor(\Movie.director.name)]) var movies: [Movie]

There are lots of movies called “The Awakening” (over 30!), and it’s not impossible to have two movies with the same name and same release date, but it would be supremely unlikely to have name, release date, and director name all be the same.

As I said, this same approach works great with a standalone FetchDescriptor, although here the type inference for key paths works a little better so we can skip the model name:

var descriptor = FetchDescriptor<Movie>(sortBy: [SortDescriptor(\.name)])
let results = (try? modelContext.fetch(descriptor)) ?? []

이찬희 (MarkiiimarK)
Never Stop Learning.