One of the most magical things about SwiftUI is how little code you need to keep your UI in sync with your data. Declare a property with @State, mutate it, and your view just updates. Simple, right?  
But if you’ve ever wondered what’s really happening when you use @State, this article takes a peek under the hood.  
  
  
  Why Do We Even Need @State?
Lets take below example
struct CounterView: View {
    private var count = 0
    var body: some View {
        Button("Tap \(count)") {
            count += 1
        }
    }
}
The above code looks good right? Create a button that says “Tap Count” plus the number of times the button has been tapped, then add 1 to tapCount whenever the button is tapped
But this doesn't compile because CounterView is a struct and you can't change properties freely. Okay lets keep this aside for a minute, What other problems do we have with above code?
SwiftUI views get created and destroyed all the time as SwiftUI updates the UI tree.
If count were just a plain Int property on a struct, it would reset to 0 every time the view was recreated. We’d lose our state instantly.
This is where @State comes in. It tells SwiftUI:  
“Hey, this piece of data should survive across view reloads.”
For example:
struct CounterView: View {
    @State private var count = 0
    var body: some View {
        Button("Tap \(count)") {
            count += 1
        }
    }
}
  
  
  What @State Really Is
@State is a property wrapper provided by SwiftUI. When you write:
@State private var count = 0
Under the hood, the State type looks like this (simplified assumption):
@propertyWrapper
struct State<Value>: DynamicProperty {
    init(initialValue: Value)
    var wrappedValue: Value { get nonmutating set }
    var projectedValue: Binding<Value> { get }
}
So, count is just wrappedValue inside a property wrapper. But the important part is where that value is stored.
Notice the nonmutating set on wrappedValue — this is what allows us to change a @State variable inside a struct View even though structs are normally immutable in SwiftUI’s body.
Note: See the section on DynamicProperty below to understand how it helps the State property wrapper
Where Does the Value Live?
When you set @State var count = 0, the value 0 is not stored inside your view struct
Instead, SwiftUI maintains a hidden state storage that lives outside the view. Each piece of state is associated with the identity of the view in the UI hierarchy.
Think of it like this:
Every time SwiftUI re-creates CounterView, it reattaches _count to the same storage box. That’s why the state survives view updates.  
How Updates Trigger UI Refresh
When you mutate a @State variable:
count += 1
The setter does two things:
- Updates the underlying stored value in SwiftUI’s state system.
 - Marks the view as dirty and schedules a re-render.
 
That’s why the UI automatically refreshes with the new value — SwiftUI invalidates the current body and recomputes it.
  
  
  @State and Binding
When you use the $ prefix, you don’t get the raw value — you get a Binding:
$count  // Binding<Int>
A Binding is essentially a lightweight reference to the same state storage. This allows child views to read and write the parent’s state without owning it.  
Example:
TextField("Name", text: $name)
Here, TextField can directly mutate the name state in the parent view.  
  
  
  Lifecycle of a @State Property
At runtime, the flow looks like this:
- SwiftUI builds the view struct (
CounterView). - It sees a 
@Stateproperty and checks if there’s existing storage. - If storage exists, it reuses it; otherwise, it creates a new storage box.
 - The view’s body runs, reading 
countviawrappedValue. - If you change 
count, SwiftUI updates storage and schedules the body to recompute. 
  
  
  A Toy Implementation of @State
SwiftUI is a closed-source Apple framework, but because property wrappers in Swift are a public language feature, we can only imagine and re-implement how @State could work.
Of course, the real @State is tightly integrated with SwiftUI’s runtime. But we can mimic the idea with an example property wrapper:
@propertyWrapper
public struct State<Value>: DynamicProperty {
    private var storage: StateStorage<Value>
    public init(wrappedValue: Value) {
        self.storage = .init(initialValue: wrappedValue)
    }
    public var wrappedValue: Value {
        get { storage.value }
        nonmutating set { storage.value = newValue }
    }
    public var projectedValue: Binding<Value> {
        Binding(
            get: { self.wrappedValue },
            set: { self.wrappedValue = $0 }
        )
    }
}
This obviously doesn’t persist across view recreations, but it shows the principle:
- 
wrappedValuegives you the value. - Mutating it triggers a refresh.
 - 
$propertygives you aBinding. 
The above example is inspired from the State property wrapper implementation in OpenSwiftUI project here
What’s DynamicProperty Doing Here?
In SwiftUI, some property wrappers (like @State, @ObservedObject, @EnvironmentObject, @AppStorage, etc.) conform to the DynamicProperty protocol.
public protocol DynamicProperty {
    mutating func update()
}
This protocol lets SwiftUI know:
- 
This property participates in the SwiftUI data flow.
- SwiftUI calls 
update()before recomputing the view’s body. - That’s how the property wrapper gets a chance to reconnect to the right storage or refresh its bindings.
 
 - SwiftUI calls 
 - 
Why it matters for @State:
- Every time SwiftUI re-renders a view, a new struct instance of your view is created.
 - Without 
DynamicProperty, the State wrapper would just get re-initialized, losing its data. - With 
DynamicProperty, SwiftUI ensures the wrapper reattaches to its persistent state storage instead of resetting. 
 
Understanding how @State works demystifies a lot of SwiftUI. It’s not magic — it’s just clever indirection and lifecycle management. By keeping state outside the view struct, SwiftUI ensures your UI is always a pure function of its data, while your data itself remains persistent.  
So the next time you write @State var count = 0, remember: you’re really just getting a little persistent box managed by SwiftUI, with automatic UI updates wired in.
References
1) Opensource implementation of SwiftUI - https://github.com/OpenSwiftUIProject/OpenSwiftUI
2) https://forums.swift.org/t/how-does-swiftui-find-state-properties-in-views/79984/3
3) https://fatbobman.com/en/posts/swiftui-state/

    
Top comments (0)