@StateObject: What It Is, Creation and Management of ObservableObject

Author: IT Sectr Published: 2026-06-26 Reading time: 9 min

@StateObject is a property wrapper in SwiftUI that creates and owns an ObservableObject instance throughout the entire lifecycle of a View. When a View first appears on screen, @StateObject initializes the object and stores it until the View is removed from memory. This ensures that data is not reset when the interface is rebuilt — for example, when switching themes or updating the parent View. According to Apple Developer Documentation (2025), @StateObject should be used as the primary source of truth for ObservableObject in the SwiftUI hierarchy, while child Views receive the already created object via @ObservedObject or @EnvironmentObject.

Key Takeaways

  • @StateObject is a property wrapper for creating and owning an ObservableObject inside a View.
  • Single creation — the object is initialized once during the View's lifetime and is not recreated on rebuilds.
  • Source of truth — @StateObject is the source of truth in the hierarchy, unlike @ObservedObject.
  • Lifecycle — the object lives as long as the View exists in memory and is destroyed along with it.
  • Initialization — @StateObject requires an initial value at creation, usually via init with parameters.

What is @StateObject in SwiftUI

@StateObject is a property wrapper introduced in iOS 14 that allows a View to create and own an instance of a class conforming to the ObservableObject protocol. Unlike @State, which works with value types (structs), @StateObject is designed for reference types — classes that can notify SwiftUI about changes to their properties.

When a View uses @StateObject var viewModel: MyViewModel, SwiftUI automatically creates an instance of MyViewModel when the View first appears and stores it in a special framework storage. On every View update (for example, when the parent state changes), SwiftUI does not recreate the object — it uses the existing instance until the View is removed from the hierarchy.

According to Apple WWDC Session 10137 (2024), @StateObject solves the data loss problem that existed in iOS 13 when Views were rebuilt, forcing developers to create ObservableObject in the parent View and pass it through the initializer. This led to code duplication and the risk of accidentally recreating the object.

swift
import SwiftUI

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0
    
    func increment() {
        count += 1
    }
}

struct CounterView: View {
    @StateObject var viewModel = CounterViewModel()
    
    var body: some View {
        VStack {
            Text("Count: \(viewModel.count)")
            Button("Increment", action: viewModel.increment)
        }
    }
}

How @StateObject Works

The @StateObject mechanism is based on the integration of SwiftUI with the Combine framework. When an ObservableObject marks its properties with the @Published attribute, SwiftUI automatically subscribes to changes via the publisher built into the ObservableObject protocol. When a published property changes, the object sends a signal through the objectWillChange publisher, which triggers a redraw of all Views observing this object.

SwiftUI stores the ObservableObject instance in a special storage tied to a specific View instance. This storage is created once during the first render and exists until the View is destroyed. This is why @StateObject guarantees reference stability — SwiftUI manages memory automatically, without relying on the View's initializer.

According to objc.io — Thinking in SwiftUI (2025), the internal implementation of @StateObject uses a mechanism similar to @State but for reference types: SwiftUI creates a boxing wrapper around the object and manages its lifecycle through its own allocator, optimized for frequent View hierarchy rebuilds.

@StateObject Lifecycle

  • Creation — when the View first appears on screen, SwiftUI calls the object's initializer and stores the reference.
  • Rebuild — when the parent View updates, the object is not recreated; the existing instance is used.
  • Destruction — when the View leaves the screen and is removed from the hierarchy, SwiftUI calls the object's deinit.

@StateObject vs @ObservedObject: Key Differences

The main difference between @StateObject and @ObservedObject lies in who owns the object. @StateObject creates and stores the object — it is the owner. @ObservedObject only observes the object that was created elsewhere and passed via initializer or property.

Characteristic@StateObject@ObservedObject
OwnershipCreates and owns the objectOnly observes
InitializationInside the View via init/defaultExternal, passed via parameter
LifecycleTied to the View's lifecycleNot controlled by the View
RecreationNot recreated on updateCan be replaced externally
iOS versioniOS 14+iOS 13+

The rule is simple: if the View creates the ObservableObject — use @StateObject. If the View only receives an already created object from the parent — use @ObservedObject. Violating this rule leads either to data loss (if using @ObservedObject for ownership) or to excessive object creation (if using @StateObject for observation).

When to Use @StateObject

@StateObject should be used in Views that are the source of truth for a specific data set. Typical scenarios include screens with their own view model, root screens of navigation stacks, and modal presentations managing their own state.

  • Screen with view model — each screen that manages its own data and logic should create its view model via @StateObject.
  • Root View — in a NavigationStack or TabView hierarchy, the root element creates data, and child elements receive it via @ObservedObject.
  • Modal windows — .sheet and .fullScreenCover often require their own @StateObject to manage a form or process.
  • Editable list — each list row containing an edit form should have its own @StateObject.
