Observation Framework for SwiftUI

Yagnikbavishi
Simform Engineering
5 min readMay 29, 2024

Unveiling the SwiftUI Observation Framework: A Deep Dive

At WWDC 2023, Apple introduced the new Observation framework, which updates views based on modifications to the observable properties a view body reads rather than changes to the properties of an observable object.

The Observation framework is supported by SwiftUI on iOS 17, iPadOS 17, macOS 14, tvOS 17, and watchOS 10 or later.

In this article, we’ll discuss:

  • What does the Observation framework do?
  • Problems before the Observation Framework
  • Observable macro
  • An example of an observable macro
  • Uses of the @Bindable property wrapper
  • Analyzing view body and view properties updates using the Instruments tool

What does the Observation Framework do?

The Observation framework solves issues related to nested observable objects, simplifies change detection, and provides the necessary resources to implement observable design patterns in SwiftUI.

Problems Before the Observation Framework

Before the Observation Framework, when creating any class with ObservableObject and defining observable properties, it was necessary to annotate those properties with @Published.

Additionally, view updates were triggered whenever any observed property changed.

For example, in previous versions, both views would re-render when we updated the age value. State management was challenging, especially with the use of @StateObject, @EnvironmentObject, and @Published. To address these issues, Apple introduced the Observation framework.

Observable Macro

Before jumping into the Observable macro, let’s first understand what Swift macro is? A Swift macro generates code at compile time, reducing the need for writing repetitive code.

You can learn more about Swift macros here.

@Observable macro is mainly used to expand our types so they can use the observation pattern. With the @Observable macro, we can remove all @Published properties and still redraw the SwiftUI view when changes occur in any property. Let’s see how we can use the @Observable macro.

@Observable macro Example

We can see the Inline macro by right-clicking on the @Observable and then clicking on “Expand Macro”. The code below is auto-generated by Swift during compile time.

Expand macro Example

The above screenshot shows that the @ObservationTracked and other codes are auto-generated by Swift during compile time. The @ObservationTracked annotation is used to track observed values.

If there are additional properties that do not automatically update when changes are detected, we will need to add @ObservationIgnored to them.

An Example of an Observable Macro

Let’s create a simple To-Do List app using the@Observable macro.

To implement observation in an existing or new application, start by substituting the @Observable macro for ObservableObject in class.

First, let’s create the model class with @Observable macro and ObservableObject.

The @ObservationTracked property is present in both name and isCompleted in this case. Also, it is an observable property when used in ObservableObject.

Note: Struct does not support the @Observable macro.

We can observe stored properties and Computed properties inside the @Observable class.

In the observation framework, we use @State and @Enviroment instead of object-based observable objects like @StateObject and @EnviromentObject.

Next, let’s implement the main view of our application, where I’ve passed the environment object.

The view below demonstrates how we can directly create the ViewModel object using the @State property instead of @StateObeject and AppPreferences object using the @Environment property, instead of @EnvironmentObject . It also showcases passing the Observable Object from one view to another.

After that, let’s implement the add task view, where I’ve created the TaskViewModel object using the @State property.

Uses of @Bindable property wrapper

@Bindable is a property wrapper that will support the creation of mutable properties of observable objects. It also includes the SwiftData model objects.

The @State property wrapper will automatically assign bindings if you use @State to construct a local @Observable object. On the other hand, you can use@Bindable to generate bindings for objects that haven’t been bound and are known to be@Observable.

@Bindable is mainly used when you create hole objects as bindable or want to make an individual item bindable in a List or a ForEach loop.

Let’s see an example of a @Bindable property wrapper.

The above view is called when the user clicks on any row from the list. I’ve used the @Bindableobject of the model class passed from the list view. However, the list view isn't invoked when the details sheet is presented.

This is the main advantage of avoiding unnecessary view updates through the observation framework. The above view will demonstrate how the task details view was implemented to prevent unnecessary view updates.

Analyzing View Body and View Properties Updates Using Instruments Tool

First, launch the Instruments tool, then choose SwiftUI from the profiling templates. Next, add a simple task and edit its details using the task details view. After that, record the session using the Instruments tool.

Observations from the screenshot:

  1. The left-hand side screenshot indicates that without using the Observation framework, our views are redrawn 23 times. Using the Observation framework reduces the view redraw count to 18.
  2. The average duration also decreases when using the Observation framework.

By using the Observation framework, we can avoid unnecessary view updates and improve app performance.

The above screenshot shows the view properties updates with and without the observation framework.

Conclusion

Using the Observation framework in SwiftUI, you can achieve a straightforward approach to data observation and maintain a clear and manageable code structure. This framework enhances app performance by updating views only when observed properties read by the body change, rather than when any observed property changes.

If your project targets iOS 17+ deployment, you should use the Observation framework for its numerous benefits, as discussed above.

For more updates on the latest tools and technologies, follow the Simform Engineering Blog.

Follow us: Twitter | LinkedIn

--

--