俺的RxSwiftまとめ⑤(Subjects/Relayとは?)

この記事は、[俺的RxSwiftまとめ①](https://qiita.com/_Masa_asaM_/items/5b15094e047633070b92),[俺的RxSwiftまとめ②](https://qiita.com/_Masa_asaM_/items/4c75b2be7e4074efec45),[俺的RxSwiftまとめ③(Observableとは何か? - その1)](https://qiita.com/_Masa_asaM_/items/17bf07d13aae320b464f),[俺的RxSwiftまとめ④(disposebagとは?)](https://qiita.com/_Masa_asaM_/items/0b1196940cb3f5fcfe0b)の続きです。

#Subjectとは何か?
`Subject`は`observer`としても`observable`としても動作するもの。
イベントの受け取って処理/イベントを発生させることができるものである。。

##PublishSubject
`PublishSubject`は、初期値無しの状態でスタートし、`subscriber`には`subscribe`してから新しく放出された要素のみを伝える`Subject`である。(ただし、`completed`/`error`は放出された後に`subscribe`した`subscriber`にも伝達される。)
マーブルダイアグラムに示すと、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595260/a9343555-5800-653c-169b-56abeabbb7f4.png)
となる
また、マーブルダイアグラムをコードで表現すると、

```swift:PublishSubject.swift
let publishSubject = PublishSubject<String>()
publishSubject.onNext("1")
let subscriber1 = publishSubject
    .subscribe{ event in
        print("subscriber1:" , event.element ?? event)
}
publishSubject.onNext("2")
let subscriber2 = publishSubject
    .subscribe{ event in
        print("subscriber2:" , event.element ?? event)
}
publishSubject.onNext("3")
publishSubject.onCompleted()
let subscriber3 = publishSubject
    .subscribe{ event in
        print("subscriber3:" , event.element ?? event)
}
```

```swift:出力
subscriber1: 2
subscriber1: 3
subscriber2: 3
subscriber1: completed
subscriber2: completed
subscriber3: completed
```
となる。
###どんな時に使われるのか?
`PublishSubject`は新しいイベントを`subscriber`に通知する時に便利なので、ユーザーのクリックに応じた更新などに用いられる。また、リアルタイム性に優れているので、頻繁にやり取りが行われるチャット等に応用が考えられる。

##BehaviorSubject
`BehaviorSubject`は`PublishSubject`に`replay`(＝最新のイベントを`subscriber`に伝達すること)の概念を付け加えたもの。そのため、初期値がないと`replay`できずエラーとなります。初期値を持たせるか、`element`を`optional`として定義することが必須となる。

マーブルダイアグラムに示すと、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595260/681490ba-30d3-4e36-103e-721757028c84.png)
となる。
また、マーブルダイアグラムをコードで表現すると、

```swift:BehaviorSubject.swift
let behaviorSubject = BehaviorSubject<String>(value: "1")
let subscriber1 = behaviorSubject
    .subscribe{ event in
        print("subscriber1:" , event.element ?? event)
}
behaviorSubject.onNext("2")
let subscriber2 = behaviorSubject
    .subscribe{ event in
        print("subscriber2:" , event.element ?? event)
}
behaviorSubject.onNext("3")
behaviorSubject.onCompleted()
let subscriber3 = behaviorSubject
    .subscribe{ event in
        print("subscriber3:" , event.element ?? event)
}
```

```swift:出力
subscriber1: 1
subscriber1: 2
subscriber2: 2
subscriber1: 3
subscriber2: 3
subscriber1: completed
subscriber2: completed
subscriber3: completed
```
###どんな時に使われるのか?
`BehaviorSubject`はビューに`Subject`の最新の状態を表せるので、`placeholder`に最新の値をあらかじめ入れておく、ロード中のインジケーターを示す等の場面で用いられる。

##ReplaySubject
`BehaviorSubject`では、最新のイベントのみを`subscriber`に伝達できたが、`ReplaySubject`はバッファサイズに指定した数だけイベントを伝達することができる。ただし、バッファサイズを大きくしたり、画像のようなデータサイズが大きいものを使いすぎると、メモリ逼迫の原因となるので注意が必要である。
マーブルダイアグラムに示すと、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595260/60cbf7c3-f864-5c60-4bcd-7735c73fa4bd.png)
となる
また、マーブルダイアグラムをコードで表現すると、

```swift:ReplaySubject.swift
let replaySubject = ReplaySubject<String>.create(bufferSize: 2)
replaySubject.onNext("1")
replaySubject.onNext("2")
let subscriber1 = replaySubject
    .subscribe { event in
        print("subscriber1:", event.element ?? event)
}
replaySubject.onNext("3")
```

```swift:出力
subscriber1: 1
subscriber1: 2
subscriber1: 3
```
となる。
###どんな時に使われるのか?
以前との差分を取りたい時などに使われる。

##AsyncSubject
`AsyncSubject`は、`completed`が流れた直後に、最後の値を`subscriber`に渡すもの。
マーブルダイアグラムに示すと、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595260/ae3bfa90-71c5-b3ff-44af-be1bbb991af7.png)
となる。

これをコードで表すと、

```swift:AsyncSubject.swift
let asyncSubject = AsyncSubject<String>()
asyncSubject.onNext("1")
let subscriber1 = asyncSubject
    .subscribe { event in
        print("subscriber1:", event.element ?? event)
}
asyncSubject.onNext("2")
let subscriber2 = asyncSubject
    .subscribe { event in
        print("subscriber2:", event.element ?? event)
}
asyncSubject.onNext("3")
asyncSubject.onCompleted()
```

```swift:出力
subscriber1: 3
subscriber2: 3
subscriber1: completed
subscriber2: completed
```
となる。
###どんな時に使われるのか？
APIのコールバックを受ける時に便利

#Relayとは何か?
`Relay`は、`subjects`の[ラッパー](https://qiita.com/dsudo/items/51ca822169ef9f8b9bc3)のことである。ラッパーとして果たす役割は、`error`や`completed`を受け付けず、`accept(_:)`で`next`で流れるelementのみを通すことである。

`PublishRelay`は`PublishSubject`,`BehaviorRelay`は`BehaviorSubject`に対応している。
コードに示すとそれぞれ、以下のようになる。

```swift:PublishRelay.swift
let publishRelay = PublishRelay<String>()

let disposeBag = DisposeBag()
publishRelay.accept("1")
publishRelay
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: disposeBag)

publishRelay.accept("2")
```

```swift:出力
2
```

```swift:BehaviorRelay.swift
let behaviorRelay = BehaviorRelay(value: "Initial value")
let disposeBag = DisposeBag()

behaviorRelay.accept("1")

behaviorRelay
    .subscribe {
        print("subscriber1:" , $0.element ?? $0)
}
.disposed(by: disposeBag)

behaviorRelay.accept("2")

behaviorRelay
    .subscribe {
        print("subscriber2:", $0.element ?? $0)
}
.disposed(by: disposeBag)

behaviorRelay.accept("3")
```

```swift:出力
subscriber1: 1
subscriber1: 2
subscriber2: 2
subscriber1: 3
subscriber2: 3
```
となる。