ObservedObject not triggering view redraw

169 views Asked by At

I've a view which requires data obtaining from an api. My understanding of SwiftUI is that by using onAppear the api will be called, viewmodel attribute updated, and the StateObject changed which will trigger a View redraw.

The issue is that no redraw is taking place.

I can see an api call occuring, and adding debug into after the decoded data is used I can see a lot of data is returned.

I've removed a lot of code to make the logic easier to follow (below).

Replacing @StateObject with @ObservedObject and passing into the view from a parent makes no difference either.

Thanks

struct FactoryDetailView: View {

  var factory: Factory
  @StateObject var factoryDetailsViewModel: FactoryDetailsViewModel()

  var body: some View {

     VStack {
        Text(factory.name)
        ForEach(factoryDetailsViewModel.details) { det in
          Text(det)
        }
     }
     .onAppear { factoryDetailsViewModel.loadDetails(factory) }
  }
}

The viewmodel:

class FactoryDetailsViewModel: ApiViewModel {
  @Published var details: [ String ]
  func loadDetails(factory: Factory) {

     // Do api call...

     self.objectWillChange.send()
     self.details = decodedResultsFromApiCall
     self.objectWillChange.send()
}
class ApiViewModel: ObservableObject {
}
2

There are 2 answers

1
Asperi On

Well... removed details might be the reason of the issue, but in general approach should look like following

struct FactoryDetailView: View {
  ...
  // assume it is a type and here there is initialization 
  @StateObject var factoryDetailsViewModel = FactoryDetailsViewModel()
  ...

now about self.objectWillChange.send() - don't call it, modifying published property it is called automatically

func loadDetails(factory: Factory) {

     // Do api call...
     { 
          // this is inside (!) API callback
          DispatchQueue.main.async {
             // update published property on main queue, always.
             self.details = decodedResultsFromApiCall
          }
     }
}
0
Houdi On

This has the answer:

@Published property wrapper not working on subclass of ObservableObject

The issue I'm seeing is the same - subclassing an ObservableObject.

I've now got a solution working by '@Published var api_response' in the parent class and removing @Published from attributes in subclasses (although leaving '@Published' in them doesn't seem to cause any side effects so they may as well remain if only to document the intent).

Thanks for the responses.