title: SwiftUI state over multiple views
body: <p>In SwiftUI handling the state of an app first seemed to be quite simple. There are essentially two property wrappers: <code>@EnvironmentObject</code> and <code>@ObservedObject</code> (and <code>@State</code>). But it turned out that it isn't that simple at all.</p>
  <p>I posted several questions here. Most of them have been answered very well:
  <a href="https://stackoverflow.com/questions/62948754/swiftui-ondelete-list-with-toggle">SwiftUI onDelete List with Toggle</a>, <a href="https://stackoverflow.com/questions/62909572/swiftui-foreach-with-indices-does-not-update-after-ondelete">SwiftUI ForEach with .indices() does not update after onDelete</a>, <a href="https://stackoverflow.com/questions/62851218/swiftui-index-out-of-range-when-deleting-cells-with-toggle">SwiftUI: Index out of range when deleting cells with toggle</a></p>
  <p>But every time I go a step further, some principal problems stay. These are</p>
  <ol>
  <li>Hand over a bindable variable via <code>NavigationLink</code> in a <code>List</code> or a <code>ForEach</code> to a SubView and reflecting changes back to the MainView. (I tried several solutions like working with <code>.indices</code> on the array and make a bindable on the fly (<a href="https://stackoverflow.com/questions/62948754/swiftui-ondelete-list-with-toggle">SwiftUI onDelete List with Toggle</a>)).
  To reflect changes back I also tried to work with <code>.onReceive()</code> which also didn't work.</li>
  <li>I always run into index-problems when I try to work with this formula: <code>someArray.firstIndex(where {condition})!</code>. You can find this in the Apple tutorials for SwiftUI. I also run into problems, when I use indexes in a <code>ForEach</code>-closure and then try to swipe-delete with <code>.onDelete()</code>. The error message is:</li>
  </ol>
  <pre><code>Thread 1: Fatal error: Index out of range
  </code></pre>
  <ol start="3">
  <li>How can I bind variables to an <code>@EnvironmentObject</code>-model that are two or more layers behind the mainView? Working with the formula in 2. produces very long variable-names. This doesn't seem to be right.</li>
  </ol>
  <p>Here is one example, which nearly works. The only thing that does not work is updating the <code>MainView</code> when I change values in the <code>SubViews</code>. But it doesn't feel right for me to use several <code>ObservedObjects</code> in my app.</p>
  <pre><code>class Model: ObservableObject {
      @Published var items: [Item]
      
      init(name: String, items: [Item]) {
          self.items = items
      }   
  }
  
  class Item: Identifiable, ObservableObject {
      var id = UUID()
      var itemName: String
      @Published var subItems: [SubItem]
      
      init(itemName: String, payments: [SubItem]) {
          self.itemName = itemName
          self.subItems = payments
      }
  }
  
  class SubItem: Identifiable, ObservableObject {
      var id = UUID()
      var isOn: Bool
      
      init(isOn: Bool) {
          self.isOn = isOn
      }
  }
  
  
  struct MainView: View {
      @EnvironmentObject var model: Model
      
      var body: some View {
          NavigationView {
              List {
                  ForEach(model.items) {item in
                      NavigationLink(destination: SubView1(item: item)) {
                          HStack{
                              Text(item.itemName)
                              Text(item.subItems[0].isOn ? &quot;True&quot;:&quot;False&quot;) // I know: bad, but only for quick debugging reasons
                              Text(item.subItems[1].isOn ? &quot;True&quot;:&quot;False&quot;)
                          }
                      }.onReceive(item.objectWillChange, perform: {self.model.objectWillChange.send()})
                  }.onDelete(perform: delete)
              }
          }
      }
      
      func delete(at offsets: IndexSet) {
          self.model.items.remove(atOffsets: offsets)
      }
      
  }
  
  struct SubView1: View {
      @ObservedObject var item: Item
      var body: some View {
          List {
              ForEach(item.subItems) {subItem in
                  NavigationLink(destination: SubView2(subItem: subItem)) {
                      ToggleView(subItem: subItem)
                  }
                  
              }.onDelete(perform: delete)
          }
      }
      
      func delete(at offsets: IndexSet) {
             self.item.subItems.remove(atOffsets: offsets)
         }
  }
  
  struct SubView2: View {
      @ObservedObject var subItem: SubItem
      var body: some View {
          Toggle(isOn: $subItem.isOn) {
              Text(&quot;Toggle-Text&quot;)
          }
      }
  }
  
  
  struct ToggleView: View {
      @ObservedObject var subItem: SubItem
      var body: some View {
          Toggle(isOn: $subItem.isOn) {
              Text(&quot;Toggle-Text&quot;)
          }
      }
  }
  </code></pre>