← All daysDay 12 of 49 · Part 4: State and data flow
Day 12

@State and @Binding

These are the lesson notes for this day. Its build-along tutorial and flash cards have not been written yet.

Lesson 14

@State and @Binding

@State stores a value that belongs to one view. SwiftUI keeps that storage alive for as long as the view is on screen, even though the view struct itself is re-created over and over. When the value changes, the view's body runs again. Mark @State properties private: only the view that owns the value should set it. @Binding is a reference to state that is owned somewhere else. It lets a child view read and write a value without owning it. You make a binding from state by writing $ in front of the name.

The rule to hold on to: each piece of state has one owner — the single source of truth. Every other view receives either a plain value, if it only reads, or a Binding, if it must write.

As of Xcode 27, @State initializes lazily: an object stored in it is created exactly once for the lifetime of the view (this also applies when running on iOS 17 and later). One result is that giving a @State property a default value and also assigning it in init is now a compile error. Use one or the other.

STATE BODY VIEW ACTION …is rendered by… …which presents… …which invites… …which mutates… the view is a function of its state
How a SwiftUI screen updates: state, body, view, action — and round again
Example
struct CounterScreen: View {
    @State private var count = 0           // this view owns the value

    var body: some View {
        VStack {
            Text("Count: \(count)")        // reads it
            StepButtons(count: $count)     // lends it to a child, read-write
        }
    }
}

struct StepButtons: View {
    @Binding var count: Int                // does not own it; edits the owner's value

    var body: some View {
        HStack {
            Button("-") { count -= 1 }
            Button("+") { count += 1 }
        }
    }
}
Exercise

Build a counter. Move the + and − buttons into a separate child view that receives a Binding. Then add a second child view that only displays the count and receives a plain Int.

After this lesson you should be able to answer
  1. Who owns a @State value, and how long does it live?
  2. What is the difference between passing count and passing $count to a child view?
  3. Why should @State properties be private?
Resources