RxSwiftのCold・Hotについて理解を深める

#はじめに
備忘録としてQiitaに残します。

#Cold・Hotを理解する前に
ColdもHotも`Observableの性質`のことを指します。

###・SubjectはHotである
Observableの派生であるSubjectは全て`HotなObservable`に該当します。

###・OperatorはColdである
各種Operatorは内部でObservableを返しますが`ColdなObservable`を返します。

※もちろん、例外もあります。

#ColdなObservableの性質

###①subscribeされるまで動作しない
ColdなObservableはsubscribeしないと`動作しない`性質を持っています。

つまり、値を流しません(ストリームが稼働していない状態)。

```swift
// sequence(Cold) → map(Cold)
Observable.of("event")
  .map { text -> String in
      print(text)
      return text
  }

// 何も出力されない
```
`subscribe`することによって値が流れます。

```swift
Observable.of("event")
  .map { text -> String in
      print(text)
      return text
  }
  .subscribe()
  .disposed(by: disposeBag)

// event
```

###②分岐ができない
これは説明するより、以下のコードで確認した方が分かりやすいです。

```swift
// textField(Hot) -> map(Cold)
let observable = textField.rx.text.filterNil().map { text -> String in
     print("\(text.count)")
     return String(describing: text.count)
}

observable.bind(to: label1.rx.text).disposed(by: disposeBag)
observable.bind(to: label2.rx.text).disposed(by: disposeBag)
```
上記のコードではfilterNil( )でnilを除外し、TextFieldに入力された値をmapで加工して2つのLabelに反映しています。

出力結果はどうなるでしょうか？

```
0
0
1
1
2
2
```
文字を入力する度に、map内の処理が`2回`呼ばれました。

ColdなObservableでは、2つのオブジェクトに対してsubscribeすると
`別々の2本のストリーム`が流れます。

ColdなObservableで複数subscribeした場合、`それぞれに対してObservableが複製されてしまう`んですね。

```
1.Hot -> Cold -> Observer

// 複製されてしまう！
2.Hot -> Cold -> Observer
```

#HotなObservableの性質

###①subscribeされなくても動作する
HotなObservableではsubscribeしなくても値が流れます。

例えば以下を実行したとします。

```swift
var behaviorSubject = BehaviorSubject<String>(value: "value")

// subject(Hot) -> map(Cold)
behaviorSubject.map { text -> String in
    print(text)
    return text
}

// 何も出力されない
```
ColdなObservableの性質で述べたように何も起きません。
`subscribeされるまで動作しないから`ですね。

ではHotなObservableに変換してみましょう。

冒頭でも述べたように各種Operatorは`ColdなObservable`を返します。

ですが`Hot変換オペレータ`と呼ばれるOperatorを使用すれば`HotなObservableに変換することが出来ます`。

今回は、その中の1つである`Publish()`を使ってみたいと思います。

```swift
var behaviorSubject = BehaviorSubject<String>(value: "value")

// subject(Hot) -> map(Cold) -> publish(Hot)
behaviorSubject.map { text -> String in
    print(text)
    return text
}
.publish() // Hot変換する
.connect() // 値が流れる
.disposed(by: disposeBag)

// value
```
`subscribeしていない`のに出力されましたね。

これはPublish( )内部でObservableを`ConnectableObservable`に変換して返しているからなんですね(違う記事で詳しくまとめる予定)。

通常のObservableではsubscribeして値を流しますが、ConnectableObservableでは`connect()が呼ばれて値を流し始めます`。

つまり、connect( )を呼びだせば`subscribeしなくても値を流すことができる`という訳です。

###②分岐できる
では、先ほどのコードをHot変換してみましょう。

```swift
// textField(Hot) -> map(Cold) -> publish(Hot)
let observable = textField.rx.text.filterNil().map { text -> String in
     print("\(text.count)")
     return String(describing: text.count)
}
.publish()

observable.bind(to: label1.rx.text).disposed(by: disposeBag)
observable.bind(to: label2.rx.text).disposed(by: disposeBag)

observable.connect().disposed(by: disposeBag)
```
結果として以下のようになりました。

```
0
1
2
```
文字を入力する度に、map内の処理が`1回`しか呼ばれていない事がわかりますね。

複数subscribeしているのにも関わらず、`同じ値がそれぞれに流れている`からこそ、このような挙動になります。

```
1. Hot -> Cold -> Hot  -> Observer
                     | // 分岐するから複製されない！
                       -> Observer
```
因みに分岐後の値は`別の値として扱われる`ので下記のように個別に処理を加えることが出来ます。

```swift
observable.bind(to: label1.rx.text).disposed(by: disposeBag)
observable.map { text -> String in
     "*\(text)*"
}
.bind(to: label2.rx.text)
.disposed(by: disposeBag)
```

#おわりに
RxSwiftは難しいということが理解できました。

正直合っているか分からないので間違っている部分や疑問に思うところがあれば
コメントして下さると有り難いです。

次回はもっとHotなObservableについて理解を深めたいと思います。

#参考資料
[今日こそ理解するHot / Cold](https://www.slideshare.net/yukitakahashi3139241/hot-cold)
[RxSwift + RxCocoaに関する基礎知識](https://github.com/tomoyasuzuki/TaskShare/issues/2)
[RxSwiftについてようやく理解できてきたのでまとめることにした(4)](https://qiita.com/_ha1f/items/2d0fc50505ce3a1fcdab)
[RxJS を学ぼう #4 - COLD と HOT について学ぶ / ConnectableObservable](https://tech.recruit-mp.co.jp/front-end/post-11558/)