RxSwift2.0.0でMVVMモデルなRSSFeedアプリを作ってみたまとめ

:tada: 2016/04/02更新：[RxSwiftでUIの更新にはDriverを使ってみた記事](http://qiita.com/mafmoff/items/e8786cd75c292cdc3ec0#%E3%81%A7%E3%81%8D%E3%81%9F%E3%82%82%E3%81%AE)も書いてみました！

# RxSwift
Reactive ProgrammingをSwiftで実現するライブラリ
[Qiitaの記事](https://qiita.com/tags/rxswift)も増えてきました！

便利そうだけど使いどころは・・・？
ということで、Swiftビギナーな自分が**RxSwiftでMVVMモデルなRSSFeedアプリ**を作ってみたまとめ

# どんなアプリ？ :pushpin: :pushpin: :triangular_flag_on_post: :tada: 

## 環境
* Xcode 7.2
* Swift 2.1
* RxSwift 2.0.0
* iOS8以上

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

##概要
<br><img width="320" alt="IMG_6373.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/98613/6a3865bf-238c-e569-e4c6-af23faf78d94.png"><br>
* [Sketch App Sources](http://www.sketchappsources.com/)で配信しているRSS FeedをTableViewでリスト表示
* リストを選択すると詳細をWebViewで表示
* 取得するRSSはXMLなので、[Google AJAX Feed API](http://www.ajaxtower.jp/googleajaxfeed/)を使ってJSONで取得

:tada: TIPS: 今回は練習用アプリなので[ATSは無効化](http://qiita.com/yanayanalte/items/e6d83c12af77fa238a58)してあります


##使用ライブラリ
|Library|Use|
|---|---|
|[Alamofire](https://github.com/Alamofire/Alamofire)|Swift用通信ライブラリ|
|[ObjectMapper](https://github.com/Hearst-DD/ObjectMapper)|JSONのレスポンスをFeedクラスにマッピング|
| [HTMLReader](https://github.com/nolanw/HTMLReader)|JSONに混在している HTMLタグをパース|
|[RxSwift](https://github.com/ReactiveX/RxSwift)|今回の主役、ReactiveProgrammingをサポート|
[SDWebImage](https://github.com/rs/SDWebImage)|（Obj-C製）リストの画像ロードに使用|
|[SVProgressHUD](https://github.com/TransitApp/SVProgressHUD)|（Obj-C製）詳細画面のローディング表示|

当初、めちゃいけてるPullRefreshライブラリ[DGElasticPullToRefresh](https://github.com/gontovnik/DGElasticPullToRefresh)を使う予定でしたが、AutoLayoutのバグで使用を断念

# RxSwiftでのMVVMモデルの実現
## こんなイメージ
<img width="752" alt="スクリーンショット 2016-01-08 0.01.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/98613/20dd2417-8407-9293-6d65-33725e17a0bc.png">


## RxSwiftの使い所 
### 1. **Model** : リクエストで`Observable`オブジェクトの生成
ここでは、APIリクエストで`Observable`オブジェクトを生成
レスポンスのJSONは、**ObjectMapper**を利用して作成したモデルクラス`FeedResponse`にマッピングしておくと楽でした


```swift:FeedRequest.swift
    func connect() -> Observable<FeedResponse> {
        let observable: Observable<FeedResponse> = Observable.create { (observer: AnyObserver<FeedResponse>) in
            self.request?.responseJSON(completionHandler: { response in
                print("\(response)")
                switch response.result {
                case .Success(let value):
                    guard let object = Mapper<FeedResponse>().map(value) else {
                        return observer.onCompleted()
                    }
                    observer.onNext(object)
                    observer.onCompleted()
                case .Failure(let error):
                    observer.onError(error)
                }
            })
            return AnonymousDisposable {
                
            }
        }
        return observable
    }
```

:tada: TIPS: `func connect() -> Observable<FeedResponse>`の部分をジェネリクスにすると、他のモデルクラスでも汎用的に使えます

>ちなみに、この実装を実現しているライブラリが[taktem/TAKSwiftSupport](https://github.com/taktem/TAKSwiftSupport/blob/master/TAKSwiftSupport/Core/Model/HttpRequest/RequestBase.swift)です
こちらの`RequestBase`を親としたリクエストクラスを作成すると便利です
taktemさんありがとうございます

:tada: TIPS: `Observable.create`は**RxSwift 2.0.0-beta**以前では`create`のみの記述でした


### 2. **ViewModel** : リクエスト呼び出し、レスポンスを`Variable`変数に格納

プロパティには`Variable`型の変数を宣言しておく
このとき、`Variable`変数は初期化の際にも値の更新とみなされ`sendNext`されてしまうので、意図しない`Subscribe`を避けるためfilteringできるように**空もしくはnil**にしておきます

```swift:ListViewModel.swift
    /// Request class
    private var request = FeedRequest()

    /// Feed
    private(set) var feed: Variable<Feed?> = Variable(nil)

    /// Entry
    private(set) var entries: Variable<[Entry]> = Variable([])

    /// Error
    private(set) var error: Variable<NSError?> = Variable(nil)

// 省略

func reloadData() {
        request.connect().subscribe(onNext: { [weak self] x in
            // Variableプロパティに格納
            self?.entries.value = x.feed.entries
            }, onError: { error in
                
            }, onCompleted: { () in
                
            }) { () in
                
        }.addDisposableTo(request.disposeBag)
    }
```
リクエストから受け取った`FeedResponse`型のオブジェクト`x`から、TableViewに表示するデータソース用の配列`entries`オブジェクトを取り出し、プロパティに代入し直す
**この代入をトリガーにして、TableViewのリロードを行います**

:tada: TIPS: `Variable`の変数は外部クラスから書き換えを禁止するためアクセスレベルを`private(set) ` にしておくと安全です

### 3. **ViewController** : ViewModelの`Variable`変数を`Subscribe`　
ViewModelで宣言した`Variable`オブジェクトは`.asObservable()`で`Observable`に変換できるので、ViewController上で`Subscribe`を行います
2.で代入した際の値の更新をトリガーに、TableViewのリロードを実施


```swift:ListController.swift

   func bind() {
        // Connection
        viewModel.entries.asObservable().filter { x in
            // 初期化の際にsubscribeをさけるためfilter
            // 配列の要素数が0件より多くなったときにsubscribeを実施
            return !x.isEmpty
            }.subscribe(onNext: { [unowned self] x in
                // 更新
                self.refresh.endRefreshing()
                self.tableView.reloadData()
                }, onError: { error in
                    // エラー
                    // FIXME: いつかやるかも
                }, onCompleted: { () in
                    // 完了
                }) { () in
                    
            }.addDisposableTo(disposeBag)
        

        // Pull Refresh
        refresh.rx_controlEvent(.ValueChanged).subscribeNext { [unowned self] x -> Void in
            // プルリフレッシュを購読し、ValueChangedがあった場合APIリロードを実行
            self.viewModel.reloadData()
        }.addDisposableTo(disposeBag)
    }
           
```
:tada: TIPS: `.filter`はViewModelでの`entries`変数の初期化（空配列の生成）の`Subscribe`を避けるために実施しています

:tada: TIPS: `.asObservable()`は**RxSwift 2.0.0-beta**以前では不要でした（ちょっと面倒になった…？）


# まとめ
* 値が更新される度に`bind`部分の処理を通るので、プルリフレッシュ、インフィニットロードで再度API通信を行ったときなどもレスポンス後の処理が分散しない
* MVVMモデルでViewControllerが肥大化せず可読性も上がるはず
　

以上です :tada: 
Swift歴はまだまだ浅いので、さらによい実装方法を勉強させて頂きます！
ありがとうございました