SwiftUI Dive: `@StateObject` vs `@ObservedObject` — When and Why to Use Each

Ghoshit VoraGhoshit Vora
4 min read

Introduction

In SwiftUI, managing the state of your data is a foundational skill. While @StateObject and @ObservedObject are both used to work with observable reference types, they serve very different purposes. Misusing them can lead to unexpected behaviors like duplicated initializations or loss of state during view redraws.

In this article, we’ll demystify @StateObject and @ObservedObject with clear examples, highlight when to use each, and explain their lifecycle differences—a topic that often trips up even experienced developers.


What is an ObservableObject?

To understand @StateObject and @ObservedObject, we first need to look at ObservableObject. It’s a protocol that allows reference types (usually class-based view models) to notify SwiftUI when data changes using @Published properties.

class CounterViewModel: ObservableObject {
    @Published var count = 0
}

This pattern is core to MVVM in SwiftUI, where the view observes a view model and automatically updates the UI when properties change.


@StateObject: Long-Lived and View-Owned

Use @StateObject when the view creates and owns the instance of an observable object. This tells SwiftUI to instantiate and retain the object only once—even if the view redraws due to state changes or layout updates.

Important: @StateObject ensures the object is not reinitialized when the view is re-rendered.

Example:

struct CounterView: View {
    @StateObject private var viewModel = CounterViewModel()

    var body: some View {
        VStack {
            Text("Count: \(viewModel.count)")
            Button("Increment") {
                viewModel.count += 1
            }
        }
    }
}

Even if CounterView is reloaded (e.g., due to a navigation pop/push or another state change), viewModel retains its current state — count doesn’t reset.


@ObservedObject: For Injected Dependencies

Use @ObservedObject when the view receives an already-created observable object — typically from a parent view. SwiftUI doesn’t own this object, so it will reinitialize it if the parent re-renders and creates a new instance during the redraw.

⚠️ Caution: If the object is initialised inline inside the view using @ObservedObject, it will be recreated every time the view updates — which means you’ll lose state.

Example:

struct CounterRowView: View {
    @ObservedObject var viewModel: CounterViewModel

    var body: some View {
        Text("Count: \(viewModel.count)")
    }
}

Here, CounterRowView does not own the view model. It observes the data passed in from its parent — that’s the key difference.


Key Behavioural Difference: Initialization and Lifecycle

Let’s highlight the critical distinction between the two:

  • @StateObject is initialized once and remains alive as long as the view is alive, even if the view re-renders.

  • @ObservedObject does not manage the lifecycle — if the object is created in the body or initializer and not retained elsewhere, it will be reinitialized every time the view updates.

Misuse Example — What Not to Do:

struct FaultyView: View {
    @ObservedObject var viewModel = CounterViewModel() // ❌ Recreated on every redraw

    var body: some View {
        Text("Count: \(viewModel.count)")
    }
}

This is incorrect usage. Every time SwiftUI redraws this view, a new view model is created, and the count resets. To fix this, you should use @StateObject.


When Should You Use Each?

  • ✅ Use @StateObject when:

    • The view creates the observable object.

    • The view is responsible for managing the object's lifecycle.

    • You want the object to be initialized only once, even when the view redraws.

    • You need the object to retain its state across view reloads.

  • 🔄 Use @ObservedObject when:

    • The observable object is passed from a parent view or an external source.

    • The view should observe the object but not manage its lifecycle.

    • You don't need to persist state across redraws (e.g., for lightweight or shared models).

⚠️ Tip:
Misusing these wrappers—like using @ObservedObject to create a new object in the view—can cause unexpected behavior, including loss of state or unnecessary re-initializations.


Conclusion

Correctly using @StateObject and @ObservedObject is crucial for building reliable SwiftUI applications. Here’s the mental model:

  • Use @StateObject when the view owns the data and should manage its lifecycle.

  • Use @ObservedObject when the view depends on external data passed from a parent.

And remember: if your observable object is being reinitialized unexpectedly, double-check whether you're using @StateObject when you should be.

Lastly, if you have feedback, suggestions, or notice anything that needs correcting, please don’t hesitate to let me know — I’d love to hear your thoughts!

0
Subscribe to my newsletter

Read articles from Ghoshit Vora directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ghoshit Vora
Ghoshit Vora

Senior iOS Developer with a strong background in building scalable and maintainable mobile applications using Swift, SwiftUI, and UIKit. Skilled in architecture patterns like MVVM, Clean Architecture, and Protocol-Oriented Programming. I focus on writing clean, testable code, implementing responsive UI, and optimizing app performance while following SOLID principles and leveraging Combine, Swift Concurrency, and Core Data in modern iOS development.