Как обновить нужный элемент внутри вложенного элемента массива?

Я хочу обновить элемент во вложенном массиве элементов. Чтобы показать проблему, которую я создал в этом примере кода ниже, суть этого вопроса заключается в обновлении вложенного элемента, а не в трюках SwiftUI. я хочу найти элемент, зная его идентификатор, и после его обнаружения пытаюсь обновить. В кодах ниже я показал вам пример обновления элементов в первом слое, но пока уровни идут глубже, мы не можем угадать, может ли это быть 2-й или 10-й уровень, мы не знаем. Как я могу обновить элемент1, элемент2 и элемент4?

    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
                }
    
            }
     
        }
    
    }

1
50
1

Ответ:

Решено

Как насчет рекурсивного подхода:

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)

Если вы хотите использовать словарь вместо массива для 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)

Пример вывода:

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)