SwiftData Query and optional relationships

Xcode 16.2 (16C5032a)

FB16300857

Consider the following SwiftData model objects (only the relevant portions are shown) (note that all relationships are optional because eventually this app will use CloudKit):

@Model
final public class Team {
    public var animal: Animal?
    public var handlers: [Handler]?
    ...
}
@Model
final public class Animal {
    public var callName: String
    public var familyName: String
    @Relationship(inverse: \Team.animal) public var teams: [Team]?
    ...
}
@Model
final public class Handler {
    public var givenName: String
    @Relationship(inverse: \Team.handlers) public var teams: [Team]?
}

Now I want to display Team records in a list view, sorted by animal.familyName, animal.callName, and handlers.first.givenName.

The following code crashes:

struct TeamListView: View {
    @Query<Team>(sort: [SortDescriptor(\Team.animal?.familyName),
        SortDescriptor(\Team.animal?.callName), 
        SortDescriptor(\Team.handlers?.first?.givenName)]) var teams : [Team]
    
    var body: some View {
        List {
            ForEach(teams) { team in 
                ...
            }
        }
    }
}

However, if I remove the sort clause from the @Query and do the sort explicitly, the code appears to work (at least in preliminary testing):

struct TeamListView: View {
    @Query<Team> var teams: [Team]
    
    var body: some View {
        let sortedTeams = sortResults()
        List {
            ForEach(sortedTeams) { team in 
                ...
            }
        }
    }
    
    private func sortResults() -> [Team] {
        let results: [Team] = teams.sorted { team1, team2 in 
            let fam1 = team1.animal?.familyName ?? ""
            let fam2 = team2.animal?.familyName ?? ""
            let comp1 = fam1.localizedCaseInsensitiveCompare(fam2)
            if comp1 == .orderedAscending { return true }
            if comp1 == .orderedDescending { return false }
            ... <proceed to callName and (if necessary) handler givenName comparisons> ...
        }
    }
}

While I obviously have a workaround, this is (in my mind) a serious weakness in the implementation of the Query macro.

It makes no sense to sort against handlers.first (or at all against a to-many relationship) so I would remove this requirement

While I suppose you're right (I keep forgetting that a SwiftData "array" is really just a representation of an unordered Set), it is not what is causing the main issue of this post. If I remove that SoreDescriptor, I still have the crash.

it is not what is causing the main issue of this post. If I remove that SoreDescriptor, I still have the crash.

Did you mean that the following query still triggers a crash? If yes, that doesn't happen to me on my iOS 18.2.1 device. Using a key path containing optional is fine.

@Query<Team>(sort: [
    SortDescriptor(\Team.animal?.familyName),
    SortDescriptor(\Team.animal?.callName)
]) var teams : [Team]

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

I am pretty sure OP meant this particular sort descriptor

Ziqiao Chen,

Well, I really thought I'd tried that, but decided to try again so that I could answer you properly. My results have two cases:

(1) In Preview, where I explicitly populate Teams with one record, the Preview crashes.

(2) In Simulator (simulating iPadOS 18.0), where I do not yet have the code installed to create a new record (and hence have no records for the query to find), the app does not crash.

Obviously, the next step is to add "New Teams" logic so I can test this issue further. I'll report again once I've added that logic.

SwiftData Query and optional relationships
 
 
Q