Mastering @Observable: Introduction to Swift Observation

Andy Kolean
8 min readJul 12, 2024

--

Discover how Swift’s @Observable macro simplifies state management in iOS apps. Learn about its benefits, implementation, and best practices in this comprehensive guide.

Unlock the power of Swift’s @Observable macro for efficient state management in iOS apps.

Mastering Swift Observation Series

This article is part of the Mastering Swift Observation series. Here’s what we cover in the series:

  1. Mastering @Observable: Introduction to Swift Observation
  2. Simplifying State Management with @Observable and @ObservedObject
  3. Using @Observable on Older iOS Versions with Perception

1. Introduction

State management has always been a cornerstone of application development. In Swift, managing state has evolved significantly over the years. Initially, developers relied on manual state management techniques, which often led to complex and error-prone codebases. As applications grew in size and complexity, the need for more robust and maintainable state management solutions became evident.

With the introduction of frameworks like Combine and SwiftUI, there was a paradigm shift in how state is managed. Combine introduced a declarative way to handle asynchronous events, while SwiftUI leveraged a reactive approach to automatically update the user interface based on state changes. Despite these advancements, developers still faced challenges related to boilerplate code, performance, and synchronization across different parts of the app.

Starting with iOS 17, iPadOS 17, macOS 14, tvOS 17, and watchOS 10, SwiftUI provides support for the Observation framework, a Swift-specific implementation of the observer design pattern. This framework offers several benefits over the previous ObservableObject protocol, including the ability to track optionals and collections of objects and the use of existing data flow primitives like State and Environment instead of object-based equivalents such as StateObject and EnvironmentObject. Additionally, the Observation framework updates views based on changes to the observable properties that a view’s body reads, which can help improve app performance.

Swift’s new @Observable macro addresses these challenges head-on. The macro is designed to simplify state management by providing a declarative and efficient way to observe changes in state and automatically update the user interface. This macro reduces the amount of boilerplate code, ensures consistent state synchronization, and enhances the performance of Swift applications.

In this post, we will delve into the specifics of the Observation framework, highlighting its key features and the problems it solves. We will also discuss the importance of observation in the context of modern Swift development and the platforms that support this framework. By the end of this post, you will have a comprehensive understanding of why the Observation framework is a significant addition to the Swift ecosystem.

2. Overview of the Observation Framework and @Observable Macro

The Observation framework in Swift is a robust, type-safe, and performant implementation of the observer design pattern. This framework allows an observable object to maintain a list of observers and notify them of specific or general state changes, thereby decoupling objects and enabling implicit distribution of updates across multiple observers.

Definition

The Observation framework provides the following capabilities:

  • Marking a type as observable: Using the @Observable macro.
  • Tracking changes within an instance of an observable type: Using the withObservationTracking(_:onChange:) function.
  • Observing and utilizing those changes elsewhere: Such as in an app’s user interface.

To declare a type as observable, attach the @Observable macro to the type declaration. This macro generates source code at compile time that adds observation support to the type.

Features

Declarative State Observation:

  • The @Observable macro leverages Swift's powerful declarative syntax, allowing developers to specify which types should be observable without writing extensive boilerplate code.

Automatic UI Updates:

  • With the Observation framework, the user interface is automatically updated whenever an observed property’s value changes. This ensures that the UI is always in sync with the underlying state, providing a seamless and responsive user experience.

Type-Safe and Performant:

  • The framework is designed to be type-safe, ensuring that only valid types and properties are observed. It is also optimized for performance, reducing the overhead associated with state management.

Integration with Existing Frameworks:

  • The Observation framework is designed to work seamlessly with existing Swift frameworks, such as SwiftUI and Combine. This integration allows developers to leverage the full power of these frameworks while benefiting from the simplified state management provided by the Observation framework.

Example Usage

Here is a simple example to illustrate how the @Observable macro can be used in a Swift application:

import SwiftUI
import Combine

@Observable
class Car {
var name: String = ""
var needsRepairs: Bool = false

init(name: String, needsRepairs: Bool = false) {
self.name = name
self.needsRepairs = needsRepairs
}
}

struct ContentView: View {
@State private var car = Car(name: "Tesla")

var body: some View {
VStack {
TextField("Enter car name", text: $car.name)
Text("Car name: \(car.name)")
}
.padding()
}
}

func render(cars: [Car]) {
withObservationTracking {
for car in cars {
print(car.name)
}
} onChange: {
print("Schedule renderer.")
}
}

In this example:

  • The Car class is marked as observable using the @Observable macro.
  • The ContentView struct uses the State property wrapper to manage the Car object. When the name property changes, the Text view is automatically updated to reflect the new value.
  • The render function demonstrates how to track changes using the withObservationTracking(_:onChange:) function. It prints the names of the cars and schedules a re-render when any car's name changes.

Supported Platforms

The Observation framework is designed to be used across all major Apple platforms, ensuring that developers can take advantage of its benefits regardless of their target platform.

iOS:

  • The Observation framework can be used in iOS applications to simplify state management and improve performance, making it easier to create responsive and dynamic user interfaces.

