If you use @State
with a struct, your SwiftUI view will update automatically when a value changes, but if you use @State
with a class then you must mark that class with @Observable
if you want SwiftUI to watch its contents for changes.
To understand what's going on, let's take a look at this code in more detail:
@Observable
class User {
var firstName = "Bilbo"
var lastName = "Baggins"
}
This is a class with two string variables, but it starts with @Observable
. That tells SwiftUI to watch each individual property inside the class for changes, and reload any view that relies on a property when it changes. I know that might sound a bit like magic, but it's not – it's just a whole lot of work carefully hidden.
I want to dig in a little here so you can see what's really happening, and to do that I'd like you to add another import
line to the top, next to import SwiftUI
:
import Observation
That @Observable
line is a macro, which is Swift's way of quietly rewriting our code to add extra functionality. Now that we've imported the framework it comes from, Xcode can do something really neat: if you right-click on @Observable
in your code, you can choose Expand Macro to see exactly what rewriting is happening – Xcode will show you all the hidden code that's being generated.
I'm not going to write out the whole macro expansion here because it's a lot, but I do want to point out three things:
@ObservationTracked
, which means Swift and SwiftUI are watching them for changes.@ObservationTracked
you can expand that macro too – yes, it's a macro inside a macro. This macro has the job of tracking whenever any property is read or written, so that SwiftUI can update only views that absolutely need to be refreshed.Observable
protocol. This is important, because some parts of SwiftUI look for this to mean "this class can be watched for changes."All three of those are important, but it's the middle one that is doing the heavy lifting: iOS keeps track of every SwiftUI view that reads properties from an @Observed
object, so that when a property changes it can intelligently update all the views that depend on it while leaving the others unchanged.
When working with structs, the @State
property wrapper keeps a value alive and also watches it for changes. On the other hand, when working with classes, @State
is just there for keeping object alive – all the watching for changes and updating the view is taken care of by @Observable
.
SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.
Link copied to your pasteboard.