@Binding in SwiftUI: Two-Way Data Flow Between Parent and Child Views

@Binding lets a child view read and write a value that’s actually owned by a parent view, without the child needing to know where that value ultimately lives. It’s SwiftUI’s equivalent of Compose’s state hoisting pattern.

The Problem Without @Binding

A plain parameter is read-only. If a child view takes a Bool as a normal parameter, it can display it but can’t change it in a way the parent sees:

struct ToggleRow: View {
    let isOn: Bool // read-only, can't be changed by this view

    var body: some View {
        Text(isOn ? "On" : "Off")
    }
}

Passing a Two-Way Connection

struct ToggleRow: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle("Enabled", isOn: $isOn)
    }
}

struct ParentView: View {
    @State private var enabled = false

    var body: some View {
        ToggleRow(isOn: $enabled)
    }
}

The $ prefix on $enabled is what creates the Binding<Bool> from the @State property. Now when ToggleRow flips the toggle, it’s mutating the parent’s enabled property directly — there’s no copy, and no manual callback to wire up.

Where the $ Prefix Comes From

Every property wrapper that can produce a binding exposes it via the $ prefix: @State private var name gives you both name (the value) and $name (a Binding<String> to it). This is the same mechanism used when you pass a @State value directly into a built-in control like TextField("Name", text: $name)TextField itself takes a Binding under the hood.

Creating a Binding Without @State

You can also construct a Binding manually with a custom getter and setter — useful when you want a two-way connection to something that isn’t itself @State, like a computed transformation of another value:

let customBinding = Binding(
    get: { celsius },
    set: { celsius = $0 }
)

Quick Rule

Use @Binding whenever a reusable child view needs to modify a value that conceptually belongs to its parent — a toggle, a text field, a stepper. It keeps the child view generic and reusable, since it doesn’t need to know or care where the value is ultimately stored.