I have a SwiftData Model like this:
@Model class LogInfo {
var tagValue: String
...
}
I want to display a list of all LogInfo objects for which the tagValue is contained within an array. The SwiftUI view looks like this:
struct LogListView: View {
@Query
private var logs: [LogInfo]
init(tagsToFilter: [LogTag]) {
_logs = Query(filter: #Predicate<LogInfo> { logInfo in
if tagsToFilter.isEmpty || tagsToFilter.contains(where: { tag in
tag.rawValue == logInfo.tagRawValue
}) {
true
} else {
false
}
})
}
The tagsToFilter
argument will be all the tags selected by the user (multi selection).
The LogTag
object is an enum, declared this way:
public enum LogTag: String, Codable, CaseIterable, Hashable, Identifiable
Because the query won't let me check against the enum directly without crashing, I've "doubled" the information with the rawValue. So I'm creating the LogListView like this: LogListView(tagsToFilter: [.case1, .case2])
. I want the list to display every LogInfo object which have either tag's raw value "case1" or "case2" in this example.
The Query works when I have an empty list, or if I remove the query altogether. But the predicate doesn't filter anything. I tried a simpler predicate, like:
#Predicate<LogInfo> { logInfo in
tagsToFilter.contains(where: { tag in
tag.rawValue == logInfo.tagRawValue
}
}
but this returns an empty list.
Am I holding it wrong, or is another SwiftData's limitation?
Try this approach using a separate Predicate
and tagStrings
instead of trying to use a dynamic Query with everything inside it.
Example code.
struct LogListView: View {
@Environment(\.modelContext) private var modelContext
@Query private var logs: [LogInfo]
init(tagsToFilter: [LogTag]) {
let tagStrings = tagsToFilter.map { $0.rawValue }
let predicate = #Predicate<LogInfo> { log in
tagStrings.contains(log.tagValue)
}
_logs = Query(filter: predicate)
}
var body: some View {
List(logs) { log in
Text(log.name) // <-- adjust as required
}
}
}
enum LogTag: String, Codable, CaseIterable {
case case1, case2, case3, case4
}