DocumentModel
contains tags relationship to TagModel
. A View receives selectedTag. Predicate (as part of @Query) should filter documents that have given tag.
let selectedTag = TagModel()
#Predicate<DocumentModel> { document in
document.tags.contains {
$0.tagID == selectedTag.tagID
}
}
Predicate doesn't build because: Cannot convert to closure result type 'any StandardPredicateExpression'.
Predicate macro expands to:
Foundation.Predicate<DocumentModel>({ document in
PredicateExpressions.build_contains(
PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(document),
keyPath: \.tags
)
) {
PredicateExpressions.build_Equal(
lhs: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg($0),
keyPath: \.tagID
),
rhs: PredicateExpressions.build_KeyPath(
root: PredicateExpressions.build_Arg(selectedTag),
keyPath: \.tagID
)
)
}
})
Full error message:
Cannot convert value of type 'PredicateExpressions.SequenceContainsWhere<PredicateExpressions.KeyPath<PredicateExpressions.Variable, [TagModel]>, PredicateExpressions.Equal<PredicateExpressions.KeyPath<PredicateExpressions.Variable, String>, PredicateExpressions.KeyPath<PredicateExpressions.Value, String>>>' to closure result type 'any StandardPredicateExpression'
Apple Developer Documentation has example code using contains()
and comparing strings:
let messagePredicate = #Predicate<Message> { message in
message.recipients.contains {
$0.firstName == message.sender.firstName
}
}
How to filter documents that contain selected tag?
contains
is not the problem here. The problem is selectedTag.tagID
.
You should not access properties of anything else other than document
(the parameter of the closure passed to #Predicate
). While #Predicate
technically supports this (i.e. the macro can expand to some result), SwiftData doesn't. SwiftData can only work with StandardPredicateExpression
, which only some PredicateExpression
s are.
Instead of having the closure capture selectedTag
, make the closure capture just selectedTag.tagID
struct SomeView: View {
let selectedTag: TagModel
@Query var documents: [DocumentModel]
init(selectedTag: TagModel) {
self.selectedTag = selectedTag
let selectedTagID = selectedTag.tagID
_documents = Query(filter: #Predicate<DocumentModel> { document in
document.tags.contains {
$0.tagID == selectedTagID
}
})
}
...
}
You can also write selectedTagID = selectedTag.tagID
directly in the closure's capture list:
_documents = Query(filter: #Predicate<DocumentModel> { [selectedTagID = selectedTag.tagID] document in
document.tags.contains {
$0.tagID == selectedTagID
}
})