Mobile Development 17 min read

Understanding @State and @Binding in SwiftUI: Usage, Implementation, and Mutable vs Immutable Behavior

In SwiftUI, this article explains the @State and @Binding property wrappers, demonstrates their usage with concrete Swift code, explores why @State works only with value types, shows internal implementation details, and clarifies mutable versus immutable behavior within view structs.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Understanding @State and @Binding in SwiftUI: Usage, Implementation, and Mutable vs Immutable Behavior

@State

A property wrapper type that can read and write a value managed by SwiftUI.

@State is designed for state management of value types inside a struct . It must be declared private and can only be accessed within the view's body or methods called from body . Changing a @State property triggers a view refresh.

struct User {
    var firstName = "Bilbo"
    var lastName = "Baggins"
}

struct ContentView: View {
    @State private var user = User() //1
    var body: some View {
        VStack {
            Text("Your name is \(user.firstName) \(user.lastName).") //2
            TextField("First name", text: $user.firstName) //3
            TextField("Last name", text: $user.lastName)
        }
    }
}

Access to a @State‑decorated property is only allowed inside body or its called methods; external mutation is prohibited.

Read‑write access requires the $ prefix (see comment //3); read‑only access uses the plain name (comment //2).

The property should be marked private to keep the state confined to the view.

If the struct User is replaced by class User , @State no longer detects changes because classes are reference types; @State only monitors value‑type mutations.

@State Detects Value Types

Struct copies are independent; modifying one view's copy does not affect another.

When using a class, both views share the same instance, so changes are not observed by @State.

@Binding

A property wrapper type that can read and write a value owned by a source of truth.

@Binding creates a two‑way connection between a stored state and a child view, allowing the child to modify the parent’s state without copying the value.

struct Product: Identifiable {
    var isFavorited: Bool
    var title: String
    var id: String
}

struct FilterView: View {
    @Binding var showFavorited: Bool //3
    var body: some View {
        Toggle(isOn: $showFavorited) { //4
            Text("Change filter")
        }
    }
}

struct ProductsView: View {
    let products: [Product] = [
        Product(isFavorited: true, title: "ggggg", id: "1"),
        Product(isFavorited: false, title: "3333", id: "2")
    ]
    @State private var showFavorited: Bool = false //1
    var body: some View {
        List {
            FilterView(showFavorited: $showFavorited) //2
            ForEach(products) { product in
                if !self.showFavorited || product.isFavorited {
                    Text(product.title)
                }
            }
        }
    }
}

Comment 1: showFavorited is stored with @State in the parent.

Comment 2: The parent passes a $showFavorited binding to the child.

Comment 3: The child receives the binding via @Binding var showFavorited .

Comment 4: Toggling the switch updates the single source of truth, causing the list to re‑filter.

Mutable vs Immutable Variables in a View

The following example shows why a @State variable can be changed inside an action closure, while a plain variable cannot.

struct StateMutableView: View {
    @State private var flag = false
    private var anotherFlag = false
    mutating func changeAnotherFlag(_ value: Bool) {
        self.anotherFlag = value
    }
    var body: some View {
        Button(action: {
            self.flag = true //1 ok
            // self.anotherFlag = true //2 compile error: self is immutable
            // changeAnotherFlag(true) //3 compile error: mutating method on immutable self
        }) {
            Text("Test")
        }
    }
}

Reason:

flag is managed by @State, whose setter is nonmutating ; the wrapper updates external storage, not the struct itself.

anotherFlag is a plain stored property; the view struct is immutable inside the closure, so it cannot be modified.

Why a Computed Getter Can Modify Self When Marked mutating

A computed property’s getter is non‑mutating by default, but adding the mutating keyword allows it to change internal storage (e.g., UserDefaults) and requires the instance to be a var .

SwiftUI View Protocol Constraints

The body property of View is a read‑only computed property; it cannot be declared mutating get . Therefore, attempts to make body mutable result in a protocol‑conformance error.

Internal Implementation of @State

SwiftUI synthesizes three backing properties for a declaration like @State var user = User() :

private var _user: State
= State(initialValue: User())
private var $user: Binding
{ _user.projectedValue }
private var user: User {
    get { _user.wrappedValue }
    nonmutating set { _user.wrappedValue = newValue }
}

The projected value ( $user ) is read‑only; attempts to assign to it produce compile‑time errors.

Summary

@State manages mutable state for value types in SwiftUI structs, keeping the state lifecycle aligned with the view.

@Binding bridges stored state to child views, enabling two‑way data flow via the $ prefix.

Property wrappers modify external storage rather than the struct itself, which explains why @State variables can be changed inside view actions while plain properties cannot.

References

https://developer.apple.com/documentation/swiftui/state

https://www.hackingwithswift.com/quick-start/swiftui/whats-the-difference-between-observedobject-state-and-environmentobject

https://kateinoigakukun.hatenablog.com/entry/2019/03/22/184356

iosSwiftSwiftUIbindingStatePropertyWrapper
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.