├── LICENSE ├── README.md ├── combine_overview_en.svg └── combine_overview.svg /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Yoshinori Imajo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 図解で整理するCombineフレームワークの全体像 2 | 3 | 4 | 5 | ## はじめに 6 | 7 | AppleはWWDC19にてCombineフレームワークを発表しました。このフレームワークによりリアクティブプログラミングおよび宣言的なコーディングが可能になります。本記事ではそのかんたんな概要とCombineの全体像を示します。 8 | 9 | ## リアクティブプログラミングって? 10 | 11 | まずはリアクティブプログラミングの説明として、コード1に手続き型のコードがリアルタイムに変更される値に対応していない例を示します。 12 | 13 | ```swift 14 | var a = 5 15 | let b = 10 16 | let flag = a > b 17 | print(flag) // false が出力される 18 | a = 20 19 | print(flag) // flagは当然変化せず false のまま出力 20 | ``` 21 | 22 | 次のコード2ではリアクティブプログラミングによりリアルタイムに変更される値に対応している例です。 23 | 24 | ```swift 25 | let a = CurrentValueSubject(5) 26 | let b = Just(10) // 10を要素として流すのみ 27 | let publisher = b.combineLatest(a) 28 | .map { $0 < $1 } // b < a の場合に true とする 29 | _ = publisher 30 | .sink { print($0) } // false と true が次々出力される 31 | a.send(20) // aの要素として20を流すため b を上回り↑ 32 | ``` 33 | 34 | iOSアプリ開発ではそもそもリアルタイムに値の変化を検知する仕組みとして、従来から様々なイベント通知手段が用意されてはいます。しかしこれを他プラットフォームで利用されるような一般化されたリアクティブプログラミング仕様であるReactive Streamsをベースとした公式フレームワークで行えることがCombineのメリットなわけです。 35 | 36 | ## Publisher 37 | 38 | 要素をイベントとして通知する概念を示したものがPublisherです。このPublisherという名前はReactiveStreams仕様そのままであり、その仕様に沿っていないRxSwiftではObservableに相当します。 39 | 40 | ## Subscriber 41 | 42 | Publisherのイベントを処理する役割としてプロトコルとして用意されています。直接Subscriberを利用するよりも、Publisherのプロトコルエクステンションとしてイベントを処理するsinkメソッドなどが用意されており、Subscriberを間接的に利用することが多いはずです。このSubscriberという名前についてもReactiveStreams仕様をベースとしています。RxSwiftには同様の概念としてObserverが存在します。 43 | 44 | ## Subscription 45 | 46 | SubscriberからPublisherへ通知を制御するために存在しており、ReactiveStreams仕様にあるバックプレッシャーを実現することができます。RxSwiftには存在しません。 47 | 48 | ## Publishers 49 | 50 | Mapなどのオペレーターが利用する様々な型がPublishersとして列挙されています。Publisherに準拠しており、オペレーターはストリームを処理し列挙されているそれぞれの型へ変換します。 51 | 52 | ## Subject 53 | 54 | 外部からイベントを発生させられる自由度が高いPublisherです。2つのSubjectが用意されておりCurrentValueSubjectは初期値を持ちます。 55 | 56 | ## Subscribers 57 | 58 | Subscriberとして機能する型の名前空間として用意されています。 59 | 60 | ## おわりに 61 | 62 | SwiftUIが開発の全盛となっても、@PublishedなどのプロパティラッパーによりCombineを意識しない方法が用意されているため、Combineをなるべく使わないという手もあります。しかし、アプリの仕様は日々複雑になるものであり、そういった際にCombineをより深く知りたくなった際、本記事が役に立つ日があるかもしれません。 63 | 64 | ## 参考 65 | 66 | - tobi462 67 | - https://twitter.com/tobi462/status/1272755992447442945 68 | - Getting Started with Combine - Shai Mishali - App Builders 2020 69 | - https://youtu.be/R7KgBgvQJ0c 70 | -------------------------------------------------------------------------------- /combine_overview_en.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Publishers
Publishers
Subscribers
Subscribers

<<Protocol>>
Subscription


+ request(

    _ demand:

      Subscribers.Demand

)

<<Protocol>>...

<<Protocol>>
Publisher


associated Output
associated Failure: Error


+ receive<S>(subscriber: S)


<<Extension>> 
 + subscribe<S>(_ subscriber: S)
<<Protocol>>...

AnySubscriber

AnySubscriber

Map<Upstream, Output>

Map<Upstream, Output>

Filter<Upstream, Output>

Filter<Upstream, Output>
Just<Output>

Just<Output>

<<Protocol>>
Subject


+ send(_ value: Self.Output)

+ send(subscription: Subscription) 

send(

    completion: Subscribers.Completion<Self.Failure>

)

<<Protocol>>...

PassthoughSubject<Output, Failure>

PassthoughSubject<Output, Failure>

Assign<Root, Input>


+ init(

    object: Root, 

    keyPath: ReferenceWritableKeyPath<Root, Input>

)

Assign<Root, Input>...

Sink<Input, Failure>


init(

    receiveCompletion: @escaping (

        (Subscribers.Completion<Failure>) -> Void

    ), 

    receiveValue: @escaping ((Input) -> Void))

)

Sink<Input, Failure>...

<<Enum>>
Completion<Failure>


finished

failure(Failure)

<<Enum>>...

Demand


+ unlimited: Subscribers.Demand

+ none: Subscribers.Demand


+ max(_ value: Int) 

    -> Subscribers.Demand

Demand...
etc ...
etc ...

Flow
Flow

<<Protocol>>
Cancellable


+ cancel()

<<Protocol>>...

CurrentValueSubject<Output, Failure>


+ init(_ value: Output)

