← Knowledge hub
SwiftUI · ORIGINAL STUDY NOTE

SwiftUI state & ownership — quick reference

A practical comparison of @State, @Binding, classic ObservableObject wrappers and the Observation model.

Who owns the state?

Start with ownership, not the wrapper name. The view that creates and preserves a value generally owns its lifecycle; dependent child views receive bindings or observable dependencies.

SwiftUI state and property wrappers
Wrapper / macroOwnershipWhen to use
@StateLocal state stored by the view.Local values or an @Observable model owned by the view.
@BindingRead/write connection to state owned elsewhere.Child view edits parent-owned state.
@StateObjectView creates and retains a classic ObservableObject.Long-lived ObservableObject using the older observation model.
@ObservedObjectView observes an ObservableObject created elsewhere.Model injected from parent.
@EnvironmentObjectObservableObject injected into view hierarchy.Shared observable dependency; use carefully.
@ObservableMacro enabling modern property tracking.Observable reference models with less boilerplate.
@BindableProvides bindings to properties of an @Observable object.Inputs and controls editing observable properties.

Interview reminder

With modern Observation, @State often owns a newly created @Observable model, and @Bindable exposes bindings when a child needs editable properties. With classic ObservableObject, use @StateObject for a view-owned instance and @ObservedObject for externally owned instances.

Observation model and a binding
import SwiftUI
import Observation

@Observable
final class LoginForm {
    var email = ""
    var password = ""
}

struct LoginView: View {
    @State private var form = LoginForm()

    var body: some View {
        LoginFields(form: form)
    }
}

struct LoginFields: View {
    @Bindable var form: LoginForm

    var body: some View {
        TextField("Email", text: $form.email)
    }
}

Environment, lifetime and two-way data flow

Use @Environment for dependencies supplied by the environment (for example a dismiss action or an injected observable model). @EnvironmentObject belongs to the older ObservableObject model; with Observation, you can inject an @Observable instance through the environment using its type. View-owned @State is transient UI state, not persistent database storage.

Share an observable model through the environment
import SwiftUI
import Observation

@Observable
final class Session {
    var username = "Guest"
}

struct RootView: View {
    @State private var session = Session()
    var body: some View {
        ProfileView().environment(session)
    }
}

struct ProfileView: View {
    @Environment(Session.self) private var session
    var body: some View {
        Text(session.username)
    }
}

Decision rule: a child only reading an observable object's properties normally just needs the model reference. Use @Bindable when a control needs a projected binding. @StateObject and @ObservedObject are still appropriate for older ObservableObject/@Published APIs; mixing them blindly with @Observable is a common mistake.

Pitfalls and trade-offs

Avoid recreating expensive models during View initialization. Place state at the least common owner of consumers, preserve identity through stable view structure, and don't put persistent data directly in @State. Observation is available from iOS 17; lower deployment targets may need compatibility approaches.

SOURCES & DOCUMENTATION

Personal study notes. Check details against your target Swift/iOS version and official documentation.