← All courses

State & Binding

🗓 May 31, 2026 ⏱ 3 min read

State is what makes UI interactive

Because SwiftUI redraws when data changes, you need a way to tell it “watch this value”. That’s what @State does. When a @State value changes, SwiftUI re-runs the view’s body and updates the screen.

struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack {
            Text("Count: \(count)")
            Button("Add one") {
                count += 1        // changing state -> the Text updates
            }
        }
    }
}

Mark @State properties private — they are owned by this view and shouldn’t be set from outside. Use @State for simple values local to one view (a toggle, a text field, a selected tab).

The $ prefix: a binding

Some views need to read and write a value — like a TextField that edits text. For that you pass a binding, created with the $ prefix. A binding is a two-way connection to the state.

struct NameView: View {
    @State private var name = ""
    var body: some View {
        VStack {
            TextField("Your name", text: $name)   // $ = two-way binding
            Text("Hello, \(name)")                // updates as you type
        }
        .padding()
    }
}

Without the $ you pass the value (read-only); with the $ you pass a binding (read-write). This distinction trips up beginners, so remember: $ means “a two-way link to this state”.

@Binding: let a child edit the parent’s state

When you break UI into small views, a child often needs to change a value the parent owns. The parent keeps the @State; the child declares a @Binding and the parent passes $value down.

struct ToggleRow: View {
    let title: String
    @Binding var isOn: Bool          // a link to the parent's state
    var body: some View {
        Toggle(title, isOn: $isOn)
    }
}

struct SettingsView: View {
    @State private var notifications = true
    var body: some View {
        ToggleRow(title: "Notifications", isOn: $notifications)
    }
}

Where should state live?

Keep state as low as possible — in the view that actually uses it. If two views need the same value, lift it to their common parent and pass bindings down. This “single source of truth” principle keeps the UI predictable.

Common mistakes

  • Forgetting the $ when a view needs a binding (compile error or no two-way update).
  • Making @State non-private or trying to set it from another view.
  • Duplicating state in two places instead of sharing one source of truth via a binding.
Summary: @State holds a view’s own changing data; the $ prefix makes a two-way binding; @Binding lets a child edit the parent’s state. Keep one source of truth and pass bindings down.