Why Is an Observedobject Array Not Updated in My Swiftui Application

Why is an ObservedObject array not updated in my SwiftUI application?

You can use a struct instead of a class. Because of a struct's value semantics, a change to a person's name is seen as a change to Person struct itself, and this change is also a change to the people array so @Published will send the notification and the View body will be recomputed.

import Foundation
import SwiftUI
import Combine

struct Person: Identifiable{
var id: Int
var name: String

init(id: Int, name: String){
self.id = id
self.name = name
}

}

class Model: ObservableObject{
@Published var people: [Person]

init(){
self.people = [
Person(id: 1, name:"Javier"),
Person(id: 2, name:"Juan"),
Person(id: 3, name:"Pedro"),
Person(id: 4, name:"Luis")]
}

}

struct ContentView: View {
@StateObject var model = Model()

var body: some View {
VStack{
ForEach(model.people){ person in
Text("\(person.name)")
}
Button(action: {
self.mypeople.people[0].name="Jaime"
}) {
Text("Add/Change name")
}
}
}
}

Alternatively (and not recommended), Person is a class, so it is a reference type. When it changes, the People array remains unchanged and so nothing is emitted by the subject. However, you can manually call it, to let it know:

Button(action: {
self.mypeople.objectWillChange.send()
self.mypeople.people[0].name="Jaime"
}) {
Text("Add/Change name")
}

@ObservedObject does not get updated on updating the array

You're creating a new ListViewModel by using an initializer (ListViewModel()) in your button action.

Instead, refer to the one that you've set up as property in your view: viewModel.addItem()

SwiftUI: Array Not Updating In All Views When Referencing The Same Observed Object

Ok I got it working. All I had to do was move the functions I had in my view to my viewModel. Im guessing to keep everything in the same scope. My guess is that everything in ContentView was it's own copy. I'm not 100% certain on that, but it works now.

SwiftUI ObservableObject not updating when value is Published

You are likely winding up with different UsersViewModel objects:

@ObservedObject var usersViewModel = UsersViewModel()

Since UsersView is a struct, this creates a new model every time the View is instantiated (which can happen very often, not just when the view appears). In iOS 14 there is @StateObject to combine State (which preserves information between View instantiations) with ObservedObject, but in iOS 13 I recommend passing in the ObservedObject if it's not a shared instance.

Published works for single object but not for array of objects

First, a disclaimer: The code below is not meant as a copy-and-paste solution. Its only goal is to help you understand the challenge. There may be more efficient ways of resolving it, so take your time to think of your implementation once you understand the problem.


Why the view does not update?: The @Publisher in SocialStore will only emit an update when the array changes. Since nothing is being added or removed from the array, nothing will happen. Additionally, because the array elements are objects (and not values), when they do change their position, the array remains unaltered, because the reference to the objects remains the same. Remember: Classes create objects, Structs create values.

We need a way of making the store, to emit a change when something in its element changes. In the example below, your store will subscribe to each of its elements bindings. Now, all published updates from your items, will be relayed to your store publisher, and you will obtain the desired result.

import SwiftUI
import Combine

class SocialStore: ObservableObject {
@Published var socials : [Social]
var cancellables = [AnyCancellable]()

init(socials: [Social]){
self.socials = socials

self.socials.forEach({
let c = $0.objectWillChange.sink(receiveValue: { self.objectWillChange.send() })

// Important: You have to keep the returned value allocated,
// otherwise the sink subscription gets cancelled
self.cancellables.append(c)
})
}
}

class Social : ObservableObject{
var id: Int
var imageName: String
var companyName: String

@Published var pos: CGPoint

init(id: Int, imageName: String, companyName: String, pos: CGPoint) {
self.id = id
self.imageName = imageName
self.companyName = companyName
self.pos = pos
}

var dragGesture : some Gesture {
DragGesture()
.onChanged { value in
self.pos = value.location
print(self.pos)
}
}
}

struct ContentView : View {
@ObservedObject var socialObject: SocialStore = SocialStore(socials: testData)

var body: some View {
VStack {
ForEach(socialObject.socials, id: \.id) { social in
Image(social.imageName)
.position(social.pos)
.gesture(social.dragGesture)
}
}
}
}

SwiftUI: View not updating when Observed object changes from another file

There are many ways to have the viewModel changes reflected in different Views, the following is an example, where the main idea is to have one source of truth that you use in all the different Views:

declare @StateObject var viewModel = VariableSetupModel() in ContentView and
@EnvironmentObject var viewModel: VariableSetupModel in ContentNavView.

Pass viewModel from
ContentView to ContentNavView using .environmentObject(viewModel), that is, add this to ContentView, eg: VStack (alignment: .leading) {...}.environmentObject(viewModel)

Note, there is no need for VariableSetupModel to be a singleton, remove the static let shared = VariableSetupModel().

Note also, to have the viewModel available in SettingsView() use, SettingsView().environmentObject(viewModel)

Another approach is to pass a ObservedObject from one view to another.
For example:

declare @StateObject var viewModel = VariableSetupModel() in ContentView and

@ObservedObject var viewModel: VariableSetupModel in ContentNavView.

Pass viewModel from ContentView to ContentNavView using ContentNavView(viewModel: viewModel).



Related Topics



Leave a reply



Submit