Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

(この記事は 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は私的にそそられているので引き続き練習していこうと思っています。しかしあくまでパラダイムの選択は自由なので、「自分が一番使いやすいものを使う」ということが大切です。

参考

keisei_1092
よろしくお願いします。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした