[Swift][Combine] ObservableObject の変更を検知する

SwiftUI で ビューの更新によく使われる ObservableObject の変更を検知する方法を説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Monterery beta 5
  • Xcode 13 beta5
  • iOS 15

ObservableObject

Combine で定義されている Protocol です。

Apple のドキュメントは、こちら

クラス内の @Published を付与された プロパティの変更を外部に伝達するような設定になっている Protocol です。

SwiftUI での使われ方


class ViewModel: ObservableObject {
  @Published var dataValue: Int = 1
  func increment() {
    dataValue += 1
  }
}

class MyView: View {
  @StateObject var viewModel = ViewModel()
  var body: some View {
    Text("DataValue: \(viewModel.dataValue)")
    Button(action: { viewModel.increment() },
           label: { Text("Increment") } )
  }
}

上記のように、ObservableObject を View 内で、@StateObject で保持していると、@Published で宣言されている dataValue の値が変更された時に、ビューも自動で更新されます。

従来は、データ更新に合わせてビューも更新されるようにコードを記述しなければいけなかったのですが、その判断を SwiftUI 側がしてくれています。

# 内部的には、どのビューの表示に どの変数が使用されているかを分析して、表示の更新を行うようです。

アプリの別モジュールでも変更を検知したい

SwiftUI のビューであれば、@StateObject や @ObservedObject を使用することで、自動で更新が行われますが、例えば、AppKit/UIKit で作成したビューなどは、(当たり前ですが)変更に応じた自動更新は行われません。

このような時には、ObservableObject が変更されたことを検知することが必要となります。

Apple のドキュメントを確認すると ObservableObject からは、objectWillChange という publisher が提供されるハズであることがわかります。

つまり、以下のようなコードで、変更を検知することができます。



class ViewModel: ObservableObject {
  @Published var dataValue: Int = 1

  func increment() {
    dataValue += 1
  }
}

class MyNSView: NSView {
  var viewModel = ViewModel()
  // (1)
  var viewModelSubsc: AnyCancellable? = nil

  init() {
    .....
    // (2)
    viewModelSubsc = viewModel.objectWillChange
      .sink { _ in 
         // (3)
         // update !
      }
  }
}
コード解説
  1. Publisher を sink した時に保存しておくための変数です
  2. viewModel は、ObservableObject に準拠しているので、objectWillChange が使えます
  3. .sink で、通知を受け取り、必要に応じた処理を行います。

objectWillChange.send() の裏返し

うまくデータの変更を検知されずに、自分で objectWillChange.send() をコードに記述することが必要となるケースはあります。

上記は、objectWillChange.send() された通知を受け取るコードを書いているということです。

まとめ:ObservableObject の変更を検知する方法

ObservableObject の変更を検知する方法
  • objectWillChange を subscribe することで変更時に通知される
  • SwiftUI の View であれば、内部的に行われる

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です