
この記事は every Tech Blog Advent Calendar 2025 の 24日目の記事です。
はじめに
Swift 5.9で導入された Observation フレームワークは、@Observable マクロを用いた簡潔な記述が可能で、特にSwiftUIのView更新において高いパフォーマンスを発揮します。
一方で、既存の Combine フレームワーク(ObservableObject)からの移行を検討する際、課題となる点がありました。それは ViewModel や Service など、UI以外の場所での値の監視です。Combine では @Published プロパティのProjected Valueを用いて値の変化をストリームとして扱えますが、それと同等の標準的な手段がこれまでの Observation フレームワークには不足していました。
Swift 6.2では、この点が解消されています。@Observable クラスのプロパティの変化を AsyncSequence として監視する機能(Observations)が追加され、Combine に依存することなく、標準APIのみで値の監視が可能になりました。
本記事では、この新しい監視手法について整理します。
ObservableObject による値の監視
Observation フレームワークが登場する以前、SwiftUI では ObservableObject と Combine を用いた状態管理が一般的でした。
@Published プロパティの projected value($)を利用することで、値の変化をストリームとして扱える点は大きな利点でした。
import Combine class CounterViewModel: ObservableObject { @Published var count: Int = 0 private var cancellables = Set<AnyCancellable>() } let viewModel = CounterViewModel() // $をつけることで Publisher として扱える viewModel.$count .sink { value in print("count changed:", value) } .store(in: &viewModel.cancellables)
この仕組みにより、View の更新だけでなく、ViewModel や Service など UI 以外のレイヤーでも、値の変化を一貫した方法で監視できていました。この点は、ObservableObject が持つ強みの一つです。
Observation フレームワークと withObservationTracking
Swift 5.9 で Observation フレームワークが導入されると、View 更新のパフォーマンスと記述の簡潔さは大きく改善されました。一方で、View 以外で値の変化を検知する方法として利用されているのが withObservationTracking です。
import Observation @Observable class Counter { var value: Int = 0 } let counter = Counter() func observe() { withObservationTracking { // 1. ここで値を読む(アクセスする)ことで監視対象にする print("Current value: \(counter.value)") } onChange: { // 2. 変更前(willSetタイミング)に呼ばれる print("Value will change...") Task { // 3. 再帰的に監視を続ける observe() } } }
withObservationTracking は、クロージャ内で読み取られたプロパティへの依存関係を自動的に追跡し、それらが変更された際に onChange を呼び出します。これは SwiftUI の View 更新を支える中核的な仕組みです。
withObservationTracking の課題
しかし、この方法をロジック層で使うには、いくつかの問題がありました。
値そのものを直接受け取れない
onChange で通知されるのは「変更される」という事実のみで、変更後の値は再度読み取る必要があります。
再登録が必要
変更を継続的に監視するためには、onChange の中で再び withObservationTracking を呼び出す再帰的な実装が必要です。
非同期処理との相性
withObservationTracking 自体は同期的であり、Callbackベースの記述になるため、async/await のフローと組み合わせる際に直感的な記述が難しくなります。
Swift 6.2: AsyncSequence による監視の導入
Swift 6.2 では、こうした課題を解消する形で、@Observable クラスのプロパティの変化を AsyncSequence として監視できる Observations 型が追加されました。
import Observation @Observable class Counter { var value: Int = 0 } let counter = Counter() // 監視対象を定義 let counterChanges = Observations { counter.value } Task { for await value in counterChanges { print("value changed:", value) } }
withObservationTracking が持っていた課題は解消され、簡潔な記述で安全に値の変化を監視できるようになりました。
Combine フレームワークに依存せず、自然な形でSwiftの並行処理モデルに直接統合されています。
まとめ
Swift 6.2 の Observations 導入により、ObservableObject が持っていた「Combine による値の監視」という優位性は解消されました。
ただし、この機能を利用するためには iOS 26以降の環境が必要となるため、サポートOSの要件によっては、すぐにプロダクションコードで全面的に採用することは難しいかもしれません。
とはいえ、「View 以外での監視」という最大の課題に対する標準的な解法が示された意義は大きく、安心して Observation への移行を進められる環境が整ったと思います。