@StateObject vs @ObservedObject

SwiftUI Mid-level Swift 5.3+ iOS 14+ Reviewed By Alok Singh

Predict the output

Tap the child's button 3 times, then tap Refresh in the parent. What does the child show?

final class TickModel: ObservableObject {
    @Published var ticks = 0
}
struct Child: View {
    @ObservedObject var model = TickModel()
    var body: some View {
        Button("Ticks: \(model.ticks)") { model.ticks += 1 }
    }
}
struct Parent: View {
    @State private var refresh = 0
    var body: some View {
        VStack {
            Button("Refresh \(refresh)") { refresh += 1 }
            Child()
        }
    }
}
Options
Hint 1 of 2

What runs again when the parent's state changes?

Hint 2 of 2

Who owns the TickModel?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: B Ticks: 0

Correct. The parent's body re-runs, Child() is re-initialized, and a new TickModel is created.

Why not the others?
  • A Ticks: 3. That is what @StateObject would show. @ObservedObject doesn't own the instance.
  • C Ticks: 4. Refresh doesn't touch the child's model.
  • D It crashes. Nothing crashes; the state is just lost.

30-second answer

Both observe an ObservableObject, but @StateObject creates and owns it, keeping one instance for the view's lifetime. @ObservedObject just watches an object someone else owns. If a view creates its model with @ObservedObject, every parent re-render makes a new instance and the state resets. On iOS 17+, use @State with @Observable instead.

Practise saying it · 45 s. No recording, just a timer

Explain @StateObject vs @ObservedObject.

Say your answer out loud, then check which of these points you covered:

  • StateObject creates and owns
  • ObservedObject watches someone else's
  • Inline ObservedObject resets on re-render
  • @State + @Observable on iOS 17+

Deep dive

@StateObject's initializer is an autoclosure, evaluated only once per identity.

Use @ObservedObject for models passed in from a parent.

The same bug exists with @Observable if the model is a plain stored property instead of @State.

ObservableObject re-renders a view when any @Published property changes.

Common mistakes

  • Creating a model inline with @ObservedObject.
  • Using @StateObject for a model passed in from outside (it ignores later values).
  • Expecting @StateObject to work with @Observable classes.

Senior take & follow-ups

Seniors connect it to identity and ownership, and mention the Observation equivalent.

Follow-up questions

Think of your answer first, then open the hint.

  1. How do you pass an initial value into a @StateObject?

    Show hint

    _model = StateObject(wrappedValue: Model(id: id)) in init.

  2. What is the Observation equivalent?

    Show hint

    @State for owning, plain let or @Bindable for children.

Suggest a fix if something here is wrong or out of date.