ObservableObject VS StateObject in SwiftUI
Overview
In the journey of app development, managing state has always been a pain point for many of us. After much exploration and attempts, I’ve finally managed to wrap my head around the concept, and I’d like to share my understanding here.
Put simply, ObservableObject and StateObject are part of SwiftUI’s state management system.
State
here refers to data within the app that can change — for example, the text being displayed based on user input, a selected button, and so on. When these states change, SwiftUI immediately detects these changes and updates the UI.
Both ObservableObject and StateObject play a role in observing and responding to these State
changes.
However, there’s an essential difference between the two — it lies in the concept of “lifecycle.”
You might think, this far, it’s something you could have found just by Googling or looking at the official documentation. As always, let’s use a metaphor to make this easier to understand.
ObservableObject
The ObservableObject is an object that observes the state.
When the state of an object changes, SwiftUI detects this and updates the related UI. However, ObservableObject is not “owned” in the view hierarchy of SwiftUI.
In other words, when the view disappears or is recreated, the ObservableObject may be recreated.
Think of ObservableObject as a ‘traveler’.
When a traveler visits a new city, they sleep at a hotel in that city. The next day, they move to another city and sleep in a new hotel. There’s no relationship between the hotels in different cities.
Similarly, an ObservableObject is recreated every time the view changes, and it has no relation to the state in the previous view.
StateObject
The StateObject is also an object that observes the state, but it’s a concept of being “owned.”
Regardless of how the SwiftUI view hierarchy changes, once the StateObject is created, its lifecycle persists independently of the view.
Think of StateObject as a ‘resident who moved to a new place’.
When someone moves, they start living in a new house. Even when the person moves around, the house remains.
Similarly, once a StateObject is created, its state persists, and even if the view changes, the state remains the same.
I know it might still be confusing.
It’s okay. Let’s check an example through code.
class Tourist: ObservableObject {
@Published var location = "Home"
init() {
print("Hello Im ready to tour!")
}
func moveLocation() {
self.location = "Busan"
}
}
First, I created a traveler through the Tourist
class. The traveler has a moveLocation
function that moves from the original home ('Home') to another city ('Busan').
class Native: ObservableObject {
@Published var location = "Home"
init() {
print("Hello Im ready to move my home!")
}
func moveLocation() {
self.location = "Busan"
}
}
Native
class represents a local person. Like a traveler, a local person moves from home to another city, but a local person remembers the change of city because it is a concept of moving to another house.
struct TravelView: View {
@ObservedObject var tourist = Tourist()
@StateObject var native = Native()
var refresh: Bool
var body: some View {
VStack {
Text("Tourist is at: \\\\(tourist.location)")
Text("Native is at: \\\\(native.location)")
Button(action: {
tourist.moveLocation()
native.moveLocation()
}) {
Text("Go to Busan")
}
}
}
}
struct ContentView: View {
@State var refresh: Bool = false
var body: some View {
VStack{
TravelView(refresh: refresh)
Button(action: {
self.refresh.toggle()
}) {
Text("Let's go home")
}
}
}
}
Now, in the TravelView
, I've executed the functions to move both the traveler and the local resident to Busan. In ContentView
, I've declared a State
variable to refresh the view and executed an action to send them back home.
As you can see, while the traveler has returned home, the local resident continues to stay in Busan
. Also, at the ini logs, you can see that the ObservableObject
is recreated each time the view refreshes.
Conclusion
Although I used metaphors to aid understanding, the core difference between ObservableObject
and StateObject
lies in the lifecycle of the objects.
While ObservableObject
is recreated every time the view is restructured, StateObject
has a lifecycle independent of the view's lifecycle.
It took me quite a while to understand this, but I hope this post can help someone out there. 😄😄😄