All posts
Swift SwiftUI iOS Swift Macros

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 @ObservationIgnored for stored properties you don’t want tracked
  • Codable conformance still works but needs explicit CodingKeys if 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.