swift初心者がSmartNews風ニュースアプリを作ってみる過程を晒す(8)　- RxSwiftを用いてMVVMを実装する


## <i class="fa fa-cube" style="font-size:1em;"></i> はじめに

swiftはほとんど未経験ですが、SmartNews風ニュースアプリを作ってみて、その過程をさらしています。


![スクリーンショット 2016-07-03 11.39.55.png](https://qiita-image-store.s3.amazonaws.com/0/34321/21241a1d-6807-a7e7-ae0f-93b099835419.png)

前回は、こんな記事を書きました。

[swift初心者がSmartNews風ニュースアプリを作ってみる過程を晒す(7)　- MVVMで関心事を分離する手法を学ぶ - Qiita](http://qiita.com/tjnet/items/e26efd33b896434b0f66)

今回は、RxSwiftを用いて実際にMVVMアーキテクチャを使用した実装を行います。

## <i class="fa fa-cube" style="font-size:1em;"></i> RxSwiftを使用したMVVM

取得した記事を表示するシンプルなニュースアプリを作成します。
ニュース記事のデータは、[ここ](http://menthas.com/top/rss)から取得します。

## <i class="fa fa-cube" style="font-size:1em;"></i> Model

Entryモデルは一つ一つの記事を表現します。
JSONオブジェクトをparseし、各プロパティにセットすることで初期化されます。

```Entry.swift
import Foundation
import RealmSwift

class Entry: Object {
    
    dynamic var title: String = ""
    dynamic var link: String = ""
    dynamic var contentSnippet: String = ""
}
```

## <i class="fa fa-cube" style="font-size:1em;"></i> ViewModel

ViewModelには以下の責務をもたせます。

- ビジネスロジック(ネットワークリクエストを発生させるetc)
- 表示に必要なデータをViewControllerに届ける
- Modelの更新を監視する

```EntriesViewModel.swift
import RxSwift
import RxCocoa

final class EntriesViewModel {
    
    //MARK: - Dependecies
    
    //MARK: - Model
    
    var entries: Driver<[Entry]> = Driver.never()
    
    //MARK: - Set up
    
    init() {
        //Initialise dependencies

    }
    
    func reloadData(title: String) {
        entries = EntryAPIService().fetchEntries(q: title)
    }
}
```

DriverはUIレイヤーを直感的にリアクティブプログラミングするための部品です。
Driverって何なんだ、ということに関しては、[こちら](http://www.finneycanhelp.com/2015/12/rxswift-driver/)に詳しく記載されています。

>Units are totally optional. You can use raw observable sequences everywhere in your program and all RxCocoa APIs work with observable sequences.

[公式ドキュメント](https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Units.md#driver-unit)にも記載があるように、使用しないと実装できないものでもないですが、便利なので使いましょう、という位置付けのようです。


### Driverの特徴

Driverの特徴は以下の通りです。

- エラーでストリームが終了しない
 - JSONのparseエラーやAPIとの通信失敗が起きた場合に、UIコンポーネントが更新できなくなるようなことを防ぎます
- main schedulerでsubscribe, observeする
 - API通信の結果を使用して、バックグラウンドスレッドでUIコンポーネントを更新するようなことをするとCrashの原因になります
- 副作用をシェアする
 - API通信の結果を複数のUIコンポーネントにバインドした時に、２回HTTPリクエストが発生するような事を回避します

[公式ドキュメント](https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Units.md#driver-unit)では、典型的なサンプルコードを交えて非常に詳しく解説されているので、一読をお勧めします :bow: 

## <i class="fa fa-cube" style="font-size:1em;"></i> ViewController

```TableViewController.swift

   @IBOutlet weak var tableView: UITableView!
    
    private let bag = DisposeBag()
    private var entryList: [Entry]  = []
    
    // create ViewModel
    let viewModel = EntriesViewModel()

    override func viewDidLoad() {
        
        super.viewDidLoad()
        self.tableView.registerNib(UINib(nibName: "EntryTableViewCell", bundle: nil), forCellReuseIdentifier: "EntryTableViewCell")
        
        viewModel.reloadData(self.title!) //1
        
        // see http://yannickloriot.com/2016/01/make-uitableview-reactive-with-rxswift/
        // bind articles to UITableView
        
        // If there is a `drive` method available instead of `bindTo`,
        // that means that the compiler has proven that all properties
        // are satisfied.
        viewModel.entries.drive(self.tableView.rx_itemsWithCellIdentifier("EntryTableViewCell")) { //2
            (index, entry: Entry, cell:EntryTableViewCell) in
            cell.updateCell(entry)
        }.addDisposableTo(bag) //3
        
        viewModel.entries.driveNext { [unowned self] in //4
            self.entryList = $0
        }.addDisposableTo(bag)
        
    }
```

- 1. APIにアクセスして記事の取得を行っています。
- 2. entriesの更新をトリガーにして、tableviewの更新を行っています。
- 3. unsubscribeを自動的に行って、リソースを解放するための記述です。

> Using dispose bags or takeUntil operator is a robust way of making sure resources are cleaned up. We recommend using them in production even if the sequences will terminate in finite time.



## <i class="fa fa-cube" style="font-size:1em;"></i>感想

- 各クラスの責務が明確になり、仕様変追加時の変更箇所が明確になりました。
- テストが書きやすくなりそうです。

## <i class="fa fa-cube" style="font-size:1em;"></i>おわりに

ソースコードは[こちら](https://github.com/tjnet/NewsAppSwiftMVVM)です。

次回は、少し趣を変えて、実装したアプリケーションのテストコードを書いていきます。


## <i class="fa fa-cube" style="font-size:1em;"></i>参考

- [Introduction to Rx](http://www.introtorx.com/)
- [ReactiveX - Intro](http://reactivex.io/intro.html)
- [RxSwiftCommunity/RxDataSources: Table and Collection View Data Sources for RxSwift](https://github.com/RxSwiftCommunity/RxDataSources)
- [RxSwift + MVVM - SSSSLIDE](http://sssslide.com/speakerdeck.com/ishkawa/rxswift-plus-mvvm)
- [【翻訳】あなたが求めていたリアクティブプログラミング入門 - ninjinkun's diary](http://ninjinkun.hatenablog.com/entry/introrxja)
- [[初心者]RxSwiftの道標[入門] - Qiita](http://qiita.com/nakailand/items/8b54dc9b4b39b0809c57)
- [tailec - Pawel Krawiec iOS blog](http://www.tailec.com/blog/reactive-recipe-2)
- [A practical MVVM example in Swift – Part 2 (featuring RxSwift) – Candy Code](http://candycode.io/a-practical-mvvm-example-in-swift-part-2/)
- [RxSwiftで実行するSchedulerの作り方とお行儀良く扱うためのメモ · Tomorrow Never Comes.](http://blog.sgr-ksmt.org/2016/03/15/rxswift_scheduler/)