SwiftUI View does not updated when ObservedObject changed

swiftui published not working
swiftui will change
swiftui list not updating
modifying state during view update, this will cause undefined behavior
observableobject swiftui
swiftui force view update
observed object not updating swift
swiftui passthroughsubject

I have a content view where i'm showing a list of items using ForEach

@ObservedObject var homeVM : HomeViewModel

var body: some View {
    ScrollView (.horizontal, showsIndicators: false) {
        HStack(spacing: 10) {
            Spacer().frame(width:8)
            ForEach(homeVM.favoriteStores , id: \._id){ item in
                StoreRowOneView(storeVM: item)
            }
            Spacer().frame(width:8)
        }
    }.frame(height: 100)
}

And my ObservableObject contains

@Published var favoriteStores = [StoreViewModel]()

And StoreViewModel is defined as below

class StoreViewModel  : ObservableObject {
    @Published var store:ItemStore

    init(store:ItemStore) {
        self.store = store
    }

    var _id:String {
        return store._id!
    }
}

If i fill the array directly it work fine , and i can see the list of stores but if i filled the array after a network call (background job) , the view did not get notified that there is a change

 StoreApi().getFavedStores(userId: userId) { (response) in
            guard response != nil else {
                self.errorMsg = "Something wrong"
                self.error = true
                return
            }
            self.favoriteStores = (response)!.map(StoreViewModel.init)
            print("counnt favorite \(self.favoriteStores.count)")
        }

But the weird thing is : - if i add a text which show the count of the array items , the view will get notified and everything work fine

That's what i mean :

    @ObservedObject var homeVM : HomeViewModel
    var body: some View {
        ScrollView (.horizontal, showsIndicators: false) {
            HStack(spacing: 10) {
                Spacer().frame(width:8)
                ForEach(homeVM.favoriteStores , id: \._id){ item in
                    StoreRowOneView(storeVM: item)
                }
                Text("\(self.homeVM.favoriteStores.count)").frame(width: 100, height: 100)
                Spacer().frame(width:8)
            }
        }.frame(height: 100)
    }

Any explaining for that ?

Share some experiences that might help.

When I used ScrollView + ForEach + fetch, nothing shows until I trigger view refresh via other means. It shows normally when I supply fixed data without fetching. I solved it by specifying .infinity width for ScrollView.

My theory is that ForEach re-renders when fetch completes, but ScrollView somehow does not adjust its width to fit the content.

It is similar in your case. By supplying fixed width Text, ForEach can calculate its width during init, and ScrollView can use that to adjust its width. But in case of fetch, ForEach has initial 0 content width, and so is ScrollView. When fetch completes ScrollView does not update its width accordingly.

Giving ScrollView some initial width should solve your problem.

