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
fromContentView
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
Access Firebase Variable Outside Closure
Changing the Status Bar Color For Specific Viewcontrollers Using Swift in Ios8
Swift Optional Escaping Closure Parameter
Uialertcontroller - Add Custom Views to Actionsheet
Checking If an Object Is a Given Type in Swift
How to Change the Textual Representation Displayed For a Type in Swift
How to Detect If an Skspritenode Has Been Touched
Get HTML from Wkwebview in Swift
Whither Dispatch_Once in Swift 3
How to Create a Segue That Can Be Called from a Button That Is Created Programmatically
Accessing Code in Swift 3 Error
Getting the Decimal Part of a Double in Swift
Programmatically Detect Tab Bar or Tabview Height in Swiftui