[SwiftUI][Combine] @Published は、Publisher を提供する Property Wrapper

     
⌛️ 2 min.

@Published を理解するために、あまり着目したことの無い方向から @Published を考察してみます。

環境&対象

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

  • macOS Big Sur 11.2.1
  • Xcode 12.4

@Published

@Published が付与されたプロパティは、更新されるとビューが更新されます。

この説明がよくみる @Published の説明だと思います。

@Published は、Combine フレームワークの一部

Apple のドキュメントを読んで最初に気づくのは、@Published は、SwiftUI ではなく Combine の一部として提供されていることです。

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

@Published は、Publisher を作る

@Published が役に立つのはビューを更新させたい時だけではありません。

@Published を指定すると、$ (つまり、projectedValue) として、そのプロパティの Publisher にアクセスすることができます。
(property wrapper として、Publisher を作成してくれるということです)

ドキュメントのサンプルにもありますが、以下のように、使うことができます。

Apple のサンプル


class Weather {
    @Published var temperature: Double
    init(temperature: Double) {
        self.temperature = temperature
    }
}

let weather = Weather(temperature: 20)
cancellable = weather.$temperature
    .sink() {
        print ("Temperature now: \($0)")
}
weather.temperature = 25

// Prints:
// Temperature now: 20.0
// Temperature now: 25.0

変数監視

従来の変数監視

Swiftが当初から持っている変数の監視は、以下のようなものです。

Playground でのチェック


import Foundation

class Test {
    var test2:Int = 10 {
        willSet{
            print("willSet (\(test2) -> \(newValue))")
        }
        didSet{
            print("didSet (\(oldValue) -> \(test2))")
        }
    }
}

func update(test:Test) {
    test.test2 += 1
}

let test = Test()

test.test2 = 11   // 10 -> 11

update(test:test)  // 11 -> 12

// Prints:
//willSet (10 -> 11)
//didSet (10 -> 11)
//willSet (11 -> 12)
//didSet (11 -> 12)

Combine 的変数監視 が @Published

変数監視を Combine的に実装すると この @Published になるということだと思います。

@Published というプロパティラッパーを設定しておくことで、$ 経由でその Publisher に簡単にアクセスでき、sink や assign をすることで、変数監視の実装が可能となります。

複数から監視することも可能なので、さらに便利です。

SwiftUI でのビュー更新

SwiftUI でのビューの更新は、@Published と ObservableObject/@ObservedObject の組み合わせにより実現されています。
ビューの更新は、この Publisher を subscribe することによって実装されています(ハズ)。

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

ObservableObject に準拠させたクラスは objectWillChange というプロパティ(Publisher)を持ちます。
@Published 指定されたプロパティに変化が起こる直前に objectWillChange に通知され、objectWillChange が Publisher として変化を通知して・・・・

これが、SwiftUI でのビュー更新の背景で起こっていること(のハズ)です。

ちなみに、@Published, ObservableObject, @ObservedObject のうち、@ObservedObject のみが SwiftUI 提供の struct です。

@Published だけでなく、ObservableObject も Combine の提供するものでした。このあたりも興味深いです。

なんとなく、ObservableObject と @ObservedObject がペアな気がしていたのですが、一方は Combine から 他方は SwiftUI から提供されていました。

まとめ:@Published は、変数監視の Combine 実装

@Published は、変数監視の Combine 実装
  • willSet/didSet の Combine 版が @Published
  • SwiftUI でのビューの更新は、@Published/ObservableObject/@ObservedObject の組み合わせで実現されている
MEMO

上で、willSet/didSet の Combine 版と書いていますが、厳密には、@Published は willSet の タイミングで Publish されることに注意しないといけません。

“更新される/た” ことにのみ着目しているのであれば問題ないかもしれませんが、”更新された値” に着目しているのであれば、@Published は、willSet のタイミングで Publish されていることに気をつけないといけません。

@Published が willSet で Publish されるのは、SwiftUI が View の更新するときに、前後の値が 必要になるためだと思います。(SwiftUI は、指定されればアニメーションを行うことが要求されます)

“更新された値”に着目しているのであれば、@Published ではなく自前の objectDidChange 的な通知を使用する方が自然だと思います。
例えば、CurrentValueSubject は、didSet 相当で Publish されます。

すでにそのように理解している人は多いかもしれませんが、改めて考察してみました。

Swift 学習におすすめの本

詳解Swift

Swift の学習には、詳解 Swift という書籍が、おすすめです。

著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。

最新版を購入するのがおすすめです。

現時点では、上記の Swift 5 に対応した第5版が最新版です。

Swift ポケットリファレンス

Swift を学んでも、プログラミング言語の文法を全て記憶しておくことは無理なので、ちょっとした文法の確認をするために、リファレンス本を手元に置いておくと便利です。

注意

Swift4 までしか対応していないので、相違点を理解して参照する必要があります。

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

コメントを残す

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