Skip to content
DeveloperMemos

Checking Out iOS 17's @Observable API

iOS, SwiftUI, Programming, @Observable, Reactive Programming2 min read

iOS 17 introduces the @Observable API, a significant advancement for state management in the SwiftUI framework. This blog post will delve into the essential details of @Observable, providing some insights into its implementation and advantages for iOS developers.

What is @Observable?

The @Observable API in iOS 17 is part of the Observation framework, a Swift-specific implementation of the observer design pattern. This framework simplifies tracking and responding to changes in data within an app, particularly in SwiftUI applications. The @Observable macro allows developers to mark a class as observable, ensuring that any changes in its properties are noticed and can trigger UI updates accordingly. It's pretty much an evolution of ObservableObject which has been around since iOS 13.0. (After spending a couple of years heavily developing in Flutter it makes me really happy to know there is a good state management solution out of the box with SwiftUI).

Implementing @Observable

To use @Observable, you first need to import the Observation framework. A typical implementation involves declaring a class with the @Observable attribute and defining its properties. For example:

1import SwiftUI
2import Observation
3
4@Observable
5class UserModel {
6 var name: String = "Fred"
7 // Other stuff
8}

Integrating with SwiftUI Views

Once you've defined an observable object, you can integrate it into your SwiftUI views. SwiftUI views automatically create a dependency on an observable object when a property of the object is read inside the view's body. When a tracked property changes, SwiftUI updates the view, leading to real-time UI adjustments.

For example:

1struct ContentView: View {
2 @Environment(UserModel.self) private var user: UserModel
3
4 var body: some View {
5 VStack {
6 Text("Hello, \(user.name)!")
7 Button("Change name") {
8 user.name = "Bob"
9 }
10 }
11 .padding()
12 }
13}
14
15@main
16struct MyApp: App {
17 @State private var userModel = UserModel()
18
19 var body: some Scene {
20 WindowGroup {
21 UserProfileView()
22 .environment(userModel)
23 }
24 }
25}

In this example, ContentView automatically updates whenever user.name changes, thanks to the @Observable property of UserModel. When you tap the "Change name" button you'll see that the name automatically changes in the Text view.

UI Screenshot

As you can also see in the example code above you can now use @State to create your model before injecting it with .environment. You might also have noticed that you no longer use @EnvironmentObject anymore - you need to use @Environment instead.

Some Advantages of @Observable

  1. Simplified State Management: @Observable reduces the complexity of managing state in SwiftUI applications. By automatically tracking changes in observable objects, it helps in creating more reactive and dynamic user interfaces. I guess the same goes for using ObservableObject too.
  2. Less Boilerplate: You can get rid of some of the boilerplate associated with ObservableObject because you no longer need to annotate everything with @Published - it all just seems to work out of the box.
  3. Ease of Use: The API simplifies the implementation of the observer pattern, making it more accessible for developers.

Requirements

To use @Observable, developers need to work with iOS 17, macOS 14, and Xcode 15. I think this aspect is kind of disappointing, I wish they had backported it for previous versions of iOS too but this seems to be Apple's approach with any new SwiftUI features or frameworks. In the mean time I did find a library called Perception that has done the work of backporting @Observable. There is a little bit of extra boilerplate but I'm itching to try it out in one of my projects. I'll make sure to write a post about it when I get around to doing so.