SwiftUI State and Binding: Owning vs. Sharing Data

In SwiftUI, `@State` is owning the data, while `@Binding` is borrowing a key. A view uses `@State` for its private source of truth, like a toggle's status, and passes a `@Binding` to child views so they can modify the original source.
WHY IT EXISTS: SwiftUI views are lightweight structs that are constantly created and destroyed. To manage data that persists across these view updates and drives UI changes, SwiftUI needed a way to store data outside the view's lifecycle. @State provides this persistent storage, managed by the framework.
THE MENTAL MODEL: Think of @State as a view's private, owned property. It's the single source of truth for data that only this view cares about. @Binding is like a secure key to someone else's property. It allows a child view to read and write a parent's @State variable without owning it, creating a two-way connection.
HOW IT WORKS: When you declare a variable with the @State property wrapper, SwiftUI allocates memory for it outside the view struct. When the value of this state changes, SwiftUI automatically invalidates and re-renders the view and any of its children that depend on that data. To create a binding from a state variable, you prefix its name with a dollar sign ($). This creates a Binding<Value> that you can pass to a child view's property marked with @Binding.
WHEN TO USE IT: Use @State for simple, transient UI state that is local to a single view. Examples include the on/off status of a toggle, the text in a search field, or whether an alert is currently presented. Use @Binding when creating reusable child views that need to modify their parent's state, like a custom stepper or a form input field.
WHEN NOT TO USE IT: Avoid @State for complex data models or data that needs to be shared across many unrelated views. For those cases, use @StateObject, @EnvironmentObject, or the Observation framework. Passing bindings down through many layers of views (called "prop drilling") is also an anti-pattern; @EnvironmentObject is often a better solution.
ONE CANONICAL EXAMPLE: A SettingsView has an @State private var notificationsEnabled = true. It contains a Toggle view. Instead of just passing the boolean value, it passes a binding: Toggle("Enable Notifications", isOn: $notificationsEnabled). The Toggle's isOn parameter is a @Binding. When the user taps the toggle, the Toggle view writes the new value back through the binding, which updates the notificationsEnabled state in SettingsView, causing the UI to update.
Read the original → developer.apple.com
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.