RxSwiftでUIの更新にはDriverを使ってみたまとめ

# Driver
[RxSwift/Documentation/Units.md #Driver](https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Units.md#driver)にも記載がある通り、
_UIレイヤーのリアクティブプログラミングのために提供されている部品_　らしいと最近認識しました


以前記事にした[RxSwiftを使ったRSSFeed取得のサンプル](https://github.com/mafmoff/RxRSSFeed)では
Viewの更新にも以下のように`Variable`変数を使っていたのですが、

```swift:これまで
        // entriesはVariable変数
        viewModel.entries.asObservable()
            .subscribe(onNext: { [unowned self] x in
                // 更新
                self.refresh.endRefreshing()
                self.tableView.reloadData()
                }, onError: { error in
                    // エラー
                    // FIXME: いつかやるかも
                }, onCompleted: { () in
                    // 完了
                }) { () in
                    
            }.addDisposableTo(disposeBag)

```

### これって**Driver**を使うべきなのでは・・・？ 
ということで、Swiftビギナーな自分が四苦八苦しながら**RxSwiftでUIの更新にはDriverを使ってみた**まとめです

# どんなアプリ？
:tada: : 以前作ったRSSFeedアプリのサンプルコードをリファクタリングしてみました

## 環境
* Xcode 7.3
* Swift 2.2
* RxSwift 2.3.1
* iOS8以上

## リポジトリ
[mafmoff/RxDriveRSSFeed](https://github.com/mafmoff/RxDriveRSSFeed)

##概要
過去の[記事](http://qiita.com/mafmoff/items/4818c113f203db54c258#%E6%A6%82%E8%A6%81)にあります

# Driverを使う
## イメージです
<img width="1244" alt="スクリーンショット 2016-04-01 19.04.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/98613/8dd0028f-97c3-b160-ef63-7501ad8f0e84.png">


## Driverの使い所
### 1. **Model** : （準備） リクエスト結果を`Variable`に反映してViewModelに通知

ここでは、リクエストを実行し結果を`Subscribe`して各`Variable`値を更新します
これでViewModel側に各値の変更が通知されます
この時API通信はバックグランドで行うように指定しておきます

```swift:ListModel

    /// Load
    final let isLoading = Variable(false)
    /// Entries
    final let entries = Variable([Entry]())
    /// Error
    final let error: Variable<ErrorType?> = Variable(nil)
    /// Rx Dispose
    final let disposeBag = DisposeBag()
    
    final func request() {
        if isLoading.value {
            return
        }
        isLoading.value = true
        error.value = nil
        
        let request = FeedRequest()
        request.connect()
            // API接続はBackgroundを指定する
            .subscribeOn(SerialDispatchQueueScheduler(globalConcurrentQueueQOS: .Background))
            .subscribe(
                onNext: { [weak self] in self?.entries.value = $0.feed.entries },
                onError: { [weak self] in
                    self?.error.value = $0
                    self?.isLoading.value = false },
                onCompleted: { [weak self] in self?.isLoading.value = false }
            )
        .addDisposableTo(disposeBag)
    }
```

それぞれView側のUIと紐付きます

* isLoading：UIRefreshControlの表示・非表示
* entries：UITableView上に表示する一覧データ
* error：エラー時用のUIViewの表示・非表示
 
### 2. **ViewModel** : モデルから受け取った値を`Driver`に変換する
リクエストで受け取った結果を`Driver`に変換します

:tada: TIPS: `Driver.never()`しておくと値を持たずCompletedすら発生しません
:tada: TIPS: `Driver`を使うには``import RxCocoa``が必要でした


```swift:ListViewModel

    /// Request Model class
    final private var model = ListModel()
    /// Entries
    final var entries = [Entry]()
    // Rx
    /// Data Updated
    final private(set) var dataUpdated: Driver<[Entry]> = Driver.never()
    /// Loading flg
    final private(set) var isLoading: Driver<Bool> = Driver.never()
    /// Error flg
    final private(set) var isError: Driver<Bool> = Driver.never()
    
    override init() {
        super.init()
        
        // ViewController側でTableViewの更新と紐付けます
        dataUpdated = Driver
            // エラーのとき、一覧に表示するエントリーを0件にするように変換しています
            .combineLatest(model.entries.asDriver(),
                           model.error.asDriver().map { $0 != nil },
                           resultSelector: { ($1) ? [] : $0 })
        // ViewController側でRefreshControlのステータスと紐付けます
        isLoading = model.isLoading.asDriver()
        // ViewController側でエラー画面の表示と紐付けます
        isError = model.error.asDriver().map { $0 != nil }
    }

```

1.のリクエストで`Variable`が更新されるたびに、`Driver`からはobserverに対して通知が行われます
ViewControllerではこの通知をトリガーにして、バインドしている各UIの更新を行います

### 3. **ViewController** : ViewModelの`Driver`をUIにバインドする
`ViewDidLoad()`内で`Driver`と各UIを紐付けます

```swift:ListController

        // TableView Reload
        viewModel.dataUpdated
            .driveNext { [unowned self] in
                self.viewModel.entries = $0
                self.tableView.reloadData()
            }
            .addDisposableTo(disposeBag)

        // Loading Status
        viewModel.isLoading
            .drive(refresh.rx_refreshing)
            .addDisposableTo(disposeBag)
        
        // Error View
        viewModel.isError
            .map { !$0 }
            .drive(errorView.rx_hidden)
            .addDisposableTo(disposeBag)

        // Pull Refresh
        refresh.rx_controlEvent(.ValueChanged)
            .subscribeNext { [unowned self] _ in self.viewModel.reloadData()
        }.addDisposableTo(disposeBag)


```

これで、リクエストをして結果が更新されるたびに`Driver`からはobserverに対して通知が行われ、バインドしている各UIの更新が働きました

## できたもの
<img width="867" alt="スクリーンショット 2016-04-02 14.46.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/98613/053c1fad-98d6-b547-1f4e-abc220f03099.png">



# まとめ
* `Driver`はメインスレッド処理に適している
* エラー発生しない

以上です :tada: 
苦しみながら書きました！RxSwiftは難しいです・・（勉強会参加したかった）

Modelクラスなど、もっとかっこいい設計がありそうです。
今後もさらによい実装方法を勉強させて頂きます！
読んでいただき、ありがとうございました