Observation Framework for SwiftUI
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.
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.
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 @Bindable
object 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:
- 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 to18
. - 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.