title: SwiftUI: Update data after item in list is selected
body: <p>I just spent two days trying to figure this out, hope someone out there can help me. Pretty sure something this basic must be doable and I'm missing some basic concept...</p>
  
  <p>Using <strong>XCode 11.4</strong> on <strong>macOS</strong>, but I suspect this is applicable to iOS as well.</p>
  
  <p>I've cut down the source to the bare minimum to illustrate the problem.</p>
  
  <p>I've got a list of items in a master detail view, where the item list is fetched from one source and once the user clicks on one of them, the details are fetched from a second source and displayed on the right side detail view.</p>
  
  <p><img src="https://i.stack.imgur.com/Q2rwf.png" width="400" /></p>
  
  <h3>Expected result</h3>
  
  <p>When I click on an item, the body of the item is displayed in the detail view on the right. </p>
  
  <p><img src="https://i.stack.imgur.com/zFkMV.png" width="400" /></p>
  
  <h3>Actual result</h3>
  
  <ol>
  <li><p>When I click on the first item, the correct body is shown on the right. But the ItemDetailStore.load() method is called twice!</p></li>
  <li><p>When I click on the second and third item, the detail view remains unchanged.</p></li>
  </ol>
  
  <h3>Source</h3>
  
  <pre><code>import SwiftUI
  
  struct ContentView: View {
      @State var selectedItem: Item?
  
      var body: some View {
          NavigationView {
              ItemList(selectedItem: $selectedItem)
              if selectedItem != nil {
                  ItemDetail(selectedItem: $selectedItem)
              }
          }
      }
  }
  
  struct ItemList: View {
      @Binding var selectedItem: Item?
      var items = [
          Item(itemId: 0, title: "Item #0", body: "Empty."),
          Item(itemId: 1, title: "Item #1", body: "Empty."),
          Item(itemId: 2, title: "Item #2", body: "Empty.")
      ]
  
      var body: some View {
          List(selection: $selectedItem) {
              ForEach(Array(items.enumerated()), id: \.element) { index, item in
                  ItemRow(item: item)
            }
          }
          .listStyle(SidebarListStyle())
      }
  }
  
  struct ItemRow: View {
      var item: Item
  
      var body: some View {
          Text("\(item.title)")
              .padding(12)
      }
  }
  
  struct ItemDetail: View {
      @EnvironmentObject var itemDetailStore: ItemDetailStore
      @Binding var selectedItem: Item?
  
      var body: some View {
          VStack {
              if itemDetailStore.items.count &gt; 0 {
                  Text(itemDetailStore.items[0].body)
              }
          }
          .frame(maxWidth: .infinity, maxHeight: .infinity)
          .onAppear {
              if self.selectedItem != nil {
                  self.itemDetailStore.load(id: self.selectedItem!.itemId)
              }
          }
      }
  }
  
  struct Item: Codable, Hashable {
      var itemId: Int
      var title: String
      var body: String
  }
  
  class ItemDetailStore: ObservableObject {
      @Published var items = [Item]()
  
      var itemsList = [
          Item(itemId: 0, title: "Item #0", body: "This is an excellent item #0."),
          Item(itemId: 1, title: "Item #1", body: "This is an excellent item #1."),
          Item(itemId: 2, title: "Item #2", body: "This is an excellent item #2.")
      ]
  
      func load(id: Int) {
          self.items = [itemsList[id]]
          print("\(self.items[0].title) loaded.")
      }
  }
  </code></pre>
  
  <p>And in the AppDelegate I inject the environment:</p>
  
  <pre><code>let contentView = ContentView().environmentObject(ItemDetailStore())
  </code></pre>
  
  <p>The culprit seems to be the onAppear modifier in ItemDetail, as it's only called when the first item in the list is clicked. Shouldn't the view be updated each time selectedItem changes? </p>
  
  <p>Any feedback is highly appreciated.</p>