I am looking to update an item in a nested array of items, for showing the issue I made this example code below, the point of this question is updating nested item and not SwiftUI tricks. i want find an item with knowing its id and after finding it trying to updated. In codes bellow I showed you the example for updating items in first layer, but as long as layers go deeper we cannot guess it can be 2 layer or 10 layer, we do not know. How can I be able to update the item1, item2 and item4?
import SwiftUI
@main
struct findItem_testApp: App {
var body: some Scene {
WindowGroup {
ContentView().environmentObject(ItemModel.shared)
}
}
}
struct ContentView: View {
@EnvironmentObject var itemModel: ItemModel
var body: some View {
VStack {
MyView(item: itemModel.items[0])
MyView(item: itemModel.items[1].nestedItems[0].nestedItems[0])
MyView(item: itemModel.items[1].nestedItems[0])
MyView(item: itemModel.items[1])
MyView(item: itemModel.items[2].nestedItems[0])
MyView(item: itemModel.items[2])
}
}
}
struct MyView: View {
let item: ItemType
var body: some View {
Text(item.name)
.bold()
.padding()
.onTapGesture {
print(item.name, item.id)
// need to update item.name from model
ItemModel.shared.updateName(id: item.id, value: item.name + " updated!")
}
}
}
struct ItemType: Identifiable {
let id: UUID
var name: String
var nestedItems: [ItemType]
init(name: String, nestedItems: [ItemType]) {
self.id = UUID()
self.name = name
self.nestedItems = nestedItems
}
}
let item0: ItemType = ItemType(name: "item0", nestedItems: [])
let item1: ItemType = ItemType(name: "item1", nestedItems: [])
let item2: ItemType = ItemType(name: "item2", nestedItems: [item1])
let item3: ItemType = ItemType(name: "item3", nestedItems: [item2])
let item4: ItemType = ItemType(name: "item4", nestedItems: [])
let item5: ItemType = ItemType(name: "item5", nestedItems: [item4])
class ItemModel: ObservableObject {
static let shared: ItemModel = ItemModel()
@Published var items: [ItemType] = [ItemType]()
init() {
self.items = [item0, item3, item5]
}
func updateName(id: UUID, value: String) {
for i in (0 ..< self.items.count) {
if (self.items[i].id == id) {
self.items[i].name = value
break
}
}
}
}
How about a recursive approach:
import Foundation
struct ItemType: Identifiable {
let id: UUID
var name: String
var nestedItems: [ItemType]
init(name: String, nestedItems: [ItemType] = []) {
self.id = UUID()
self.name = name
self.nestedItems = nestedItems
}
}
class ItemModel {
var items: [ItemType]
init(items: [ItemType]) {
self.items = items
}
func updateName(id: UUID, newName: String) {
updateNameRecursively(id: id, newName: newName, items: &self.items)
}
private func updateNameRecursively(id: UUID, newName: String, items: inout [ItemType]) {
for i in 0..<items.count {
if items[i].id == id {
items[i].name = newName
return
} else {
updateNameRecursively(id: id, newName: newName, items: &items[i].nestedItems)
}
}
}
}
extension ItemModel {
func prettyPrintItems(items: [ItemType], indentLevel: Int = 0) {
let indent = String(repeating: " ", count: indentLevel)
for item in items {
print("\(indent)- \(item.name) (id: \(item.id))")
prettyPrintItems(items: item.nestedItems, indentLevel: indentLevel + 1)
}
}
}
let item0 = ItemType(name: "item0")
let item1 = ItemType(name: "item1")
let item2 = ItemType(name: "item2", nestedItems: [item1])
let item3 = ItemType(name: "item3", nestedItems: [item2])
let item4 = ItemType(name: "item4")
let item5 = ItemType(name: "item5", nestedItems: [item4])
let model = ItemModel(items: [item0, item3, item5])
print("Before:")
model.prettyPrintItems(items: model.items)
model.updateName(id: item1.id, newName: "item1 updated!")
model.updateName(id: item2.id, newName: "item2 updated!")
model.updateName(id: item4.id, newName: "item4 updated!")
print("After:")
model.prettyPrintItems(items: model.items)
If you would like to use a dictionary instead of an array for the nestedItems
:
import Foundation
struct ItemType: Identifiable {
let id: UUID
var name: String
var nestedItems: [String: ItemType]
init(name: String, nestedItems: [String: ItemType] = [:]) {
self.id = UUID()
self.name = name
self.nestedItems = nestedItems
}
}
class ItemModel {
var items: [String: ItemType]
init(items: [ItemType]) {
self.items = Dictionary(uniqueKeysWithValues: items.map { ($0.id.uuidString, $0) })
}
func updateName(id: UUID, newName: String) {
updateNameRecursively(id: id.uuidString, newName: newName, items: &self.items)
}
private func updateNameRecursively(id: String, newName: String, items: inout [String: ItemType]) {
if let item = items[id] {
items[id]?.name = newName
} else {
for key in items.keys {
updateNameRecursively(id: id, newName: newName, items: &items[key]!.nestedItems)
}
}
}
}
extension ItemModel {
func prettyPrintItems(items: [String: ItemType], indentLevel: Int = 0) {
let indent = String(repeating: " ", count: indentLevel)
for item in items.values {
print("\(indent)- \(item.name) (id: \(item.id))")
prettyPrintItems(items: item.nestedItems, indentLevel: indentLevel + 1)
}
}
}
let item0 = ItemType(name: "item0")
let item1 = ItemType(name: "item1")
let item2 = ItemType(name: "item2", nestedItems: [item1.id.uuidString: item1])
let item3 = ItemType(name: "item3", nestedItems: [item2.id.uuidString: item2])
let item4 = ItemType(name: "item4")
let item5 = ItemType(name: "item5", nestedItems: [item4.id.uuidString: item4])
let model = ItemModel(items: [item0, item3, item5])
print("Before:")
model.prettyPrintItems(items: model.items)
model.updateName(id: item1.id, newName: "item1 updated!")
model.updateName(id: item2.id, newName: "item2 updated!")
model.updateName(id: item4.id, newName: "item4 updated!")
print("After:")
model.prettyPrintItems(items: model.items)
Example Output:
Before:
- item0 (id: CE42EA37-3841-4D4C-931D-0611FC80E1F0)
- item3 (id: 0EADCD5E-C24B-495A-9581-97DEC427B859)
- item2 (id: 44103D4A-0871-4235-B895-4AD16C3AABB0)
- item1 (id: 0412FC6B-B5A4-4AFB-8BF4-C4569EBA16E7)
- item5 (id: 4C396B6A-1FA7-4E7B-AE6C-EE4B10395AB6)
- item4 (id: 5CA53EA0-D229-429C-812E-A364EE725B89)
After:
- item0 (id: CE42EA37-3841-4D4C-931D-0611FC80E1F0)
- item3 (id: 0EADCD5E-C24B-495A-9581-97DEC427B859)
- item2 updated! (id: 44103D4A-0871-4235-B895-4AD16C3AABB0)
- item1 updated! (id: 0412FC6B-B5A4-4AFB-8BF4-C4569EBA16E7)
- item5 (id: 4C396B6A-1FA7-4E7B-AE6C-EE4B10395AB6)
- item4 updated! (id: 5CA53EA0-D229-429C-812E-A364EE725B89)