UIViewControllerのライフサイクルとRxSwiftのお話

みなさん、こんにちは。[freddi](https://twitter.com/___freddi___)です。

本記事は、iOS Advent Calender 2018の23日目の記事として、**`UIViewController`のライフサイクルとRxSwift**のお話をさせていただきます。短い記事ですが、よろしくおねがいします。

本記事では、都合上RxSwiftに関しての入門の説明はほぼ無いので、ご注意ください。[^1]

# 危険なRxSwiftの使い方

まず先に、`UIViewController`のライフサイクルとRxSwiftが関係するアンチパターンの例を紹介します。

```swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    someThingObservableValue.flatMap { ... } // someThingObservableValueはObservableな変数
        .subscribe( ... )
        .disposed(by: disposeBag)
}
```

一見、ちゃんと動くように見えるコードですね、`Observable`な変数を`subcribe`するだけのコードです。

しかし、ここで気にしてほしいのが、`subcribe`している関数です。**`viewWillAppear`は、何回も呼ばれる可能性があるライフサイクルの関数です。** [^2]

このコードの例だと、

- **viewWillAppearのような複数回呼ばれる可能性のある関数でsubscribeを行おうとしている**[^3]
- **複数回同じようなストリームをsubcribeをしてしまうことになる** 
- **呼ばれた分だけsubscribe処理が働くことになり、subscribeで重い処理などを行っていると大変なことになる**

という重大な問題が出てきます。これは、**`Multiple Subscribing`**のような名前で呼ばれていることがあります。`subscribe`で重い処理をするしない関係なく、**`Multiple Subscribing`は避けるべきです**。

# 対処法
## その１、無難に`viewDidLoad`に入れる
```swift
override func viewDidLoad() {
    super.viewDidLoad()
    someThingObservableValue.flatMap { ... } 
        .subscribe( ... )
        .disposed(by: disposeBag)
}
```

`viewDidLoad`は一回しか呼ばれないので、`subscribe`するものは`viewDidLoad`に入れたほうが良いです。
ただ、**これもこれで、`viewDidLoad`が非常に大きくなる問題もあるのでご注意ください**。[^4]

## その2、Completeを即流す

```swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    Observable.just(())
        .withLatestFrom(someThingObservableValue)
        .flatMap { ... } 
        .subscribe( ... )
        .disposed(by: disposeBag)
}
```

多分ですが、`viewDidLoad`に入れることがそもそも解決策ではないこともあると思います(`viewWillAppear`でどうしてもやりたいこととか)。

そんな時、Observableから使える**`.just(:)`などを使えば、引数で与えられた値をストリームに流したあとに`Completed`を流します。**[^5]
これにより`subscribe`後に即ストリームを開放するという事もでき、`subscribe`する関数を複数回呼び出しても`Multiple Subscribing`が起こる心配はありません。

ただ、目的の値を流すとなると`withLatestFrom`のような関数を利用しなければならず、場合によっては**flatMapのネストが無駄に深くなるなどの厄介な点も出てきます**。

## その3、`BehaviorRelay`がいい説
```swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    if someBehaviorRelayValue.value ... {
       ...
    }
}
```

そもそも`BehaviorRelay`を使えば、`asObservable()`せず（ストリームも作らず）に値を見ることができます。何回も`subscribe`が呼ばれるような場合で、かつ`BehaviorRelay`に置換できそうなら置換したほうがいいです。`BehaviorRelay`も`subscribe`できますし・・・。

## その4、そもそもRxがいらない説[^2]
```swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    ... // Rxを使わない方法でゴニャゴニャ
}
```
これ一番上に持って行きたかったですが、怒られそうなのであえてここに書きました。**Rx使わないのも、それでかなり良い選択です**。[^6]
`RxSwift`がかなり根付いているプロジェクトだと、この検討は多分難しいですが・・・。Rx乱用ダメ絶対。


# 終わりに
私がRx初心者の頃にやらかしたことを記事にしました。ざっくりとした紹介でしたが、みなさんのご参考になりましたでしょうか？
もしTipsや修正すべき点があれば、気軽にリクエストとコメントをよろしくおねがいします。

では今回は失礼します。皆様ありがとうございました。

次の日は[Fumiya Sakai](https://qiita.com/fumiyasac@github)さんの記事です！皆さんもお楽しみに！


[^1]: これは個人的な意見ですが、**RxSwiftを触ろうとする前に、ReactiveXについて調べるほうがいいかもしれません**。理由はいくつかありますが、私はReactiveXというものを知ることで、**他の言語のRxのコードがRxSwiftでも参考になることを知りました**。

[^2]: https://qiita.com/sgr-ksmt/items/e23e684c5e46ea3e8d08

[^3]: 自分で作った関数を呼び出すとき、その中で`subscribe`をすれば、そこで`Multiple Subscribe`の危険性も出てきます。ですが、シングルトンではないかつライフサイクルの短い（一つのスコープのみに存在する）オブジェクトではこの限りでないです。

[^4]: この問題は"Fat viewDidLoad"という名前で呼ばれているのをたまに見ます。

[^5]: 他にも、`from`も値を流したあとに`Completed`を流します。[この記事](https://qiita.com/_ha1f/items/43b28792d27dbee7133d)では`just`や`from`に似ている関数について調べることができます。

[^6]: **RxSwiftは必要に応じて使うようにして、必要無いならばなるべく使わないという方針が後々圧倒的に楽です。**