- Published at
Using @Observable on Older iOS Versions with Perception

Learn how to use the @Observable macro in SwiftUI applications on iOS 13+ with the Perception library from Point-Free, ensuring modern state management on older platforms.
- Authors
-
-
- Name
- Andy Kolean
-
Table of Contents
1. Introduction
Swift 5.9 introduced the @Observable
macro, a powerful tool for state management in SwiftUI applications. However, its use is limited to the latest Apple platforms: iOS 17, macOS 14, tvOS 17, and watchOS 10. Given that a significant portion of users still operate on older versions, many developers are unable to utilize these new features. The Perception library from Point-Free addresses this by back-porting the capabilities of @Observable
to work on older platforms, starting from iOS 13, macOS 10.15, tvOS 13, and watchOS 6.
In this post, we will explore how to use the @Perceptible
macro from the Perception library, the motivations behind it, and its significance. This guide will help you implement @Perceptible
in your projects, allowing you to take advantage of modern state management techniques on older platform versions.
2. Overview
The Perception library replicates the functionality of @Observable
and withObservationTracking
from Swift 5.9, but extends support to older Apple platforms. This means you can begin using these advanced observation tools in your SwiftUI projects without waiting for broader adoption of the latest OS versions.
3. Usage of @Perceptible
Step-by-Step Guide
To integrate @Perceptible
into your project, follow these steps:
-
Import the Perception library:
import Perception
-
Mark your view model as
@Perceptible
:@Perceptible class SettingsViewModel { var name: String = "John Doe" var age: Int = 30 }
-
Use the view model in your SwiftUI view, and wrap your content with
WithPerceptionTracking
:struct SettingsView: View { let viewModel = SettingsViewModel() var body: some View { WithPerceptionTracking { VStack { Text("Username: \(viewModel.name)") Button(action: { viewModel.name = "Jane Doe" }) { Text("Change Username") } } .padding() } } }
In this example, the SettingsViewModel
class is marked as @Perceptible
, making its properties observable and allowing the SettingsView
to react to changes in SettingsViewModel
. The WithPerceptionTracking
wrapper ensures the view subscribes to the model’s changes. If you forget to wrap your view, a runtime warning will alert you to the oversight.
4. Integration with SwiftUI
@Perceptible
integrates seamlessly with SwiftUI, enabling effective state management even on older platform versions. This ensures your user interface remains responsive and up-to-date with minimal code changes.
Example
Here’s an example demonstrating how @Perceptible
can be used in a SwiftUI view:
@Perceptible
class SettingsViewModel {
var name: String = "John Doe"
var age: Int = 30
}
struct ContentView: View {
@Perception.Bindable var viewModel = SettingsViewModel()
var body: some View {
WithPerceptionTracking {
VStack {
TextField("Name", text: $viewModel.name)
Stepper("Age: \(viewModel.age)", value: $viewModel.age)
}
.padding()
}
}
}
In this setup, the ContentView
automatically updates when the name
or age
properties of SettingsViewModel
change, showcasing how @Perceptible
aids in state management within SwiftUI.
5. How the Perception Library Works
The Perception library leverages the open-source nature of Swift’s Observation framework. By adapting the @Observable
macro to @Perceptible
, it ensures compatibility with older platforms. The library differentiates its tools from Apple’s native ones while also enabling a seamless transition to native tools when running on the latest platforms.
The Perception library includes a PerceptionRegistrar
type, which determines whether to use the native ObservationRegistrar
or a custom back-ported implementation based on the platform version. When running on iOS 17 and newer, the library defers to the native observation tools. On older platforms, it uses its own implementation to handle observation.
Methods such as access
, willSet
, didSet
, and withMutation
in PerceptionRegistrar
are dynamically selected at runtime, ensuring that the appropriate observation tools are used depending on the platform capabilities. This allows developers to use modern state management techniques across a wide range of iOS and macOS versions.
Conclusion
The @Perceptible
macro provides a powerful solution for back-porting the capabilities of @Observable
to earlier versions of iOS and macOS. By leveraging @Perceptible
, you can implement modern state management techniques and maintain a responsive application, regardless of the platform version.
Implementing @Perceptible
in your projects ensures compatibility with older devices while benefiting from the enhanced state management features provided by the Observation framework. Try integrating @Perceptible
into your projects to simplify state management and improve application performance.