RxCocoaが提供するDriverって何？


# はじめに

今まで以下の RxSwift に関する記事を書いてきました。

 1. [オブザーバーパターンから始めるRxSwift入門](http://qiita.com/k5n/items/17f845a75cce6b737d1e)
 1. [RxSwift入門(2) 非同期処理してみる](http://qiita.com/k5n/items/98aaf84fc164f7a5502c)
 1. [RxSwiftを深く理解する](http://qiita.com/k5n/items/643cc07e3973dd1fded4)
 1. [RxSwiftの機能カタログ](http://qiita.com/k5n/items/e80ab6bff4bbb170122d)
 1. [Rxを使った設計をビジュアル化する](http://qiita.com/k5n/items/b8d2277cfe11f81111a7)

この中で RxCcoca が提供している Driver を説明してこなかったので、ここで説明しようと思います。

[公式の解説](https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Units.md) では Driver だけでなく ControlProperty, ControlEvent も含めて Units と呼んで説明されています。これらは RxCocoa で提供されていて、Rx に一般的なものではなく RxSwift 独自のものです。

RxSwift のバージョンは 3.0.1 です。

# UI層とのバインドで必要なこと

iOS では UI 部品の更新はメインスレッドで行う必要があります。例えば MVP (MVVM) パターンに従って Presenter (ViewModel) が画面の状態を表していて、ViewController がそれを表示するという形の場合、Presenter (ViewModel) では以下のことを行います。

 - メインスレッドで通知
 - shareReplayLatestWhileConnected を使った Cold-Hot 変換

コードだと以下のような感じですね。

```swift
final class Presenter {
  let buttonTitle: Observable<String>
  // ...

  init(model: Model) {
    buttonTitle = model.value
      .observeOn(MainScheduler.instance)
      .shareReplayLatestWhileConnected()
      
    // ...
```

UI 部品の値は onNext で伝えるだけで onError で伝えるものはありません。非同期処理をしてその結果を UI に伝える場合、Presenter より下でエラー処理していなかったら、UI の値に適用する前にエラー処理が必要になります。

```swift
    buttonTitle = model.value
      .observeOn(MainScheduler.instance)
      .catchError { _ in Observable.empty() }  // エラーは無視
      .shareReplayLatestWhileConnected()
```

# DRY(Don't Repeat Yourself)!

同じことを何度もやるなら共通化したくなります。そこで Driver です。

```swift
final class Presenter {
  let buttonTitle: Driver<String>
  // ...

  init(model: Model) {
    buttonTitle = model.value
      .asDriver(onErrorDriveWith: Driver.empty())  // エラーは無視
      
    // ...
```

こんな感じで Driver に変換すると、

 - メインスレッドで通知
 - shareReplayLatestWhileConnected を使った Cold-Hot 変換
 - エラー処理

をやってくれます。

利用側では subscribe や bindTo でなく drive を使います。

```swift
final class ViewController: UIViewController {
  // ...

  override func viewDidLoad() {
    super.viewDidLoad()

    presenter.buttonTitle.drive(button.rx.title())
      .addDisposableTo(disposeBag)

    // ...
```

drive メソッドは Driver にしかないので、buttonTitle が Driver でなく単なる Observable だったらコンパイルエラーになります。

つまり ViewController 側で購読をする際に drive を使うことで、 Presenter (ViewModel) 側に対して Driver を使うこと・・・言い換えると

 - メインスレッドで通知
 - shareReplayLatestWhileConnected を使った Cold-Hot 変換
 - onError 通知しない

を強制することができます。

# Driverへの変換メソッド

Observable を Driver に変換するメソッドは３つ用意されています。１つ目は先ほど紹介した onErrorDriveWith 引数で、エラーが発生時に指定した Driver のイベントを通知するバージョン。

```swift
observable.asDriver(onErrorDriveWith: Driver.empty())  // エラーを無視
```

次にエラーが発生したら指定した値を通知するバージョン。

```swift
observable.asDriver(onErrorJustReturn: [])  // エラーなら空配列を通知
```

あとはエラー内容に応じて処理を切り替えられるバージョン。

```swift
observable.asDriver(onErrorRecover: { error in
    if error is MyError { return Driver.empty }
    return Driver.just([])
});
```

# ControlProperty

RxCocoa が提供する UI 部品のプロパティ（rx.text, rx.title など）が ControlProperty として公開されています。ObservableType と ObserverType を継承しているので、Observable として扱うこともできるし、bindTo の引数に指定することもできます。

ControlProperty として公開されているものは、当たり前ですがメインスレッドで通知されますし、エラーを発生させないので、

```swift
label.rx.text.asDriver()
```

で Driver に変換できます。

# ControlEvent

RxCocoa が提供する UI 部品の発生させるイベント（rx.tap など）が ControlEvent として公開されています。ControlProperty と違って現在値を持たず、ObserverType でないので書き込み（外側からイベント発生）できません。

# Variableの場合

Variable はエラーを発生させないので、

```swift
presenter.value.asDriver()
```

で Driver に変換できます。メインスレッドで通知されるようになります。

# まとめ

もっと早くに説明したかったんですが、最初の「[オブザーバーパターンから始めるRxSwift入門](http://qiita.com/k5n/items/17f845a75cce6b737d1e)」ではまだスケジューラの話をしてなかったし、それ以降の記事でも本題から外れるし、Driver は RxSwift 本体じゃなくて RxCocoa の機能だし・・・で、別記事になりました。

ずっと途中まで書いて放置してたんですが、RxSwift の Advent Calendar １日目が空いたままだったので、完成させて公開してみました。