Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

SwiftData: filtering against an array of PersistentIdentifiers

I would like to have a SwiftData predicate that filters against an array of PersistentIdentifiers.

A trivial use case could filtering Posts by one or more Categories. This sounds like something that must be trivial to do.

When doing the following, however:

let categoryIds: [PersistentIdentifier] = categoryFilter.map { $0.id }
let pred = #Predicate<Post> {
    if let catId = $0.category?.persistentModelID {
         return categoryIds.contains(catId)
    } else {
         return false
    }
}

The code compiles, but produces the following runtime exception (XCode 26 beta, iOS 26 simulator):

'NSInvalidArgumentException', reason: 'unimplemented SQL generation for predicate : (TERNARY(item != nil, item, nil) IN {}) (bad LHS)'

Strangely, the same code works if the array to filter against is an array of a primitive type, e.g. String or Int.

What is going wrong here and what could be a possible workaround?

I don’t think Apple stores the persistent identifier in the database, only just the primary key.

One possible reason why primitive types only work with arrays would be because the store their working with (SQLite) only support those types.

Custom structs can be stored, but IIRC you can only query them if they are stored as JSON strings. But if you’re just passing an array it is just mapping those bindings that were laid out in the SQL/predicate.

I did notice that when you use model(for:) it creates a predicate for persistent identifiers as a Set, so they probably have a switch of primitive collection types supported and some for other internal use cases.

You could just use a unique ID string from your model and pass the entity’s name.

Why not use the objects (Category) directly in the predicate since they conform to Equatable?

The PersistentIdentifier is basically an abstraction over the DB primary key, it's a Struct consisting of the storeId, the entity name, and the primary key. You can normally use PersistentIdentifiers in SwiftData predicates, so you can do { post in post.persistentModelID == aModelIdIAmLookingFor }

I cannot use the Category directly in the predicate, since it is an Optional. SwiftData predicates have problems comparing Optionals, such things only tend to work if you provide a default value before the comparison, e.g. (optionalString ?? "") == someNonOptionalString.

After some debugging, I have discovered that what is causing the issue, is not the PersistentIdentifier itself, but the if-let clause with the PersistentIdentifier. Thus, performing the optional binding with Category instead of the PersistentIdentifier makes it work:

if let cat = $0.category {
    return categoryIds.contains(cat.persistentModelID)
}
SwiftData: filtering against an array of PersistentIdentifiers
 
 
Q