I’ve been learning about swiftUI and @StateObject
and @ObservedObject
and thought I grasped it until I realized I could exchange @ObservedObject
with @StateObject
without any difference to the code behaviour. Consider this example:
class SharedModel: ObservableObject {
@Published var value: String = "Initial Value"
}
struct ContentView: View {
@StateObject var sharedModel = SharedModel()
var body: some View {
VStack {
Text("Value in ContentView: (sharedModel.value)")
TextField("ContentView textfield", text: $sharedModel.value)
SubView(sharedModel: sharedModel)
}
}
}
struct SubView: View {
@ObservedObject var sharedModel: SharedModel
var body: some View {
VStack {
Text("Value in SubView: (sharedModel.value)")
TextField("sub textfield", text: $sharedModel.value)
}
}
}
Writing something in either TextField
will update the text in both Text
views. So, ContentView and SubView will update when the property of SharedModel is updated. Great. But then I played around a bit and changed @ObservedObject
to @StateObject
and didn’t notice any difference. It worked as before. The way I though it would work is that if a property is annotated with @StateObject
then it’s the source of truth and won’t relay changes to other @StateObject
properties.
So what’s the deal here? What’s the point of @ObservedObject
and why should I use it if @StateObject
works equally fine?
2
Answers
You are talking about three different things:
@ObservedObject
is a property wrapper and is used – similar to value type@Binding
– for reference type objects which are not owned by the current view. In the first version of SwiftUI it was also the property wrapper for objects owned by the current view.@StateObject
is a property wrapper and is used – similar to value type@State
– exclusively for reference type objects which are owned by the current view. It has replaced@ObservedObject
in later versions of SwiftUI for the owned types. For compatibility reasons@ObservedObject
still works but it’s not recommended.ObservableObject
on the other hand is a protocol and is required for classes created with the@StateObject
wrapper. It refreshes the affected view whenever one of its@Published
properties changes.Please note the distinct difference between
ObservableObject
and@ObservedObject
.StateObject
’s main difference is that it has storage within SwiftUI it can preserve an object when theView
is reloaded.You might not notice any differences but using
ObservedObject
to initialize will cause leaks, inconsistent behavior and unnecessary loss of data.https://developer.apple.com/documentation/swiftui/monitoring-model-data-changes-in-your-app
When going from
StateObject
toStateObject
you don’t get a new object because it is aclass
is a reference type and they would both be pointing to the same address. You would need a “deep” copy for them to be completely different.You would have 2 different o nexts trying to own the same thing within SwiftUI so you might end up with leaks.
The new
Observable
can do what you describe if you pass anObservable
to aState
SwiftUI now creates a deep copy for you.