swift
struct ProfileView: View {
    @StateObject var viewModel = ProfileViewModel()
    
    var body: some View {
        NavigationStack {
            Form {
                TextField("Name", text: $viewModel.name)
                TextField("Email", text: $viewModel.email)
                Button("Save") {
                    viewModel.saveProfile()
                }
            }
            .navigationTitle("Profile")
        }
    }
}

Initializing @StateObject with Parameters

Initializing @StateObject with parameters requires special syntax, since SwiftUI manages object creation on its own. You cannot simply pass parameters to the initializer — you need to use an escaping closure or a separate factory method.

According to Swift by Sundell (2024), the cleanest approach is to use a factory method or closure that SwiftUI will call when the object is first created. An alternative approach is to initialize the ObservableObject in the parent View and pass it via @StateObject using the standard initializer.

swift
class UserViewModel: ObservableObject {
    @Published var user: User
    
    init(user: User) {
        self.user = user
    }
}

struct UserDetailView: View {
    @StateObject var viewModel: UserViewModel
    
    init(user: User) {
        _viewModel = StateObject(wrappedValue: UserViewModel(user: user))
    }
    
    var body: some View {
        Text(viewModel.user.name)
    }
}

It is important to remember that the View initializer with @StateObject should use an underscore before the property name (_viewModel) to access the property wrapper itself, not its value. This is a standard Swift pattern for working with property wrappers in initializers.

Common Mistakes with @StateObject

The most common mistake is using @ObservedObject instead of @StateObject for a View that should own the object. In this case, each time the parent is rebuilt, the object will be recreated, leading to the loss of all accumulated data. This mistake is especially insidious in complex hierarchies with NavigationStack or TabView.

  • Data loss during navigation — if a child screen uses @ObservedObject for its own view model, data will be reset when navigating back and reopening.
  • Memory leak — creating @StateObject in a parent View that is never removed can lead to object accumulation if each child screen also creates @StateObject without control.
  • Object duplication — passing a single ObservableObject to multiple @StateObject in different Views creates several independent instances that do not synchronize with each other.

To avoid these problems, follow a simple rule: one @StateObject per source of truth. If data should be shared across multiple screens — create @StateObject once in the root View and pass it via @ObservedObject or @EnvironmentObject to child elements.

swift
// ❌ Wrong: @ObservedObject for owning an object
struct BadView: View {
    @ObservedObject var vm = ViewModel() // will be recreated on each update!
}

// ✅ Correct: @StateObject for owning
struct GoodView: View {
    @StateObject var vm = ViewModel() // created once for View lifetime
}

Frequently Asked Questions

What is the difference between @StateObject and @State?

@State works with value types (structs, strings, numbers) and stores the value directly in SwiftUI storage. @StateObject works with reference types — classes conforming to ObservableObject. @State is suitable for simple local states, @StateObject is for complex objects with logic and published properties.

Can I use @StateObject in iOS 13?

No, @StateObject is only available from iOS 14 and above. For iOS 13, use @ObservedObject and create the ObservableObject in the parent View via @State with manual lifecycle management. An alternative is to use @State with a struct instead of a class for data that does not require reference semantics.

What happens if I use @StateObject in a child View where the object is passed from the parent?

The child View will create its own copy of the ObservableObject, completely independent from the parent's. Changes in one will not affect the other. This is almost always a mistake: use @ObservedObject to receive an object from the parent and @StateObject only to create a new object inside the View.

When is an object created via @StateObject destroyed?

The object is destroyed when the View that created it is completely removed from the SwiftUI hierarchy. For a screen in NavigationStack, this happens on pop from the navigation stack. For a modal window — when it is dismissed. For TabView — when switching tabs, if the View is not cached.

How do I pass parameters to @StateObject during initialization?

Use a custom init with access to the property wrapper via underscore: _viewModel = StateObject(wrappedValue: MyViewModel(param: value)). This pattern allows passing any parameters to the ObservableObject while maintaining the guarantee of a single object creation during the View's lifetime.

Summary

  • @StateObject — a property wrapper for creating and owning an ObservableObject inside a View, available from iOS 14.
  • Single creation guarantee — the object is initialized once and is not recreated when the View is rebuilt.
  • Source of truth — @StateObject is the source of truth, while @ObservedObject is only an observer.
  • Lifecycle — the object lives as long as the View exists in the SwiftUI hierarchy and is destroyed when it leaves.
  • Initialization with parameters — requires access to the property wrapper via _viewModel and StateObject(wrappedValue:).
  • Ownership mistake — using @ObservedObject to create an object leads to data loss on rebuild.
  • One object — one @StateObject — for shared data, create @StateObject in the root View and pass it to children via @ObservedObject.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also