StateObject vs ObservedObject

I had been reading a lot about SwiftUI’s @StateObject & @ObservedObject and came across a bunch of interesting discussions on twitter. I found these discussions insightful and most importantly they are from some of the best iOS developers.

I have added them as notes below and they help me to quickly brush up. Hope they help you as well.

Entire twitter thread on the discussion between chriseidhof, nicklockwood, donnywals & others :
https://twitter.com/chriseidhof/status/1280085055021383681
https://twitter.com/nicklockwood/status/1280133214489710596
https://twitter.com/nicklockwood/status/1279033830083526656

Luca Bernardi: https://twitter.com/luka_bernardi/status/1280224429637681152?s=20
A good guidance is to use StateObject only if you allocate an instance in the initializer, use ObservedObject everywhere else.
Use StateObject like you would use State, as a private source of truth within a view, and not as a derived value provided from another view.

https://twitter.com/chriseidhof/status/1281173155508355077?s=20
I’ve been thinking about this, but if the state object depends on a changeable property that’s passed in from the outside (let’s say an id) it doesn’t work. I assume that’s not a bug, right? In my understanding, StateObject is used when the object will never change?

https://twitter.com/nicklockwood/status/1279052574197497856?s=20
With StateObject the default value is invoked once and then stored in a side table (effectively a singleton). With ObservedObject it might be a different instance being passed from the parent each time the view tree is updated.

https://twitter.com/nicklockwood/status/1280133853840113669?s=20
In other words, you only get to set the reference for a state object once. After that, the child view can observe changes the parent makes to that object’s properties, but it can’t ever receive a new instance of the object itself, even if the parent’s body is re-evaluated.

https://twitter.com/twostraws/status/1280090039503007745?s=20
It still leaves the underlying question of when  @ObservedObject  is actually useful if we can use  @StateObject everywhere – “clarification of ownership” seems to be about it.

https://twitter.com/DonnyWals/status/1280086281251323905?s=20
StateObject can be injected by a parent view but can also be created locally. Using StateObject also implies ownership. An ObservedObject should always be injected by a parent (and is owned externally)

https://kean.blog/post/swiftui-data-flow
By using @StateObject, the store is instantiated only once per view, right before body runs. SwiftUI keeps the store around for the entire view lifecycle. You can pass the store deeper into the view hierarchy via @ObservedObject, @Binding, or @EnvironmentObject, just as you would expect.
The key difference is that @StateObject manages the lifetime of the wrapped object for you. SwiftUI keeps the object alive for all lifetime of the view.
struct ArtistList: View {
@ObservedObject var store = ArtistStore()
}
SwiftUI will create a new instance of ArtistStore every time a view is re-created, and it discards of the view structs quickly after computing the body. It might lead to loss of data or to the very least, performance inefficiencies.

https://swiftwithmajid.com/2020/06/29/new-property-wrappers-in-swiftui/
new StateObject property wrapper that fills the most significant gap in SwiftUI data flow management. SwiftUI creates only one instance of the StateObject for each container instance that you declare and holds it in the internal framework memory that saves it during view updates. StateObject works in a very similar way to State property wrapper, but instead of value types, it is designed to work with reference types.

https://www.hackingwithswift.com/quick-start/swiftui/what-is-the-stateobject-property-wrapper
SwiftUI’s @StateObject property wrapper is designed to fill a very specific gap in state management: when you need to create a reference type inside one of your views and make sure it stays alive for use in that view and others you share it with.

Sharing an observed object with a new view
https://www.hackingwithswift.com/books/ios-swiftui/sharing-an-observed-object-with-a-new-view
Passes the observed object using : AddView(expenses: self.expenses)
@ObservedObject var expenses: Expenses

https://www.hackingwithswift.com/quick-start/swiftui/what-is-the-published-property-wrapper
whenever an object with a property marked @Published is changed, all views using that object will be reloaded to reflect those changes.
@Published is opt-in – you need to list which properties should cause announcements, because the default is that changes don’t cause reloads

https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app
SwiftUI might create or recreate a view at any time, so it’s important that initializing a view with a given set of inputs always results in the same view. As a result, it’s unsafe to create an observed object inside a view. Instead, SwiftUI provides the StateObject attribute for this purpose.

A state object behaves like an observed object, except that SwiftUI knows to create and manage a single object instance for a given view instance, regardless of how many times it recreates the view. You can use the object locally, or pass the state object into another view’s observed object property,
You can pass individual properties of an observed object to child views, as shown above. When the data changes, like when you load new data from disk, SwiftUI updates all the affected views. You can also pass an entire observable object to a child view and share model objects across levels of a view hierarchcy

https://www.reddit.com/r/swift/comments/ixqp3m/swiftui_error_cannot_convert_value_of_type/
I do not have enough context to tell whether this is the problem so it will be a long shot, but I forgot to mention another issue with your code which is that you are instantiating to an @ObservedObject whose storage duration is bound to the duration of the struct in which it’s declared, so it is likely resetting its values every time the view’s struct is recreated. If you are building for iOS 14 you can replace @ObservedObject with @StateObject, otherwise you’ll have to find an alternative way to keep the object alive, such as by making it an EnvironmentObject.

https://www.swiftbysundell.com/tips/a-first-look-at-swiftui-stateobject/
https://www.swiftbysundell.com/articles/published-properties-in-swift/
This is where StateObject comes in, which provides a built-in way to have one of our views assume ownership over an ObservableObject. That way, we no longer need to retain that object elsewhere, since SwiftUI will manage it for us automatically, even as our views get updated and their values recreated.
We should still keep using ObservedObject for injected dependencies that are retained and managed outside of our view hierarchy, while StateObject can be a great alternative for reference types that are used to keep track of a view’s internal state.

https://swiftuipropertywrappers.com: You should use @StateObject if You want to respond to changes or updates in an ObservableObject.The view you’re using @StateObject in creates the instance of the ObservableObject itself.

Internally, SwiftUI will not keep an @ObservedObject around when it discards and recreates a view if this is needed for a fresh render.Instead, SwiftUI knows that the parent view will pass down an ObservedObject (which could be either a @StateObject if the parent owns the property, or an @ObservedObject if the parent doesn’t own the property) that’s used as the value for the property marked as @ObservedObject.

You should use @ObservedObject if you want to respond to changes or updates in an ObservedObject.The view does not create the instance of the ObservedObject itself. (if it does, you need a @StateObject)

Phew! I read and re-read the posts multiple times to get some grasp. Hope they help you too. ✌🏽

PS. These notes are directly picked up from their respective site.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create your website with WordPress.com
Get started
%d bloggers like this: