Observable.just()からはじめるRxSwift

  • 4
    いいね
  • 0
    コメント

(この記事は CPS Lab Advent Calendar 2016 の25日目の記事です)

Reactive Programmingの流行をみて、とりあえずどんなものかと手探りでチュートリアルをガサゴソしていたのですが、よさげなプレゼンを見つけたので咀嚼してみた。

とりあえずRxSwiftとRxCocoaいれる

  • Xcodeで「Single Application」で新規プロジェクトを作成
  • ターミナルで当該フォルダまで cd
  • bundle init
  • Gemfileが生成されるのでGemfileの中に gem 'cocoapods' と書いて bundle install
  • bundle exec pod init
  • Podfileが生成されるのでPodfileの中に pod 'RxSwift' (改行) pod 'RxCocoa'
  • bundle exec pod install
  • open (プロジェクト名).xcworkspace

Observable.just() からはじめるRxSwift

ViewControllerの viewDidLoad() の中に以下を書いて実行します

ViewController.swift
let _ = Observable.just(1).subscribe(
    onNext: { event in
        print(event)
    }
)
  • let _ = ... こう書かないと「返り値があるからそいつ受け止めてくれ〜」って怒られる
  • Observable Rxのちから :muscle: を使う
  • .just これを宣言すると引数がこの場合は Int から Observable<Int> に変わります
  • .subscribe forEachのようなもので、 onNextonError などにクロージャを渡すとObservableの要素を順番になめてくれる

複数のObservableを流してみる

Observable.just() では単一の要素を流すことができました。
次に Observable.from() を使ってみましょう。

ViewController.swift
let _ = Observable.from(0...10).subscribe(
    onNext: { event in
        print(event)
    }
)

0...10 はSwiftで範囲を表す記法です

これで実行すると0から10までの数字がコンソールに表示されたことかと思います。
Observable.from() を使うと、配列の各要素をObservableでラップしてくれ、subscribeで順番になめるということができます。

エラーハンドリングをしてみよう

まず「単一の Error 」をsubscribeしてみましょう。

先にErrorプロトコルを採用したenumを作っておきます。
class ViewController の宣言の外に書いてください。

ViewController.swift
enum MyError: Error {
    case error
}

次にエラーのObservableを生成してみます。こちらは viewDidLoad() の中で行います

ViewController.swift
let _ = Observable.error(MyError.error).subscribe(
    onNext: { event in
        print(event)
    },
    onError: { error in
        print("エラー! \(error)")
    }
)

こう書いて実行すると エラー! error と表示されます
エラータイプのObservableをsubscribeしたので、 onError に入りました。

配列の要素の中にエラーを混入させてみる

先ほど試した Observable.from() を使って、普通の要素の中にエラーを混入させてみます。
enum MyError はまだ使います。

ViewController.swift
Observable<Any>.create { observer in
    [1, 2, 3, 4].forEach({
        observer.on(.next($0))
    })
    observer.on(.error(MyError.error))
    return Disposables.create()
}.subscribe(
    onNext: { event in
        print(event)
    },
    onError: { error in
        print("エラー! \(error)")
    }
)

Observable.create() というのが出てきましたが、上半分では4つ普通の要素を注入した最後にErrorを注入しています。下半分は変わっていません。 1 2 3 4 エラー! error という風にコンソールに出てくればOKです。

そしてUITableViewへ

なんか just とか from とか subscribe とか onNext とかを喋ればなんとかなるということが薄々わかってきたところで、UIの実装をやってみます。

ここまでいろいろ試してきた ViewController.swift (クラス名 ViewController )に少し手を加えます。

  • TableViewController.swift (クラス名 TableViewController )にする
  • 継承元を UIViewController => UITableViewController
  • StoryboardからViewControllerを消してTableViewControllerに
    • Identity Inspectorのクラスの名前を変更
    • Attributes InspectorでIs Initial View Controllerにチェックを入れる
    • TableViewのprototype cellを選択してAttributes Inspectorの中からIdentifierに cell と設定する

さて、通常の書き方でUITableViewをやると次のようなメソッドをよく書いていることかと思います。

TableViewController.swift
override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 20
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")   
    cell.textLabel?.text = indexPath.row
    return cell
}

ここでついにRxCocoaの登場です。上のコードは書かずに、次のコードのようにクラスを変更してみましょう。

TableViewController.swift
class TableViewController: UITableViewController {

    var disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        tableView.delegate = nil
        tableView.dataSource = nil
        let items = Observable.just((0..<20).map { "\($0)" })
        items.bindTo(tableView.rx.items(cellIdentifier: "cell", cellType: UITableViewCell.self)) { (row, element, cell) in
            cell.textLabel?.text = "\(element)"
        }
        .addDisposableTo(disposeBag)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

これで実行してみてください。 0 から 19 の要素をラベルにもったセルを生成してやることができました。ここらへんになってくると私もまだ理解できていないのですが、だいたいやっていることとしては

  • disposeBag というのを使うとメモリリークを防げるらしい
  • tableViewのdelegateとdataSourceをnilにする(こうしないとエラーで死ぬ)
  • 0 から 19 という要素の入った items というObservableな配列を生成する。
    • そして、tableViewにバインドする。
      • セルは cell と名をつけたprototype cellを使用する
      • テキストラベルの値に要素の値を利用する

という感じでしょうか。

ここまで、Observableの要素を生成するところからObservableな要素群をUIKitのコンポーネントにバインドするところまでをさらってみました。自分も書きながら理解するところがありとてもためになりました。
Reactive Programmingは私的にそそられているので引き続き練習していこうと思っています。しかしあくまでパラダイムの選択は自由なので、「自分が一番使いやすいものを使う」ということが大切です。

参考