Xcode: 15.1
I've got (simplified) model with relationship many-to-many defined as follows
@Model
final class Item {
var title: String
@Relationship(inverse: \Tag.items) var tags: [Tag]?
init(_ title: String) {
self.title = title
}
}
@Model
final class Tag {
var name: String
var items: [Item]?
init(_ name: String) {
self.name = name
}
}
and a view with a query
struct ItemsView: View {
@Query var items: [Item]
var body: some View {
List {...}
}
init(searchText: String) {
_items = Query(filter: #Predicate<Item> { item in
if (searchText.isEmpty) {
return true
} else {
return item.tags!.contains{$0.name.localizedStandardContains(searchText)}
}
})
}
}
This code compiles but fails at runtime with an error: Query encountered an error: SwiftData.SwiftDataError(_error: SwiftData.SwiftDataError._Error.unsupportedPredicate)
It looks like Predicate does not like optionals cause after changing tags and items to non optionals and the predicate line to
item.tags.contains{$0.name.localizedStandardContains(searchText)}
everything works perfectly fine.
So, my question is, does anybody know how to make it work with optionals?
Full code: https://github.com/m4rqs/PredicateWithOptionals.git
Sorry, I wasn't able to test my answer earlier and was confident that this kind of query worked but I end up with the same error: to-many key not allowed here
.
I tried other alternatives but couldn't find one that worked. Doesn't seem like it's possible to use optional to-many relationships inside a Predicate
(even checking for $0.tags != nil
or similar will crash)
A workaround would be to query on the to-one side of the relationship, i.e Tag
and compute the array of Item
from the results.
It's not ideal but I couldn't find another way. Since it's working for non-optional relationships we can only hope that this will possible in future releases.
struct ItemsView: View {
@Query private var tags: [Tag]
var items: [Item] {
self.tags.lazy.compactMap(\.items).flatMap { $0 }
}
init(searchText: String) {
self._tags = Query(filter: #Predicate<Tag> {
$0.name.localizedStandardContains(search)
})
}
}