ObservableObject VS StateObject in SwiftUI

Kyubo Shim
4 min readMay 24, 2023

--

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

Photo by Simon English on Unsplash

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

Photo by Michal Balog on Unsplash

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. 😄😄😄

--

--