RxSwift触ってみた


先週メルカリのグループ会社ソウゾウがリリースした「アッテ」の開発の裏側を聞ける[atte FeS【Go・Swift開発編】](http://mercari.connpass.com/event/29506/)に行ってきました。

その際の発表資料が[こちら](http://tech.mercari.com/entry/2016/04/20/130620)で公開されており、その中でも[SwiftとRxSwift](https://speakerdeck.com/bricklife/atutekai-fa-falseji-shu-swift-to-rxswift)の内容を聞いてRxSwiftに興味を持ったので今更ですが入門してみました。

## RxSwiftとは

[ReactiveX](http://reactivex.io/intro.html)(Reactive Extensions)のSwift実装です。他にもRxJavaやRxJSなど各言語や各プラットフォーム用のRxがあります。

ReacitveXというのは

> ReactiveX is a library for composing asynchronous and event-based programs by using observable sequences.

らしいです。

> observableのシークエンスを使って非同期でイベントベースのプログラムを実現するためのライブラリ。

という感じでしょうか？


この`observable`というのがキモらしいのですがよくわからないので実際に動かしてから勉強しようと思いました。

## 導入してみる

[RxSwift](https://github.com/ReactiveX/RxSwift)のinstllationを見るとCocoaPodsやCarthageで導入できるようです。今回はCocoaPodsで導入してみます。

まずRxSwiftSampleというプロジェクトを作成します。

そして`Podfile`に下記のように記述。

```:Podfile
use_frameworks!

target 'RxSwiftSample' do
    pod 'RxSwift',    '~> 2.0'
    pod 'RxCocoa',    '~> 2.0'
end

# RxTests and RxBlocking have most sense in the context of unit/integration tests
target 'RxSwiftSampleTest' do
    pod 'RxBlocking', '~> 2.0'
    pod 'RxTests',    '~> 2.0'
end
```

下記のコマンドを実行します。

```sh
$ pod install
...
Installing RxBlocking (2.4)
Installing RxCocoa (2.4)
Installing RxSwift (2.4)
Installing RxTests (2.4)
...
```
ということで2.4が入ったようです。

その他の環境は下記で試しています。

```
Xcode
Version 7.3 (7D175)

pod --version
0.39.0
```

## RxSwiftを使って実装してみる

### テキストフィールドの変更をラベルに自動反映

さっそく[SwiftとRxSwift](https://speakerdeck.com/bricklife/atutekai-fa-falseji-shu-swift-to-rxswift)のP37にある「テキストフィールドの変更をラベルに自動反映」を試してみます。

Rxを使わない実装と比較するためにラベルとテキストフィールドを２つずつ配置します。
左側をRxを使った実装(label1, textField1)、右側を既存実装(label2, textField2)としてみます。

![Screen_Shot_2016-04-26_at_02_12_47.png](https://qiita-image-store.s3.amazonaws.com/0/8868/bc56fa48-0f67-fc54-71ab-836a7738b9d9.png "Screen_Shot_2016-04-26_at_02_12_47.png")

とりあえずRxSwiftを使った実装は下記。

```swift:ViewController.swift
import UIKit
import RxSwift		// 1. RxSwiftインポート
import RxCocoa		// 2. RxCocoaインポート

let disposeBag = DisposeBag()	// 3. unsubscribeに必要なもの？

class ViewController: UIViewController {

    // for Rx impl
    @IBOutlet weak var label1: UILabel!
    @IBOutlet weak var textField1: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        textField1.rx_text					// 4. rx_textは後述
            .map {"「\($0)」"}				// 5. 変更があった要素に「」つけて
            .bindTo(label1.rx_text)			// 6. labelに反映
            .addDisposableTo(disposeBag)	// 7. 不要になったらunsubscribe 
    }

	// ...

}

```

おお、これだけでテキストフィールドの変更がさくさくラベルに反映される！楽しい。

### rx_textとは？

さて、突然textField1に`rx_text`というプロパティができていますが、
見てみるとRxCocoaが下記のようなextensionで`rx_text`を定義していました。

```swift:UITextField+Rx.swift
extension UITextField {
    /**
    Reactive wrapper for `text` property.
    */
    public var rx_text: ControlProperty<String> {
        return UIControl.rx_value(
            self,
            getter: { textField in
                textField.text ?? ""
            }, setter: { textField, value in
                textField.text = value
            }
        )
    }
}

```

さらに掘っていくと`rx_text`は`ObservableType`というprotocolに適合していました。
こいつが`observable`!（まだ全然わかってないけど）

そしてその`rx_text`を`map`, `bindTo`, `addDisposableTo`といろいろ操作をしていくようです。

詳しくはまた今度見ようと思いますが、

- obsevableを一個一個処理して(map)
- observerに紐付けて(bindTo)
- いらなくなったらremoveObserver(addDisposableTo)

と想像。

## 既存実装で同じ動きを実装してみる

比較のためRxSwiftを使わないで実装してみます。
いくつか実装方法はあると思いますが、テキストが変更されたときに通知されるUITextFieldTextDidChangeNotificationを使って実装してみます。

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController {

    // for existing impl
    @IBOutlet weak var label2: UILabel!
    @IBOutlet weak var textField2: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        // for existing impl
        label2.text = "「」"
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UITextInputDelegate.textDidChange(_:)), name: UITextFieldTextDidChangeNotification, object: self.textField2)
    }

	// ...	

    func textDidChange(notification: NSNotification) {
        if let text = textField2.text {
            label2.text = "「\(text)」"
        }
    }

    deinit {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}
```

これでも同じ動きは実装できましたが、テキストが変更されたときの処理やremoveObserverの処理を別メソッドとして離れた場所に実装することになるので対象のViewの数が増えるとどれがどの処理がわかりにくくなりそうです。

一方でRxSwiftを使うとすべてをviewDidLoadの１箇所に書けるので、Rxの流儀に慣れればすごくソースが読みやすくなる気がしました。

## まとめ

とりあえずなんとなくどう動いているのかというのとRx素敵そうというイメージが掴めました。次はなぞの`observable`に具体的にどんなことができるのか見ていこうと思います。

**今回のソース**
https://github.com/jollyjoester/RxSwiftSample