CurrentValueSubject<Output, Failure>...
receive(subscription:)
receive(subscription:)
request(_: Demand)
request(_: Demand)
receive(_ :Input)
receive(_ :Input)
subscribe(Subscriber)
subscribe(Subscriber)
Inherit or Conforms
Inherit or Conforms

<<Protocol>>
Subscriber


associated Input
associated Failure: Error


receive(subscription: Subscription)
+ receive(_ input: Self.Input)

     -> Subscribers.Demand

+ receive(
     completion: Subscribers.Completion<Self.Failure>
 )

<<Protocol>>...
NOTE:  The Just Publisher is a type that converts a single element into a Publisher and uses it.
NOTE:  The Just Publisher is a type that...
NOTE:  Unlike the Reactive Streams specification, the cancellation is separated from the Reactive Streams specification so that Subscriptions are compliant with Cancelable.
NOTE:  Unlike the Reactive Stream...
NOTE:  The Sink and Assign types can be substituted by the Publisher methods sink and assign, so you may not need to know about them directly. You can see an example of using the Assign type directly at around 07:34 in WWDC19: Introducing Combine.
NOTE:  The Sink and Assign types can be substituted by the Publishe...
NOTE:  For the end events handled in the stream, enum Completion enumerates the successful completion of the event and the error.
NOTE:  For the end events handled in the stream, enum Completion en...
NOTE: The operator methods are provided as Publisher instance methods and return the types that conform to the Publisher enumerated in enum Publishers.
For example, the map method returns a Publisher-compliant type called Map.
NOTE: The operator methods are provided as...
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /combine_overview.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Publishers
Publishers
Subscribers
Subscribers

<<Protocol>>
Subscription


+ request(

    _ demand: Subscribers.Demand

)

<<Protocol>>...

<<Protocol>>
Publisher


associated Output
associated Failure: Error


+ receive<S>(subscriber: S)


<<Extension>> 
 + subscribe<S>(_ subscriber: S)
<<Protocol>>...

AnySubscriber

AnySubscriber

Map<Upstream, Output>

Map<Upstream, Output>

Filter<Upstream, Output>

Filter<Upstream, Output>
Just<Output>

Just<Output>

<<Protocol>>
Subject


+ send(_ value: Self.Output)

+ send(subscription: Subscription) 

send(

    completion: Subscribers.Completion<Self.Failure>

)

<<Protocol>>...

PassthoughSubject<Output, Failure>

PassthoughSubject<Output, Failure>

Assign<Root, Input>


+ init(

    object: Root, 

    keyPath: ReferenceWritableKeyPath<Root, Input>

)

Assign<Root, Input>...

Sink<Input, Failure>


init(

    receiveCompletion: @escaping (

        (Subscribers.Completion<Failure>) -> Void

    ), 

    receiveValue: @escaping ((Input) -> Void))

)

Sink<Input, Failure>...

<<Enum>>
Completion<Failure>


finished

failure(Failure)

<<Enum>>...

Demand


+ unlimited: Subscribers.Demand

+ none: Subscribers.Demand


+ max(_ value: Int) 

    -> Subscribers.Demand

Demand...
その他多数
その他多数

アクションに対する処理の流れ
アクションに対する処理の流れ

<<Protocol>>
Cancellable


+ cancel()

<<Protocol>>...

CurrentValueSubject<Output, Failure>


+ init(_ value: Output)

CurrentValueSubject<Output, Failure>...
生成
生成
要求
要求
通知
通知
購読
購読
送信
送信
継承または準拠による実装
継承または準拠による実装

<<Protocol>>
Subscriber


associated Input
associated Failure: Error


receive(subscription: Subscription)
+ receive(_ input: Self.Input)

     -> Subscribers.Demand

+ receive(
     completion: Subscribers.Completion<Self.Failure>
 )

<<Protocol>>...
NOTE:  Justは1つの要素をPublisherとして変換して利用する型です。
JustはRxではオペレーターに分類されるがCombineは分類されていないようです。
NOTE:  Justは1つの要素をPublisherとして変換して利用する型です。...
NOTE:  Reactive Streams仕様とは違い、SubscriptionがCancellableに準拠する形でキャンセル機能を分離しています。
NOTE:  Reactive Streams仕様とは違い、Subscripti...
NOTE:  Sink型やAssign型はPublisherのメソッドsinkやassignによって代用できるため直接知らなくてもいいかもしれません。WWDC19: Introducing Combineの07:34頃には直接Assign型を利用している例が見られます。
NOTE:  Sink型やAssign型はPublisherのメソッドsinkやassignによって代用できるため直接知らなくてもいいかもしれません。WWDC19: Introducing Combineの07:3...
NOTE:  ストリームで取り扱うイベントのために、enum Completionではイベントの正常終了とエラーを列挙しています。ちなみにRxSwiftではenum Event によりイベントとして流れる要素、正常終了、エラーの3つを列挙する設計でした。
NOTE:  ストリームで取り扱うイベントのために、enum Completionではイベントの正常終了とエラーを列挙しています。ちなみにRxSwiftではenum Event によりイベントとして流れる要素、正常終了、エラーの3つを列挙する設計でした。
NOTE: CurrentValueSubject は最新の値を保持できる Subject です。そのため初期値が必要になります。
NOTE: CurrentValueSubject は最新の値を保持できる Subject です。そのため初期値が必要になります。
NOTE: オペレーター用のメソッドはPublisher のインスタンスメソッドとして用意されており、enum Publishers に列挙される Publisher に準拠する型を返します。
例えばmapメソッドはMap型というPublisher準拠の型を返すことになります。
NOTE: オペレーター用のメソッドはPublisher のインスタンスメソッドとして用意されており、enum Publishers に列挙される Publisher...
Viewer does not support full SVG 1.1
--------------------------------------------------------------------------------