How @State works in SwiftUI
The @State keyword is a @propertyWrapper , a feature introduced in Swift 5.1. SwiftUI manages the storage of any property you declare as a state. When the state value changes, the view invalidates its appearance and recomputes the body. Use the state as the single source of truth for a given view.
Why @State only works with structs
SwiftUI’s State
property wrapper is designed for simple data that is local to the current view, but as soon as you want to share data between views it stops being useful.
Let’s break this down with some code – here’s a struct to store a user’s first and last name:
struct User {
var firstName = "Bilbo"
var lastName = "Baggins"
}
We can now use that in a SwiftUI view by creating an @State
property and attaching things to $user.firstName
and $user.lastName
, like this:
struct ContentView: View {
@State private var user = User()
var body: some View {
VStack {
Text("Your name is \(user.firstName) \(user.lastName).")
TextField("First name", text: $user.firstName)
TextField("Last name", text: $user.lastName)
}
}
}
First, that structs always have unique owners, whereas with classes multiple things can point to the same value. And second, that classes don’t need the mutating
keyword before methods that change their properties, because you can change properties of constant classes.
When we use @State
, we’re asking SwiftUI to watch a property for changes. So, if we change a string, flip a Boolean, add to an array, and so on, the property has changed and SwiftUI will re-invoke the body
property of the view.
When User
was a struct, every time we modified a property of that struct Swift was actually creating a new instance of the struct. @State
was able to spot that change, and automatically reloaded our view. Now that we have a class, that behavior no longer happens: Swift can just modify the value directly.
Remember how we had to use the mutating
keyword for struct methods that modify properties? This is because if we create the struct’s properties as variable but the struct itself is constant, we can’t change the properties – Swift needs to be able to destroy and recreate the whole struct when a property changes, and that isn’t possible for constant structs. Classes don’t need the mutating
keyword, because even if the class instance is marked as constant Swift can still modify variable properties.