Search code examples
swiftuiswiftui-list

How to Refresh List in SwiftUI with @Observable Model


I am trying to understand why my row views don't refresh when I am repopulating the array holding them.

As a sample, I have the following views.

//main content view which generates a list full of PersonRow
struct ContentView: View {
  @State private var org : Organization = .shared
  
  var body: some View {
    List(org.people) {
      PersonRow(person: $0)
    }
    .padding()
    
    Button(action: {
      org.createPeople()
    }) {
      Text("Create People")
    }
  }
}
//Row representing each person.
struct PersonRow : View {
  @State var person : Person //observable
  
  var body: some View {
    let color : Color = person.isActive ? .blue : .red
    HStack {
      
    Text(person.name)
      .foregroundStyle(color)
      
      Spacer()
      
      Button(action: {
        person.isActive.toggle()
      }) {
        Text(person.isActive ? "Active" : "Inactive")
      }
    }
    .padding()
  }
}
@Observable class Person : Identifiable, Equatable {
  static func == (lhs: Person, rhs: Person) -> Bool { lhs.id == rhs.id }
  
  var id : String = ""
  var name: String = "John Doe"
  var isActive: Bool = true
}
@Observable class Organization {
  
  static let shared = Organization()
  
  private init() { createPeople() }
  
  var people: [Person] = []
  
  func createPeople() {
    people.removeAll()
    
    for i in 0..<10 {
      let p = Person()
      p.id = "\(i)"
      p.name = "John Doe \(i)"
      people.append(p)
    }
    
    print("Created people")
  }
}

On each row, when clicking active vs inactive it toggles as expected and the view updates as expected.

However, when I click "Create People" in the ContentView, I repopulate the entire Organization array with a new set of Person, but the list doesn't refresh. This is where I am having an issue. Shouldn't the list refresh since I am observing Oragnization and the array was updated? Can someone explain what it is I am doing incorrectly?


Solution

  • State makes a deep copy of Observable you should use @Bindable instead. Also note that State should always be private.

    That Equatable implementation also tells SwiftUI to only refresh when the id changes, you have to include all properties for Equatable.