I'm building a View right now, where I try to get several DisclosureGroup with ForEach based on categories. This was the easy part. My problem is the second Array, where I want to fetch items with said category.
I am new to the game and still learning, but I can't figure out how to do it.
Code here:
@Environment(\.modelContext) var context
@Query private var category: [CategoryModel]
@Query private var bonsai: [BonsaiModel]
@State private var searchQuery = ""
var body: some View {
NavigationStack {
VStack {
}
VStack(alignment: .center) {
List {
Section {
// Query category to build DisclosureGroup
ForEach(category) { categories in // Loop through category
DisclosureGroup(categories.name) {
ForEach(bonsai) { items in // Loop through bonsai, where bonsai.category = category
Text(items.name)
.bold()
if let category = items.category {
Text(category.name) // show category.title
.foregroundColor(Color(.systemGray))
.bold()
} else {
Text(" ")
.padding(.top, 4)
}
}
}
}
}
}
.padding(.horizontal, 8)
.padding(.top, 8)
.listStyle(.inset)
I have put two Text-Items in it, to see what data I try to fetch in the "bonsai" ForEach.
Here is a picture, to visualize what I am trying to accomplish:
Right now you see everything, but you can see the categories which should used as a "filter" and only show in their Group.
My guess is, that it will work with compactMap or filter, but I have no clue how I get it to work.
Model here:
@Model
final class BonsaiModel {
var uuid: UUID
var timestamp: Date
var purchased: Date
var price: Double
var name: String
var isFavorite: Bool
var age: String
@Attribute(.externalStorage)
var bonsaiImageData: Data?
@Relationship(deleteRule: .nullify, inverse: \CategoryModel.bonsai)
var category: CategoryModel?
@Relationship(deleteRule: .nullify, inverse: \SpeciesModel.bonsai)
var species: SpeciesModel?
init(price: Double = 0.00,
name: String = "",
isFavorite: Bool = false,
age: String = "",
bonsaiImageData: Data? = nil
) {
self.uuid = UUID()
self.timestamp = Date()
self.purchased = Date()
self.price = price
self.name = name
self.isFavorite = isFavorite
self.age = age
self.bonsaiImageData = bonsaiImageData
}
}
@Model
final class CategoryModel {
@Attribute(.unique) var name: String = ""
var bonsai: [BonsaiModel]?
init(name: String) {
self.name = name
}
}
@Model
final class SpeciesModel {
@Attribute(.unique) var name: String = ""
var bonsai: [BonsaiModel]?
init(name: String) {
self.name = name
}
}
Given your model you can directly access the relationship property for the BonsaiModel in CategoryModel, that way you only get the objects that belongs to the current CategoryModel and do not need to perform any filtering or matching of objects.
ForEach(category) { categories in
DisclosureGroup(categories.name) {
ForEach(categories.bonsai ?? []) { items in
Text(items.name)
//...
}
}
}
Side note: I would suggest making the to-many side of the relationship none-optional
var bonsai: [BonsaiModel] = []
also with this solution you can remove the query for BonsaiModel from your view