What is the correct way to track the number of items in a relationship using SwiftData and SwiftUI?
Imagine a macOS application with a sidebar that lists Folders
and Tags
. An Item
can belong to a Folder
and have many Tags
. In the sidebar, I want to show the name of the Folder
or Tag
along with the number of Items
in it.
I feel like I'm missing something obvious within SwiftData to wire this up such that my SwiftUI views correctly updated whenever the underlying modelContext
is updated.
// The basic schema
@Model final class Item {
var name = "Untitled Item"
var folder: Folder? = nil
var tags: [Tag] = []
}
@Model final class Folder {
var name = "Untitled Folder"
var items: [Item] = []
}
@Model final class Tag {
var name = "Untitled Tag"
var items: [Item] = []
}
// A SwiftUI view to show a Folder.
struct FolderRowView: View {
let folder: Folder
// Should I use an @Query here??
// @Query var items: [Item]
var body: some View {
HStack {
Text(folder.name)
Spacer()
Text(folder.items.count.formatted())
}
}
}
The above code works, once, but if I then add a new Item
to that Folder
, then this SwiftUI view does not update. I can make it work if I use an @Query
with an #Predicate
but even then I'm not quite sure how the #Predicate is supposed to be written. (And it seems excessive to have an @Query
on every single row, given how many there could be.)
struct FolderView: View {
@Query private var items: [Item]
private var folder: Folder
init(folder: Folder) {
self.folder = folder
// I've read online that this needs to be captured outside the Predicate?
let identifier = folder.persistentModelID
_items = Query(filter: #Predicate { link in
// Is this syntax correct? The results seem inconsistent in my app...
if let folder = link.folder {
return folder.persistentModelID == identifier
} else {
return false
}
})
}
var body: some View {
HStack {
Text(folder.name)
Spacer()
// This mostly works.
Text(links.count.formatted())
}
}
}
As I try to integrate SwiftData and SwiftUI into a traditional macOS app with a sidebar, content view and inspector I'm finding it challenging to understand how to wire everything up.
In this particular example, tracking the count, is there a "correct" way to handle this?