Predicate to filter against array of items

How can I filter an item based on an array using a Predicate
I am having the following SwiftData models:

@Model
class Place {
...
  @Relationship(inverse: \Tag.places) var tags: [Tag]
...
}

and

@Model
class Tag {
...
  @Attribute(.unique) var uuid: UUID
  @Relationship(deleteRule: .nullify) private(set) var places: [Place]
...
}

I want to filter the Place model based on its tags to match against any provided tags
for example I want all places that have any of [work, restaurants] tags

what I did is:

let tags: [Tag] = ...
#Predicate<Place> { place in
  place.tags.allSatisfy { tag in
    tags.contains(where: { $0.uuid == tag.uuid })
  }
}

I am getting this error:

Cannot convert value of type 'PredicateExpressions.SequenceAllSatisfy<PredicateExpressions.KeyPath<PredicateExpressions.Variable, [Tag]>, PredicateExpressions.SequenceContainsWhere<PredicateExpressions.Value<[Tag]>, PredicateExpressions.Equal<PredicateExpressions.KeyPath<PredicateExpressions.Variable, UUID>, PredicateExpressions.KeyPath<PredicateExpressions.Variable, UUID>>>>' to closure result type 'any StandardPredicateExpression'

Any thoughts on how can I make it work?

Since Predicate is both Codable and Sendable, it requires that everything the predicate captures (i.e. all instances captured by the closure) are also Codable and Sendable. In your case, your predicate is capturing tags which has the type [Tag], and it looks like from the declaration of Tag that it is neither Codable nor Sendable, which is likely what the compiler is trying to tell you here.

To resolve this, can you capture the list of UUIDs instead (since UUID is Codable & Sendable)? For example:

let tags: [Tag] = ...
let uuids = tags.map(\.uuid)
#Predicate<Place> { place in
  place.tags.allSatisfy { tag in
    uuids.contains(tag.uuid)
  }
}
2 Likes

Whoah I had no Idea Predicate captures should be Codable!
Thank you for your help, this solves the problem :pray:t2:

1 Like