Getting Started with SwiftUI's Observable Macro
The new @Observable macro in Swift 5.9+ revolutionizes state management. Let’s see how it works.
The Problem with ObservableObject
// Old way - ObservableObject
class UserStore: ObservableObject {
@Published var name: String = ""
@Published var email: String = ""
@Published var isPremium: Bool = false
}
Issues with this approach:
- Must use
class(reference type) - Thread safety is your responsibility
@Publishedhas overhead
Enter @Observable
// New way - @Observable macro
@Observable
class User {
var name: String = ""
var email: String = ""
var isPremium: Bool = false
}
That’s it! No @Published, no ObservableObject.
Using @Observable
@Observable
class AppState {
var currentUser: User?
var isLoading: Bool = false
var notifications: [Notification] = []
}
struct ContentView: View {
@State private var appState = AppState()
var body: some View {
VStack {
if let user = appState.currentUser {
Text("Welcome, \(user.name)")
} else {
Text("Please log in")
}
}
}
}
Property Access Tracking
The macro automatically tracks which properties you access:
@Observable
class ShoppingCart {
var items: [CartItem] = []
var discountCode: String = ""
var total: Double {
items.reduce(0) { $0 + $1.price }
}
var isEmpty: Bool {
items.isEmpty
}
}
Migration Guide
| ObservableObject | @Observable |
|---|---|
@Published var |
var |
ObservableObject protocol |
@Observable macro |
objectWillChange |
Automatic |
@ObservedObject |
@State or direct access |
When to Use What
- @State: Local view state
- @State private var: Private observable
- @Bindable: Two-way binding
- @Environment: Shared app-wide state
struct DetailView: View {
@Bindable var user: User // Two-way binding
var body: some View {
Form {
TextField("Name", text: $user.name)
Toggle("Premium", isOn: $user.isPremium)
}
}
}
The @Observable macro makes SwiftUI state management cleaner and more intuitive.