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?
StateObject
’s main difference is that it has storage within SwiftUI it can preserve an object when the View
is reloaded.
You might not notice any differences but using ObservedObject
to initialize will cause leaks, inconsistent behavior and unnecessary loss of data.
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 property wrapper, which creates a single source of truth for a reference type that you store in a view hierarchy.
https://developer.apple.com/documentation/swiftui/monitoring-model-data-changes-in-your-app
When going from StateObject
to StateObject
you don’t get a new object because it is a class
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 an Observable
to a State
SwiftUI now creates a deep copy for you.