Lexicon
⌘K
Explore
GuidesNotes
QuestionsWriteMy LibraryBooks
Sign in

Lexicon

a personal knowledge base

← Back to note

Version history

No earlier versions yet — history builds up each time this note is edited.

Current version1 of 1

Swift

SwiftUI state management

SwiftUI rebuilds views from state. The trick is choosing the right property wrapper so ownership and updates flow the way you expect.

The core property wrappers

WrapperOwns the data?Use for
@StateYessimple value-type state local to one view
@BindingNoa two-way reference to state owned elsewhere
@Observable / @StateObjectYesreference-type model owned by the view
@ObservedObjectNoa model passed in from a parent
@EnvironmentObjectNoshared model injected into the hierarchy

@State and @Binding

@State is private to a view. Pass a two-way handle to a child with @Binding using the $ prefix.

struct CounterView: View {
    @State private var count = 0
    var body: some View {
        Stepper('Count: \(count)', value: $count)
        ChildView(count: $count)
    }
}

struct ChildView: View {
    @Binding var count: Int
    var body: some View {
        Button('Reset') { count = 0 }
    }
}

Observable models

For anything beyond a couple of values, use an observable reference type. The view that creates it uses @StateObject (or the newer @Observable macro), children receive it as @ObservedObject.

final class CartModel: ObservableObject {
    @Published var items: [Item] = []
}

struct CartView: View {
    @StateObject private var cart = CartModel()
    var body: some View { /* ... */ EmptyView() }
}

The one mistake to avoid

Do not use @ObservedObject to create a model inside a view. It will be recreated on every redraw and lose its state. The view that creates and owns a model uses @StateObject; everyone downstream uses @ObservedObject.