macOS:

  • macOS developers can leverage the Observation framework to manage state in their applications, ensuring that the user interface remains consistent and up-to-date with minimal effort.

tvOS and watchOS:

  • The Observation framework is also supported on tvOS and watchOS, allowing developers to use the same state management techniques across all Apple platforms. This consistency makes it easier to develop and maintain applications that target multiple platforms.

3. Importance of Observation

The Observation framework in Swift is more than just a tool for state management; it represents a significant advancement in how developers can create responsive and efficient applications. This section explores the importance of observation in modern Swift development and the key benefits it brings to the table.

State Management

Role in Managing State:

  • The Observation framework simplifies state management by automating the process of tracking changes to data and updating the user interface accordingly. This ensures that the UI always reflects the current state of the application, providing a consistent and seamless user experience.

Reduction of Boilerplate Code:

  • Traditional state management often involves extensive boilerplate code to manually track and update state changes. The Observation framework, with its @Observable macro, reduces this boilerplate by automatically generating the necessary code to observe changes and update the UI.

Performance

Efficient State Synchronization:

  • By updating views only when properties that are directly read by the view’s body change, the Observation framework ensures efficient state synchronization. This selective updating reduces unnecessary re-renders, improving the overall performance of the application.

Optimized for Reactive Programming:

  • The Observation framework leverages reactive programming principles, which are foundational to modern frameworks like SwiftUI. This integration enhances the performance and responsiveness of applications by ensuring that the UI reacts promptly to state changes.

Flexibility and Integration

Support for Existing Primitives:

  • The framework supports existing data flow primitives like State and Environment, allowing developers to use familiar tools while benefiting from the enhancements provided by the Observation framework. This compatibility simplifies the transition for existing projects.

Incremental Adoption:

  • Developers can adopt the Observation framework incrementally, starting with a single data model type and gradually updating other parts of the application. This flexibility allows for a smoother transition and reduces the risk of introducing bugs during the migration process.

Use Cases

Tracking Optionals and Collections:

  • Unlike the ObservableObject protocol, the Observation framework can track optionals and collections of objects. This capability is crucial for applications that need to handle complex data structures and dependencies.

Improved Data Flow:

  • By using observation to update views based on changes to specific properties rather than any property change, the framework improves data flow within the application. This targeted approach enhances the accuracy and efficiency of state updates.

4. Problems Solved by Observation

The Observation framework addresses several key challenges that developers face when managing state in Swift applications. By introducing the @Observable macro and other observation tools, the framework simplifies and enhances the state management process. This section highlights the primary problems solved by the Observation framework.

Reduction of Boilerplate Code

Eliminating Repetitive Code Patterns:

  • Traditional state management often requires extensive boilerplate code to manually track state changes and update the UI. The @Observable macro significantly reduces this boilerplate by automatically generating the necessary code for observing and updating properties. This automation makes the codebase more concise and easier to maintain.

Simplifying Property Declarations:

  • The Observation framework eliminates the need for the @Published property wrapper. Instead, properties within an @Observable type are automatically tracked based on
  • their accessibility to observers. This change simplifies property declarations and reduces the amount of boilerplate code required.

Ensuring State Synchronization

Consistent State Management:

  • Managing state synchronization across different parts of an application can be complex and error-prone. The Observation framework ensures consistent state synchronization by automatically updating views when observable properties change. This automatic synchronization helps avoid state mismatches and inconsistencies, ensuring that the UI always reflects the current state.

Selective Updates for Performance:

  • Unlike the ObservableObject protocol, which triggers view updates for any property change, the Observation framework updates views only when properties that are directly read by the view's body change. This selective updating reduces unnecessary re-renders and improves the overall performance of the application.

Optimizing Performance

Efficient State Updates:

  • The Observation framework optimizes state updates by leveraging reactive programming principles. This optimization ensures that the UI responds promptly to state changes, providing a smooth and responsive user experience.

Reducing Overhead:

  • By minimizing the amount of code required to manage state and automating the observation process, the Observation framework reduces the overhead associated with state management. This reduction in overhead contributes to better performance and efficiency in Swift applications.

Supporting Complex Data Structures

Tracking Optionals and Collections:

  • One of the significant limitations of the ObservableObject protocol is its inability to track optionals and collections of objects effectively. The Observation framework addresses this limitation by providing robust support for tracking changes in optionals and collections. This capability is essential for applications that need to handle complex data structures and dependencies.

Enhanced Data Flow Management:

  • The framework improves data flow management by using observation to update views based on changes to specific properties rather than any property change. This targeted approach enhances the accuracy and efficiency of state updates, leading to more reliable and maintainable applications.

Conclusion

The Observation framework and the @Observable macro address several key challenges in state management for Swift applications. By reducing boilerplate code, ensuring consistent state synchronization, optimizing performance, and supporting complex data structures, the framework provides a powerful and efficient solution for managing state in Swift applications.

In the next post, we will delve into the basics of @Observable and @ObservedObject, exploring how these annotations simplify state management and providing examples of their usage.

--

--

Andy Kolean

Hi there! I’m Andy Kolean, an iOS Developer who loves creating high-quality applications for Apple platforms.