LoginSignup
44
42

More than 5 years have passed since last update.

RxSwiftに取り組み始めた

Posted at

もくもくと勉強する会(如法会 その2)に参加しています。広島の勉強会ですが、長野の自宅から参加です。

題材はなんでも良いらしいので、前から気になっていたRxSwiftについて学んでみました。

実は以前にもRxSwiftをアプリに組み入れようとしてみたのですが、いざ取り組んでみようとしたらXcodeを前に「で、自分の場合は何から手を付けたら??」と固まってしまった諦めた経験があるので、この機に基本からやり直すことに決めた次第です。

やったこと・わかったこと

ArrayやMapをObservable()にする

RxSwiftの土俵に乗るためには、まず何より対象をストリームに乗せるべく、Observable化しなければいけません。

ArrayやMapのようなSequenceTypeは、ArraySequenceTypeのextensionで定義されたtoObservable()メソッドによって、Observable化できます。

// Arrayのextensionが使われる
print("\([1,2,3,4,5,6].toObservable())")
// → RxSwift.Sequence<Swift.Int> Array

// SequenceTypeのextensionが使われる
print("\((0...100).toObservable())")
// → RxSwift.Sequence<Swift.Int> 

print("\([0:1, 2:3, 4:5, 6:7].toObservable())")
// → RxSwift.Sequence<(Swift.Int, Swift.Int)>

ArraySequenceTypeの2種類のtoObservable()は、どちらもObservable+Creation.swiftに実装があり、Sequenceクラスをインスタンス化して返すようになっていました。下はArray版のコードです。

// extension Array
@warn_unused_result(message="http://git.io/rxs.uo")
public func toObservable(scheduler: ImmediateSchedulerType? = nil) -> Observable<Generator.Element> {
    return Sequence(elements: self, scheduler: scheduler)
}

他方のSequenceTypeもほとんど同じで、上のコードでelements引数に対してArray(self)と配列化していました。MaptoObservable()を呼ぶと、タプルの配列に変化するようです。

// extension Array
@warn_unused_result(message="http://git.io/rxs.uo")
public func toObservable(scheduler: ImmediateSchedulerType? = nil) -> Observable<Generator.Element> {
    return Sequence(elements: Array(self), scheduler: scheduler)
}

このschedulerは、デフォルト引数によってnilが渡されていますが、代わりにConcurrentMainSchedulerSerialDispatchQueueSchedulerも指定できるようです。このへんの使い分け方はまだわかりません。

いちおうnilを渡したときの処理を追ってみると、インスタンス化の時点では単にメンバー変数にそのまま渡しているものの、Sequenceクラスのsubscribe()メソッド内でスケジューラの有無による別処理が走ることになっていました。コメントにある通り、その場で実行することで、わざわざSequenceSinkにご登場いただかなくても済むようになっています。

// Sequence.swift
class Sequence<E> : Producer<E> {
    :
    override func subscribe<O : ObserverType where O.E == E>(observer: O) -> Disposable {
        // optimized version without scheduler
        guard _scheduler != nil else { // ★nilの場合はここに入る!
            for element in _elements {
                observer.on(.Next(element))
            }

            observer.on(.Completed)
            return NopDisposable.instance
        }

        let sink = SequenceSink(parent: self, observer: observer)
        sink.disposable = sink.run()
        return sink
    }
}

要素を順に読んでいく

Observable化したあとは、RxSwiftの言葉で話します。ArrayやMapの要素を1つずつ読んでいくにはObservableTypeのextension、subscribe()メソッドを使います。先ほどのSequence.swiftファイルで出てきました。

[1,2,3,4,5,6].toObservable()
    .subscribe { event in
        print("\(event)")
        // Event(1) → Event(2) → ... → Completed
    }
    .addDisposableTo(DisposeBag())

クロージャに届くのはenumのEventで、それぞれの値がenumにラップされているものです。この型はとてもシンプルなもので、デバッグプリントなどを除けば以下で表現できる簡単な型です。

public enum Event<Element> : CustomDebugStringConvertible {
    case Next(Element)
    case Error(ErrorType)
    case Completed
}

上のコードは、実行するとEvent(1) → Event(2) → ... → Completedというように1行ずつ出力されて終わります。最後にCompletedが届いているので、きちんとswitchなどでパターンマッチングしてあげないといけないですね。

後片付け

Rxにおける後片付けは、DisposeBagオブジェクトにaddDisposableTo()することです。それにより、Completedが届いたあとに実行中に確保していたリソースは解放され、一連のストリームが終了します。

DisposeBag()DisposeBag.swiftファイルで定義されています。各Observable(正確にはDisposableに従うオブジェクト)のextensionもここで実装してあり、addDisposable(bag)メソッドを呼ぶことで、bagが持つ配列に格納するようになっています。

extension Disposable {
    public func addDisposableTo(bag: DisposeBag) {
        bag.addDisposable(self)
    }
}

DisposeBagを呼んでみたところ、面白いことに生きている間は何もしないことがわかりました。デイニシャライザで仕事をしていて、これまでaddしてきたDisposableなオブジェクトを配列から取り除き、そのdispose()メソッドを呼ぶようになっていました。

次にしたいこと

今回は、ここまでです。ArrayやMapをObservableにする方法と、それをsubscribeする方法、そして、最後にDispoaseBag()にて後片付けされることがわかりました。

次回の如法会に参加する場合、flatMapやmapについて知見を深められたらと思います。

以上です。

余談:書籍のお知らせ

最後に、知人の書いた本をちょっとだけですが宣伝です。

以前、「上を目指すプログラマーのためのiPhoneアプリ開発テクニック iOS 7編」などで本を一緒に書いた西方さんが、新しくCore Data+Swiftをテーマにした本「Swift+Core DataによるiOSアプリプログラミング」を出しました。

献本いただいたので私も読んでみたのですが、Core Dataの基本からアプリに組み込む時の実践的な内容までが書かれた、良書になっています。

お近くの本屋で見かけたら、ぜひ手にとってみてはいかがでしょうか。払った値段を超える知識を得られると思います。私もずっと手元に置いておき、調べるのに使っています。

44
42
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
42