[Combine] Combine の Publisher を改めて理解する

Combine の基本要素の1つである Publisher を改めて説明します。

Publisher

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

「時間とともに、値の列を送信するタイプ」として説明されています。実際には、protocol です。

Publisher 例(TimerPublisher)

Timer クラスは、以下のように、Timer.TimerPublisher を提供するようになりました。

Timer.TimerPublisher

Timer.TimerPublisher(interval: TimeInterval, tolerance: TimeInterval? = nil, runLoop: RunLoop, mode: RunLoop.Mode, options: RunLoop.SchedulerOptions? = nil)

Timer クラスから直接提供することもできます。

example

Timer.publish(every interval: TimeInterval, tolerance: TimeInterval? = nil, on runLoop: RunLoop, in mode: RunLoop.Mode, options: RunLoop.SchedulerOptions? = nil) -> Timer.TimerPublisher

どちらも 返り値として TimerPublisher を返します。

引数を見て想像できるように、この Publisher は、指定した時間経過毎に、値を送信してきます。

TimerPublisher の詳細を確認すると、以下のようになっています。

  • 送信してくる値のタイプとして、Timer.TimerPublisher.Output に Date が typealias されています。
  • Timer.TimerPublisher.Failure に Never と設定されていることから この Publisher は、エラーを送ることはないとわかります。

Publisher 例(PassthroughSubject)

エラーを送ってくるかもしれない Publisher の1つとして、PassthroughSubject という Publisher があります。

PassthroughSubject は、外部から、値を送ることをリクエストすることができる Publisher (の1つ)です。

Send メソッドを使うことで、PassthroughSubject に値を送るようにリクエストできます。

PassthroughSubject を定義する時には、Output のタイプを含めて定義する必要があります。

PassthroughSubject 定義例

let mySubject = PassthroughSubject()
mySubject.send("TestValue")
mySubject.send(completion: .failure(....))
上記では、送られてくるデータは、String 型で、エラー時には、Error が送信されてくると定義しています。

定義した後に、PassthroughSubject を経由して、"TestValue" を送っています。
その後、エラーを送っています。

# エラーは、別に定義が必要ですが、省略しています。

注意
send(completion:) は、エラーだけでなく、非エラーケースでのデータ終了(今後データを送ることはない)を通知するためにも使われます。

Publisher の使い方

Publisher に対して、sink することで、Publisher から値が送信された時に 受け取ることができます。

# assign を使うと、受け取った値をプロパティに設定することができますが、sink を使うことができれば同様のことができますので、急いで理解しなくとも良いと思います。

TimerPublisher (エラーを送ってこない Publisher) の使い方

先ほど見たように、TimerPublisher は、エラーを送ってこないので、シンプルに記述することができます。

example

let cancellable = Timer.publish(every: 3, on: .main, in: .default)
    .autoconnect()
    .sink { (value) in
        print("receive: \(value)")
    }

例えば、上記のコードでは、3秒ごとに、受け取った value (Date 型) を print します。

注意
sink したときの返り値 cancellable は、自分で保持しておかないといけません。スコープ外に外れてしまうと 消去されてしまい、Publisher がデータを送付しても受け取れなくなってしまいます。

PassthroughSubject (エラーを送ってくるかもしれない Publisher) の使い方

PassthroughSubject は、エラーを送ってくるかもしれませんので、そこも含めて sink する必要があります。

PassthroughSubject

enum MyError: String, Error {
    case IntentionalError = "IntentionalError"
}


let myPS = PassthroughSubject()
let cancellable = myPS.sink(receiveCompletion: { (completion) in
                            switch completion {
                                case .finished:
                                    print("finished successfully")
                                case .failure(let error):
                                    print("error occured: \(error)")
                            }}, receiveValue: { (value) in
                                print("value received: \(value)")
                            })
myPS.send("test")
--> value received: test
//myPS.send(completion: .finished)
// --> finished successfully
myPS.send(completion: .failure(.IntentionalError))
--> error occured: IntentionalError

まとめ:Publisher を理解するポイント

Publisher を理解するポイント
  • Publisher は、時間と共にデータを送ってくる
  • .sink することで、データが送られてきた時に処理することができる
  • Publisher には、データを確実に送ってくる Publisher と Failure を送ってくるかもしれない Publisher がある
  • .sink したときの返り値は、保存しておかないと、呼ばれなくなってしまう

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

コメントを残す

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