SwiftUI's @Observable Macro: A Practical Guide
With the introduction of Swift 5.9 and iOS 17, Apple gave us the @Observable macro — a significant improvement over the older ObservableObject protocol that many SwiftUI developers had grown accustomed to.
The Old Way: ObservableObject
Before @Observable, tracking state in a reference type looked like this:
class UserStore: ObservableObject {
@Published var name: String = ""
@Published var isLoggedIn: Bool = false
}
Every property you wanted to publish needed the @Published wrapper, and your view needed @ObservedObject or @StateObject to subscribe to it.
The New Way: @Observable
With the macro, the same class becomes:
@Observable
class UserStore {
var name: String = ""
var isLoggedIn: Bool = false
}
No @Published, no protocol conformance. The macro expands at compile time and instruments each stored property automatically.
Key Benefits
Selective re-rendering: SwiftUI now tracks which properties a view actually reads, not just that the model changed. This means fewer unnecessary re-renders.
Simpler view code: You can use @State for model objects directly without choosing between @StateObject and @ObservedObject.
struct ContentView: View {
@State private var store = UserStore()
var body: some View {
Text(store.name)
}
}
Works in plain Swift: @Observable is not a SwiftUI-only feature. You can use it in any Swift 5.9+ context.
Gotchas
- Requires iOS 17 / macOS 14 minimum deployment target
- Computed properties are not observed automatically — you need
@ObservationIgnoredfor stored properties you don’t want tracked Codableconformance still works but needs explicitCodingKeysif you use@ObservationIgnored
Conclusion
@Observable is a clean, modern replacement that reduces boilerplate and improves performance. If your minimum deployment target is iOS 17+, there’s no reason to stick with ObservableObject.