RxSwiftのbindToについて

RxSwiftにはbindToが大きく分けて2種類あります。

引数にobserverを取るものと、binderを取るものです。 引数にvariableを取るものは基本observableを取るものと動きが同じです。

以下のような特徴があります。

引数にobserver/variableを取るもの

内部的に即subscribeが呼ばれる。

実装はこうなっています。(複数ありますが、簡単なほうを抜粋)

    /**
    Creates new subscription and sends elements to observer.
    
    In this form it's equivalent to `subscribe` method, but it communicates intent better, and enables
    writing more consistent binding code.
    
    - parameter observer: Observer that receives events.
    - returns: Disposable object that can be used to unsubscribe the observer.
    */
    @warn_unused_result(message="http://git.io/rxs.ud")
    public func bindTo<O: ObserverType where O.E == E>(observer: O) -> Disposable {
        return self.subscribe(observer)
    }

A=>B というシーケンスを作って即実行したい時に使えます。

引数にbinderを取るもの

内部的には引数のbinder: Self -> R のクロージャがselfを引数に実行されるだけ。

実装はこうなっています。(複数ありますが、簡単なほうを抜粋)

    /**
    Subscribes to observable sequence using custom binder function.
    
    - parameter binder: Function used to bind elements from `self`.
    - returns: Object representing subscription.
    */
    @warn_unused_result(message="http://git.io/rxs.ud")
    public func bindTo<R>(binder: Self -> R) -> R {
        return binder(self)
    }

A=>Bというシーケンスだけ作っておいて、後で別のシーケンスとつなげたり、subscribeのタイミングをコントロールしたりする時に役立ちます。

ちょっと例が雑ですが、以下のようにログイン状態の時とそうでない時でシーケンスを変えるなんて使い方はいかがでしょうか。

    var loggedIn = true
    override func viewDidLoad() {
        super.viewDidLoad()

        let number1Seq01 = number1.rx_text.bindTo(verifyString).retry(3)
        if loggedIn {
            number1Seq01
                .bindTo(doThisOnlyWhenLoggedIn)
                .bindTo(number4.rx_text)
                .addDisposableTo(disposeBag)
        } else {
            number1Seq01
                .bindTo(number4.rx_text)
                .addDisposableTo(disposeBag)
        }
    }

    func verifyString(observable: ControlProperty<String>) -> Observable<String> {
        return Observable.create {
            observer in
            /* Do something useful... */
            return observable.subscribeNext {
                value in
                print(#function)
                observer.onNext(value)
            }
        }
    }
    func doThisOnlyWhenLoggedIn(observable: Observable<String>) -> Observable<String> {
        return Observable.create {
            observer in
            /* Do something useful... */
            return observable.subscribeNext {
                value in
                print(#function)
                observer.onNext(value)
            }
        }
    }

まとめ

先日参加した 第2回RxSwift勉強会 @ Sansanにて「bindToはsubscribeと同じ」という言及があって、アレそうだったっけ?と思って再度検証してみました。

厳密にはsubscribeと同じ意味のものと、そうでないものがありました。bindToのbinderファンクションの仕組みを利用する事で、複雑なシーケンスも普通の変数として取り回す事も出来るようになりました。

実際には、私は起動シーケンスの処理でこの Observable.create とbindTo を多用しています。色々な処理を様々な依存関係で記述する必要があるような時に、あると便利な道具だと思います。

ご意見募集中です。