SwiftUI ObservableObject と @Published の基本

SwiftUI でビューとデータを結びつける仕組みの中心にあるのが ObservableObject プロトコルだ。このプロトコルに準拠したクラスは、自身の変更をビューに通知できるようになる。変更通知のトリガーとなるのが @Published プロパティラッパーであり、この2つの組み合わせが SwiftUI のリアクティブな UI 更新を支えている。

ObservableObject の役割

ObservableObject は Combine フレームワークで定義されたプロトコルで、objectWillChange というパブリッシャーを自動的に持つ。ビューがこのオブジェクトを監視していると、objectWillChange が発火したタイミングでビューの再描画が走る。

class CounterModel: ObservableObject {
    @Published var count = 0

    func increment() {
        count += 1
    }
}

上のコードでは CounterModel が ObservableObject に準拠し、count プロパティに @Published を付けている。count の値が変わるたびに objectWillChange が自動発火し、このモデルを監視しているビューが再描画される。

@Published の仕組み

@Published はプロパティラッパーの一種で、値の変更前に objectWillChange.send() を呼び出す。つまり、開発者が手動で通知を送る必要はなく、プロパティに代入するだけで自動的にビューへ伝わる。

@Published あり

プロパティの変更が自動でビューに通知される。宣言するだけで Combine のパブリッシャーとして機能する。

@Published なし

プロパティが変更されてもビューには通知されない。内部的な状態管理にのみ使う場合に適している。

すべてのプロパティに @Published を付ける必要はない。ビューの表示に関係しない内部状態、たとえばキャッシュやフラグなどは @Published なしで宣言すればよい。不要な再描画を避けるために、通知が必要なプロパティだけに絞ることがパフォーマンス上も重要になる。

ビューからの利用

ObservableObject をビューで使うには、@StateObject または @ObservedObject でインスタンスを保持する。ここでは基本的な利用例として @StateObject を使った形を示す。

struct CounterView: View {
    @StateObject private var model = CounterModel()

    var body: some View {
        VStack {
            Text("\(model.count)")
                .font(.largeTitle)
            Button("increment") {
                model.increment()
            }
        }
    }
}

@StateObject はビューのライフサイクルに紐づいてインスタンスを管理する。ビューが再描画されてもインスタンスは保持され、count の値が失われることはない。

objectWillChange の手動発火

通常は @Published に任せておけば十分だが、より細かい制御が必要な場合は objectWillChange を手動で呼べる。たとえば複数のプロパティを一括で変更したあと、まとめて1回だけ通知したいケースだ。

class ProfileModel: ObservableObject {
    var name = ""
    var age = 0

    func update(name: String, age: Int) {
        objectWillChange.send()
        self.name = name
        self.age = age
    }
}

この場合 name と age に @Published は付けず、update メソッド内で明示的に send() を呼んでいる。通知は1回だけ発火するため、2つのプロパティの変更に対してビューの再描画も1回で済む。

クラス限定の理由

ObservableObject はクラス専用のプロトコルであり、構造体には適用できない。これは SwiftUI の監視メカニズムが参照型であることを前提としているためだ。

構造体は値型なので、変更のたびにコピーが生成される。一方クラスは参照型であり、複数のビューが同じインスタンスを共有して参照セマンティクスで監視できる。

同一のオブジェクトを複数箇所から参照し、変更を共有できる仕組み。

この参照型という性質が、親ビューから子ビューへモデルを渡す場面で威力を発揮する。どのビューから変更しても同じインスタンスが更新されるため、アプリ全体でデータの一貫性を保てる。

まとめ

ObservableObject と @Published は SwiftUI のデータフロー設計における基盤だ。@Published を付けたプロパティが変わればビューが自動で再描画されるという単純な仕組みだが、どのプロパティに @Published を付けるか、手動通知をどう使い分けるかで、アプリのパフォーマンスと設計の柔軟性が変わってくる。