@ObservedObject Issue, in a good number of views and before Beta 5, I did not have to pass this and (2) I can discover no apparent SwiftUI way to respond to orientation changes. of making changes and I notice there is no xcode update to go with the Beta? Without @ObservedObject, the change announcements would be sent but ignored. This changes will also be reflected on all the screens knowing that this will also update the UI with the help of SwiftUI. @ObservedObject var user = User() var body: some View {

For horizontal ScrollView you need to have your height fixed or just use conditional ScrollView which will show when someModels.count > 0

var body: some View {
   ScrollView(.horizontal) {
      HStack() {
         ForEach(someModels, id: \.someParameter1) { someModel in
            someView(someModel)
         }
      }.frame(height: someFixedHeight)
   }
}
// or
var body: some View {
   VStack {
      if someModels.count > 0 {
         ScrollView(.horizontal) {
            HStack() {
               ForEach(someModels, id: \.someParameter1) { someModel in
                  someView(someModel)
               }
            }
         }
      } else {
        EmptyView()
        // or other view
      }
   }
}

Properly conforming to Hashable protocol by implementing

func hash(into hasher: inout Hasher) { 
   hasher.combine(someParameter1)
   hasher.combine(someParameter2)
}

and

static func == (lhs: SomeModel, rhs: SomeModel) -> Bool {
   lhs.someParameter1 == rls.someParameter1 && lhs.someParameter2 == rls.someParameter2

}

And also following @Asperi suggestion of using proper id from model, which must be unique to every element.

ForEach(someModels, id: \.someParameter1) { someModel in
    someView(someModel)
}

There is no other way for ForEach to notify updates other than unique ids.

Text(someModels.count) was working because it is notifying the changes as count is being changed.

UIViewRepresentable not updated when observed object changed , 3 (Xcode 11.3.1), when a SwiftUI View passes an @ObservedObject to a UIViewRepresentable, and the UIViewRepresentable is a struct , SwiftUI� As a result, we can type into the text fields but the text view above won’t be updated. To fix this, we need to tell SwiftUI when interesting parts of our class have changed. By “interesting parts” I mean parts that should cause SwiftUI to reload any views that are watching our class – it’s possible you might have lots of properties

I would start from forwarding results to the main queue, as below

DispatchQueue.main.async {
    self.favoriteStores = (response)!.map(StoreViewModel.init)
}

Next... ForEach tracks id parameters, so you should be sure that results have different _id s in the following, otherwise ForEach is not marked as needed to update

ForEach(homeVM.favoriteStores , id: \._id){ item in
    StoreRowOneView(storeVM: item)
}

How to send state updates manually using objectWillChange, How to use @ObservedObject to manage state from external objects � How Updated for Xcode 12.0 For example, you might want the view to refresh only if you're happy Publishers are Combine's way of announcing changes to whatever is watching, which in the case of SwiftUI is zero or more views. The print statement shows that the button is updating the numbers, but the View does not update accordingly. I know that updating a value in an array does not change the array value itself, so I use a manual objectWillChange.send() call. I would have thought that should trigger the update, but the screen never changes. Any idea?

What is ItemStore? more importantly

ForEach(homeVM.favoriteStores , id: \._id){ item in
    StoreRowOneView(storeVM: item)
}

what is _id i believe your _id is same each time so it will not update the list. You have to put id: some variable that you want to re-render the list when it changed.

Update: If you do not have any other variable to take its value as reference than you can use

ForEach((0..<homeVM.favoriteStores.count)) { index in
    StoreRowOneView(storeVM:: homeVM.favoriteStores[index])
}

Why an ObservedObject array is not updated in my SwiftUI , However, if I change the value of an existing Person, it is not reloaded in the View // NamesClass.swift import Foundation import SwiftUI import Combine class� Updated for Xcode 12.0. When using observed objects there are three key things we need to work with: the ObservableObject protocol is used with some sort of class that can store data, the @ObservedObject property wrapper is used inside a view to store an observable object instance, and the @Published property wrapper is added to any properties inside an observed object that should cause views

How to modify state from state in SwiftUI, Modifying state during view update, this will cause undefined behavior. @ ObservedObject var viewModel: ViewModel = ViewModel() private let detector which should change the image property of our viewModel , the view is not reloaded. SwiftUI framework was designed to encourage building the apps in the single-source-of-truth style, be that Redux-like centralized app state or ViewModels serving the data only to their views. It feels natural to use @ObservedObject or @EnvironmentObject for the state management of such views, as the ObservableObjects fit the design really well.

SwiftUI coredata changes not being refreshed in ContentView but in , SwiftUI coredata changes not being refreshed in ContentView but in other views it is. Using observedobject @Published. Question But the underlying issue is, that you publish an array of objects. Since updating an object doesn't actually change the array, the list wouldn't refresh. So what 2. Reply. View all 17 comments� The reason I requested enough code, is that such subtle changes, which seem to be irrelevant to view updates, would change the behavior. I do expect that SwiftUI 2, introduced in WWDC 2020 soon, would fix all such issues and we would not need any more do-not-know-why workarounds.

Why I quit using the ObservableObject, SwiftUI framework was designed to encourage building the apps in the But it's sufficient to have just a few hundreds of views subscribed on the same update ( and not being state objects, such as EnvironmentObject or ObservedObject . when neither of the values used in the specific view got changed. This property publish any changes to its data, so that this change is updated in the view. The start method updates the counter every second. The stopwatch is stopped. The couinter is reset to 0 and the stopwatch is stopped. In the Project navigator, click to select ContentView.swift. Change the code inside the ContentView struct to

Comments
  • I am facing exact same problem. No luck though. I am stuck on it for days now. The ViewModel updates after network call but list stops updating after that. When I print the object after network calls, it gives me expected values but view does not updates.
  • @Ishmeet update me if you found a solution for this problem please
  • I did not find a solution yet. I think this is a SwiftUI Bug.
  • @Ishmeet - Did you guys find a solution by now? I have the same problem but I am using List View. My List has always 2 cells & the cells are loaded correct with static info, when I hit a webservice & updated the model, the view no longer readjusts. When I print the model objects they are updated with latest data.. Plz let update the post if you guys found any solution
  • @Dinakar we did find a solution by refactoring to use the right combination of State/ObservableObject/Binding. We made sure that our object is owned by the parent as State. However, while passing the object downstream we used Binding and ObservedObject Property Wrapper. This video from WWDC helped us achieve the result: developer.apple.com/videos/play/wwdc2019/226 . If you can share your code, I might be able to help better.
  • You just saved me a tonne of time. I'm working on a firebase project and I had this EXACT thing happen to me. Thank you!
  • ForEach is worked fine when i add another Text outside it , which sho the count of stores , so the id was not the problem , and for DispatchQueue , i think that i'm on the main thread , i don't need it , and also it work fine after adding a Text , which is really weird