How We Can Read and Write to Same Observableobject in Swiftui

How we can read and write to same ObservableObject in SwiftUI?

how we can make StateObject and ObservedObject working on a same data?

You can make it via shared instance, in example:

class TextModel: ObservableObject {
static let shared = TextModel() // << here !!

@Published var stringOfText: String = "No Data!"
}

struct ContentView: View {
@ObservedObject var readStringOfTextView = TextModel.shared // << here !!

// ... other code
}

struct TextView: View {
@StateObject var textModel = TextModel.shared // << here !!

// ... other code
}

Do I have to use an ObservableObject in SwiftUI?

You don't have to use @ObservedObject to ensure that updates to your model object are updating your view.

If you want to use a struct as your model object, you can use @State and your view will be updated correctly whenever your @State struct is updated.

There are lots of different property wrappers that you can use to update your SwiftUI views whenever your model object is updated. You can use both value and reference types as your model objects, however, depending on your choice, you have to use different property wrappers.

@State can only be used on value types and @State properties can only be updated from inside the view itself (hence they must be private).

@ObservedObject (and all other ...Object property wrappers, such as @EnvironmentObject and @StateObject) can only be used with classes that conform to ObservableObject. If you want to be able to update your model objects from both inside and outside your view, you must use an ObservableObject conformant type with the appropriate property wrapper, you cannot use @State in this case.

So think about what sources your model objects can be updated from (only from user input captured directly inside your View or from outside the view as well - such as from other views or from the network), whether you need value or reference semantics and make the appropriate choice for your data model accordingly.

For more information on the differences between @ObservedObject and @StateObject, see What is the difference between ObservedObject and StateObject in SwiftUI.

How do you edit an ObservableObject’s properties in SwiftUI from another class?

To make SwiftUI view follow state updates, your controller needs to be ObservableObject.

SwiftUI view will update when objectWillChange is triggered - it's done automatically for properties annotated with Published, but you can trigger it manually too.

Using same publisher of your state, you can sync two observable objects, for example like this:

class Controller: ObservableObject {
let state: State
private var cancellable: AnyCancellable?
init(_ state: State) {
self.state = state

cancellable = state.objectWillChange.sink {
self.objectWillChange.send()
}
}

func changeState() {
state.text = "bar"
}
}

struct ContentView: View {
@StateObject var controller = Controller(State())

SwiftUI - ObservableObject doesn't cause update

A new SocialMealPlan object currentMealPlan is being created and initialized every time the view needs to be redrawn/recreated. So one object triggers the update (by assignment to one of the Published vars), but the new updated view refers to its own new freshly initialized copy of currentMealPlan.

Option 1: Make currentMealPlan a @StateObject (so one copy representing a state is kept and referred to). ie @StateObject var currentMealplan = SocialMealplan(owner: YKUser.none, mealplan: Mealplan.none)

Option 2: Or keep the @ObservedObject, but create it before and outside th view. But if other views also need to refer to the currentMealPlan, create one before the View and pass it as an environment variable. ie MealPlanView().environmentObject(currentMealPlan) and then in the view @EnvironmentObject var currentMealPlan: SocialMealPlan

SwiftUI Change Observed Object From Another View

It is changing the value. The problem is you are instantiating an instance of GlobalValue locally in your struct. It has no life outside of the struct. I you want to use an Observable Object as a global store like that, you need to create it once and use that instance.

The easiest way to do this is to add
static let shared = GlobalValue() in your class, and in your struct use globalValue = GlobalValue.shared which essentially gives you a singleton. You will have one instance that all the views can read and write to.

How to bind an ObservableObject?

To bind to properties of an observable object, you'd create a property in the view that will hold an instance of the object and bind to it just like you would with any local @State or @Binding property:

struct SomeView: View {
@StateObject var gv = GlobalVariables()

var body: some View {
// ...
.sheet(isPresented: $gv.someCondition) {
// ...
}
}
}

(Bear in mind that isPresented: expects a Binding<Bool>, so someCondition has to be a non-optional Bool)

  • Use @StateObject if GlobalVariables was instantiated and owned by the view
  • Use @ObservedObject - if it's instantiated outside the view and an instance is passed via init
  • Use @EnvironmentObject - same as @ObservedObject, but instance is passed via .environmentObject

How to read property from a struct when being in @ObservableObject

ViewModel

class getJoke: ObservableObject {
@Published var currentDate = Date()
}

View that can change passing data

struct CustomDatePicker: View {

@Binding var currentDate: Date

var body: some View{
VStack {
DatePicker(selection: $currentDate, displayedComponents: .date){
Text("Select your date")
}
.datePickerStyle(.compact)
}
}
}

And put everything together

    struct ContentView: View {

@StateObject var vm = getJoke()

var body: some View {
VStack(spacing: 40) {
CustomDatePicker(currentDate: $vm.currentDate)
Button {
print(vm.currentDate)
} label: {
Text("Show selected date")
}

}
}
}

Classes and Observable Object

You create two different instance of Data in your subviews, instead you need to share one, so create it in ContentView and pass to subviews as below

struct ContentView: View {
@ObservedObject var data = Data()
var body: some View {
VStack {
TextView(data: data)
ButtonView(data: data)
}
}
}

struct TextView: View {

@ObservedObject var data: Data

var body: some View {
Text(data.currentWord)
}
}

struct ButtonView: View {

@ObservedObject var data: Data

var body: some View {
Button(action: {self.data.randomWord()}) {
Text("Random word")
}
}
}

Also, as variant, for such scenario can be used EnvironmentObject pattern. There are a lot of examples here on SO you can find about environment objects usage - just search by keywords.

SwiftUI - ObservableObject created multiple times

Latest SwiftUI updates have brought solution to this problem. (iOS 14 onwards)

@StateObject is what we should use instead of @ObservedObject, but only where that object is created and not everywhere in the sub-views where we are passing the same object.

For eg-

class User: ObservableObject {
var name = "mohit"
}

struct ContentView: View {
@StateObject var user = User()

var body: some View {
VStack {
Text("name: \(user.name)")
NameCount(user: self.user)
}
}
}

struct NameCount: View {
@ObservedObject var user

var body: some View {
Text("count: \(user.name.count)")
}
}

In the above example, only the view responsible (ContentView) for creating that object is annotating the User object with @StateObject and all other views (NameCount) that share the object is using @ObservedObject.

By this approach whenever your parent view(ContentView) is re-created, the User object will not be re-created and it will persist its @State, while your child views just observing to the same User object doesn't have to care about its re-creation.

SwiftUI Using ObservableObject in View on New Sheet

 @ObservedObject var contentData = ContentData()

ContentData() in the above line creates a new instance of the class ContentData.

You should pass the same instance from ContentView to NewView to retain the values. Like,

.sheet(isPresented: $showNewView) {
NewView(contentData: self.contentData)
}

Stop creating new instance of ContentData in NewView and add the ability to inject ContentData from outside,

    struct NewView: View {

@ObservedObject var contentData: ContentData

...
}


Related Topics



Leave a reply



Submit