SwiftUI ObservableObject vs @Observable: When to Use Each
Choose between ObservableObject and @Observable based on complexity and iOS version requirements.
Quick Summary
| Dimension | ObservableObject | @Observable |
|---|---|---|
| Performance | Similar | Slightly better (less boilerplate) |
| Use case | Complex state management | Simple, lightweight models |
| iOS minimum | iOS 13+ | iOS 17+ |
| Complexity | Higher (requires object binding) | Lower (macro simplifies code) |
| Flexibility | High | Moderate |
ObservableObject — When to Use
ObservableObject has been the go-to protocol for managing state in SwiftUI since its introduction. It works by publishing changes using @Published properties, which trigger view updates when values change.
Use ObservableObject when:
- You need compatibility with iOS versions prior to iOS 17
- Your app has complex state logic or dependencies between properties
- You're using Combine or need fine-grained control over object lifecycle
// Using ObservableObject
class UserSettings: ObservableObject {
@Published var username = "Guest"
}
struct ProfileView: View {
@StateObject var settings = UserSettings()
var body: some View {
TextField("Username", text: $settings.username)
}
}
@Observable — When to Use
The new @Observable macro, introduced in Swift 5.9 and iOS 17, simplifies state management by reducing boilerplate and offering a more declarative syntax. It leverages Swift's new observation framework and automatically tracks changes to properties.
Use @Observable when:
- You're targeting iOS 17 and later
- Your model is simple and doesn't require Combine publishers
- You want cleaner code with less ceremony
// Using @Observable
@Observable
class UserSettings {
var username = "Guest"
}
struct ProfileView: View {
@State var settings = UserSettings()
var body: some View {
TextField("Username", text: $settings.username)
}
}
Side-by-Side Example
Here's the same username editing feature implemented with both approaches:
// Using ObservableObject
class UserSettings: ObservableObject {
@Published var username = "Guest"
}
struct ProfileView: View {
@StateObject var settings = UserSettings()
var body: some View {
TextField("Username", text: $settings.username)
}
}
// Using @Observable
@Observable
class UserSettings {
var username = "Guest"
}
struct ProfileView: View {
@State var settings = UserSettings()
var body: some View {
TextField("Username", text: $settings.username)
}
}
Both implementations achieve the same result, but @Observable removes the need for @Published and allows more flexible state ownership via @State, @Binding, or @ObservedObject.
Decision Guide
Use ObservableObject when you need to support iOS versions before iOS 17 or require Combine integration. Use @Observable for new iOS 17+ apps where code simplicity and reduced boilerplate are priorities.