State
Hub › iOS › Beginner › State
Goal
You will add a @State property to a view, mutate it from a Button, and watch the view re-render. After this page you will know the one reactivity primitive every SwiftUI app uses to keep the screen in sync with its data.
Prerequisites
Why a struct can't just hold mutable data
A SwiftUI view is a struct, and struct properties are baked in at the time the struct is created. When you tap a button and want a number on screen to go up, you need a piece of memory that:
- Lives outside the struct, so SwiftUI can keep it between re-renders.
- Triggers a re-render of the view when it changes.
@State is the primitive that does both. It's a property wrapper — a Swift feature that lets a keyword like @State add behavior around a property. Behind the scenes, @State stores the value in a small box SwiftUI owns; the view holds a reference to the box.
The two rules:
@Stateproperties are declaredprivate var— they belong to one view.- Mutating a
@Stateproperty re-renders the view whosebodydepends on it.
Make a counter
Open ContentView.swift in HelloIOS and replace the contents with:
import SwiftUI
struct ContentView: View {
@State private var count: Int = 0
var body: some View {
VStack(spacing: 16) {
Text("Count: " + String(count))
.font(.largeTitle)
Button("Increment") {
count = count + 1
}
.font(.headline)
}
.padding()
}
}
#Preview {
ContentView()
}Resume the canvas and click the Live Preview button (a small play icon in the canvas toolbar) so taps register. Tap Increment — the number goes up.
Three things to read:
@State private var count: Int = 0— declares state with an initial value. The type can be inferred (= 0is enough) but spelling it out is clearer at this stage.Button("Increment") { count = count + 1 }—Buttontakes a label and a trailing closure that runs on tap. Inside the closure, you mutatecountdirectly even though the surrounding struct is, technically, immutable. The property wrapper handles it.Text("Count: " + String(count))readscount. SwiftUI tracks which@Stateproperties eachbodyreads and only re-runs the bodies that need to update.
For frontend developers
In Vue, ref(0) is a reactive primitive that triggers a re-render on mutation. SwiftUI's @State plays the same role: a property the view watches, re-render on change. The difference is that SwiftUI runs body again to compute a new view, instead of patching the DOM in place.
Next → List