I have a class that I would like to use to populate a self-nesting tree style list. Here is the basic definition:
final class Item : Hashable, Codable, Identifiable {
var name: String
var brand: String?
var liked: Bool?
var notes: String?
var children: [Item]?
init(categoryName: String, items: [Item]?) {
self.name = categoryName
if let items {
self.children? = []
self.children!.insert(contentsOf: items, at: 0)
}
}
init(productName: String, liked: Bool, notes: String) {
self.name = productName
self.brand = ""
self.liked = liked
self.notes = notes
}
init(productName: String, brand: String, liked: Bool, notes: String) {
self.name = productName
self.brand = brand
self.liked = liked
self.notes = notes
}
}
Even though the goal is to eventually pull this data from JSON or CoreData or something (I'm VERY new), for the purposes of testing, I have manually built some data like so:
let item1 = Item(productName: "Crispy Honey Shrimp", brand: "P.F. Changs", liked: true, notes: "Goold flavor and texture")
let item2 = Item(productName: "Bourbon Chicken Pasta (bag)", brand: "Zatarain's", liked: true, notes: "Tasty, easy to make")
let item3 = Item(productName: "Bourbon CHicken Pasta (box)", brand: "Zatarain's", liked: false, notes: "Wrong pasta, bad sauce")
let item4 = Item(productName: "Fries", liked: true, notes: "Well seasoned. Cronch")
let item5 = Item(productName: "Texas Cheesesteak Plate", liked: true, notes: "")
let cat7 = Item(categoryName: "Checker's", items: [item4])
let cat6 = Item(categoryName: "Waffle House", items: [item5])
let cat5 = Item(categoryName: "Fast Food", items: [cat7])
let cat4 = Item(categoryName: "Dine-In", items: [cat6])
let cat3 = Item(categoryName: "Restaraunts", items: [cat4, cat5])
let cat2 = Item(categoryName: "Frozen Food", items: [item1, item2, item3])
let cat1 = Item(categoryName: "Grocery", items: [cat2])
let items = [cat1, cat3]
Finally, the view looks like this:
struct ItemList: View {
var body: some View {
List(items, children: \.children) { row in
HStack {
Text(row.name)
}
}
}
}
The simulator crashes, and it seems to have difficulties with the data model? I guess? Its difficult for me to parse. A pastebin of the crash report is at https://pastebin.com/mpXdcQ0a
I'm a C# dev, and SwiftUI is a VERY new paradigm for me. And I'm definitely struggling. If I am obviously going wrong somewhere - even if its just advice, pointing out anti-patterns, not actual answers to the question - I welcome any knowledge. Thanks.
Try this basic approach in SwiftUI, using struct Item
, with a custom id
to make it Identifiable
. Also using a @State var items: [Item] = []
in the View
and setting its data in .onAppear{...}
struct Item : Hashable, Codable, Identifiable { // <--- here
let id = UUID() // <--- here
var name: String
var brand: String?
var liked: Bool?
var notes: String?
var children: [Item]?
enum CodingKeys: String, CodingKey { // <--- here, note no id
case name,brand,liked,notes,children
}
init(categoryName: String, items: [Item]?) {
self.name = categoryName
if let items {
self.children = []
self.children?.append(contentsOf: items) // <--- here
}
}
init(productName: String, liked: Bool, notes: String) {
self.name = productName
self.brand = ""
self.liked = liked
self.notes = notes
}
init(productName: String, brand: String, liked: Bool, notes: String) {
self.name = productName
self.brand = brand
self.liked = liked
self.notes = notes
}
}
struct ContentView: View {
@State var items: [Item] = [] // <--- here
var body: some View {
List(items, children: \.children) { row in
HStack {
Text(row.name)
}
}
.onAppear {
let item1 = Item(productName: "Crispy Honey Shrimp", brand: "P.F. Changs", liked: true, notes: "Goold flavor and texture")
let item2 = Item(productName: "Bourbon Chicken Pasta (bag)", brand: "Zatarain's", liked: true, notes: "Tasty, easy to make")
let item3 = Item(productName: "Bourbon CHicken Pasta (box)", brand: "Zatarain's", liked: false, notes: "Wrong pasta, bad sauce")
let item4 = Item(productName: "Fries", liked: true, notes: "Well seasoned. Cronch")
let item5 = Item(productName: "Texas Cheesesteak Plate", liked: true, notes: "")
let cat7 = Item(categoryName: "Checker's", items: [item4])
let cat6 = Item(categoryName: "Waffle House", items: [item5])
let cat5 = Item(categoryName: "Fast Food", items: [cat7])
let cat4 = Item(categoryName: "Dine-In", items: [cat6])
let cat3 = Item(categoryName: "Restaraunts", items: [cat4, cat5])
let cat2 = Item(categoryName: "Frozen Food", items: [item1, item2, item3])
let cat1 = Item(categoryName: "Grocery", items: [cat2])
items = [cat1, cat3] // <--- here
}
}
}
Once you start using persistence, for example SwiftData, you will have to adjust the code, such as:
@Model class Item: Codable, Identifiable { ... }
and in the View
:
@Query(sort: \.name, order: .reverse) var items: [Item]