How to reset child view state variable

I'm sure it's something very silly but how should one reset the state value of a child view when another state has changed?


For example, the code below shows 2 folders, which respectively have 2 and 3 items., which can be edited.


If you select the second folder (Work) and its 3rd item (Peter) and then select the first folder (Home), the app crashes since `selectedItemIndex` is out of bounds.


I tried to "reset" the state value when the view gets initialized but it seems like changing the state like such triggers out a "runtime: SwiftUI: Modifying state during view update, this will cause undefined behavior." warning.


init(items: Binding<[Item]>) {
        self._items = items
        self._selectedItemIndex = State(wrappedValue: 0)
    }


What is the proper way to do this? Thanks!


Here's the code:


AppDelegate.swift


import Cocoa
import SwiftUI

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    var window: NSWindow!


    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Create the SwiftUI view that provides the window contents.
        let store = ItemStore()
        let contentView = ContentView(store: store)

        // Create the window and set the content view. 
        window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered, defer: false)
        window.center()
        window.setFrameAutosaveName("Main Window")
        window.contentView = NSHostingView(rootView: contentView)
        window.makeKeyAndOrderFront(nil)
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }
}


ContentView.swift


import SwiftUI

final class ItemStore: ObservableObject {
    @Published var data: [Folder] = [Folder(name: "Home",
                                            items: [Item(name: "Mark"), Item(name: "Vincent")]),
                                     Folder(name: "Work",
                                            items:[Item(name: "Joseph"), Item(name: "Phil"), Item(name: "Peter")])]
}

struct Folder: Identifiable {
    var id = UUID()
    var name: String
    var items: [Item]
}

struct Item: Identifiable {
    static func == (lhs: Item, rhs: Item) -> Bool {
        return true
    }
    
    var id = UUID()
    var name: String
    var content = Date().description
    
    init(name: String) {
        self.name = name
    }
}

struct ContentView: View {
    @ObservedObject var store: ItemStore
    
    @State var selectedFolderIndex: Int?
    
    var body: some View {
        HSplitView {
            // FOLDERS
            List(selection: $selectedFolderIndex) {
                Section(header: Text("Groups")) {
                    ForEach(store.data.indexed(), id: \.1.id) { index, folder in
                        Text(folder.name).tag(index)
                    }
                }.collapsible(false)
            }
            .listStyle(SidebarListStyle())
            
            // ITEMS
            if selectedFolderIndex != nil {
                ItemsView(items: $store.data[selectedFolderIndex!].items)
            }
        }
        .frame(minWidth: 800, maxWidth: .infinity, maxHeight: .infinity)
    }
}


struct ItemsView: View {
    @Binding var items: [Item]
    @State var selectedItemIndex: Int?
    
    var body: some View {
        HSplitView {
            List(selection: $selectedItemIndex) {
                ForEach(items.indexed(), id: \.1.id) { index, item in
                    Text(item.name).tag(index)
                }
            }
            .frame(width: 300)
            
            if selectedItemIndex != nil {
                DetailView(item: $items[selectedItemIndex!])
                .padding()
                .frame(minWidth: 200, maxHeight: .infinity)
            }
        }
    }
}


struct DetailView: View {
    @Binding var item: Item
    
    var body: some View {
        VStack {
            TextField("", text: $item.name)
        }
    }
}

struct IndexedCollection<Base: RandomAccessCollection>: RandomAccessCollection {
    typealias Index = Base.Index
    typealias Element = (index: Index, element: Base.Element)

    let base: Base

    var startIndex: Index { base.startIndex }

    var endIndex: Index { base.endIndex }

    func index(after i: Index) -> Index {
        base.index(after: i)
    }

    func index(before i: Index) -> Index {
        base.index(before: i)
    }

    func index(_ i: Index, offsetBy distance: Int) -> Index {
        base.index(i, offsetBy: distance)
    }

    subscript(position: Index) -> Element {
        (index: position, element: base[position])
    }
}

extension RandomAccessCollection {
    func indexed() -> IndexedCollection<Self> {
        IndexedCollection(base: self)
    }
}

Thanks to @jordanpittman for suggesting a fix:


ItemsView(items: $store.data[selectedFolderIndex!].items).id(selectedRowIndex)


Source: https://swiftui-lab.com/swiftui-id

How to reset child view state variable
 
 
Q