【iOS】Combine チュートリアル
本記事ではCombineを学ぶ上で重要な「Publisher」、「Subscriber」、「Operator」の概略について書きます。「Publisher」、「Subscriber」、「Operator」は種類が多く、一つ一つコードを書いて学習を進めていくのは少し大変です。そこで、Combineでよく使われる型やメソッドの動作をすぐに確認することができ、効率よく学習を進めることができるようなPlaygroundファイルをまとめたプロジェクトを作成しました。
本記事を一通りお読みいただいた後に、上記Playgroundファイルに記載しているコメントを読みながら動作をご確認いただくことで、Combineの基礎的な内容を一通り学ぶことができるようになっています。
本記事に掲載しているソースコードは、上記Playgroundファイル内に記載しているコードを引用しています。是非、プロジェクトファイルをダウンロードして、ソースを動かしながら読み進めてもらえると幸いです!
Combineとは
CombineはSwiftでリアクティブプログラミングを行うための、Apple純正のフレームワークです。Combineを使うと、あるオブジェクトで発生したイベントを別のオブジェクトに伝達することができます。イベントは以下の3種類のタイプに分類することができます。
- 値
- イベントの完了
- イベントの失敗
「値」は「1」や「"Swift"」といった「値」そのものです。単なる「値」をイベントと呼ぶのは少々違和感を感じますが、Combineでは「値」そのものもイベントとして扱います。「イベントの完了」はイベントの伝達が全て完了したことを、伝達先のオブジェクトに伝えるためのイベントです。「イベントの失敗」は何かしらの理由でイベントの伝達が失敗したことを、伝達先のオブジェクトに伝えるためのイベントです。
Combineを学ぶ上で重要な項目は以下の3つです。
- Publisher (パブリッシャ)
- Subscriber (サブスクライバ)
- Operator (オペレータ)
「Publisher」はイベントをPublish(発行)することができます。「Subscriber」は「Publisher」がPublishしたイベントを、Subscribe(購読)することができます。「Publisher」を「Subscriber」でSubscribeすると、「Publisher」からPublishされたイベントを、「Subscriber」内で定義したクロージャ内で受信することができます。「Operator」は「Publisher」がPublishしたイベントに処理を加えることができ、処理を加えたイベントをSubscribeすることができます。
では、ここまで説明してきた内容をコードで確認します。
PassthroughSubject<String, Never>
型は「Publisher」の役割を果たす型です。send(_:)
メソッドで、イベントをPublishすることができます。sink(receiveValue:)
は「Subscriber」の役割を果たすメソッドです。引数に指定したクロージャで、Publishされたイベントを受信することができます。map(_:)
は「Operator」の役割を果たすメソッドです。サンプルコードでは、Publisherが整数値「0, 1, 2」をPublishし、Operatorが整数値「0, 1, 2」を文字列「"0", "1", "2"」に変換、そして、sink(receiveValue:)
メソッドの引数に指定したクロージャでPublishされた文字列「"0", "1", "2"」をコンソールに出力しています。
Publisher
イベントを発行するオブジェクトをPublisherといいます。PublisherをSubscribeすることで、PublisherからPublishされたイベントを受信することができます。
以下のサンプルコードでは、配列からPublisherを生成しています。Publisherをsink(receiveCompletion:receiveValue:)
メソッドでSubscribeして、Publishされた「値」をクロージャ内で受信しています。
Subscriber&Subscription
PublisherからPublishされたイベントを受信することができるように設定するメソッドを「Subscriber」といいます。例えば、sink(receiveValue:)
メソッドは「Subscriber」の一種です。sink(receiveValue:)
メソッドはAnyCancellable
型のオブジェクトを返します。これを「Subscription」といいます。「Subscription」を保持している間は、「Subscribe」している状態を維持することができます。
以下のサンプルコードでは、Reciver
クラスのinit()
メソッド内でPublisherをsink(receiveValue:)
メソッドでSubscribeしています。しかし、sink(receiveValue:)
メソッドから返される「Subscription」を保持していないため、イベントをPublishしてもsink(receiveValue:)
メソッドに指定したクロージャは実行されません。
以下のサンプルコードでは、Reciver
クラスのinit()
メソッド内でPublisherをsink(receiveValue:)
メソッドでSubscribeしています。そして、sink(receiveValue:)
メソッドから返されるSubscriptionを、subscription
プロパティを保持しています。その結果、Subscribeしている状態は維持され、イベントがPublishされる度にsink(receiveValue:)
メソッドで定義したクロージャが実行されます。また、AnyCancellable
型にはcancel()
メソッドが用意されています。任意のタイミングで保持しているSubscriptionを破棄することができます。cancel()
メソッドを実行すると、保持していたSubscriptionが破棄され、以降はsend(_:)
メソッドでイベントをPublishしても、Subscriberで定義したクロージャは実行されなくなります。
Operator
Operatorは元のPublisherが持つ値を加工して、新たなPublisherを生成することができます。
以下のサンプルコードでは、元のPublisherが持つ値を2倍した、新たなPublisherを生成しています。
おわりに
「Publisher」、「Subscriber」、「Operator」それぞれの概略について書きました。現時点では、なんとなくそれぞれの役割は理解できたといった感覚ではないでしょうか。もし、もっと理解を深めたいという方は、最初にご紹介したPlaygroundファイルをご活用ください。
参考資料
・Framework Combine
・Introducing Combine ・Combine in Practice
Discussion