[iOS] RxSwiftのPlaygroundのサンプルの出力

2019/02/2

こんにちは。きんくまです。

RxSwiftを勉強中です。それで、いろいろと調べていたのですが、イマイチ全体像が見えてこない!
でこれは何なのか?と考えたら、一番の基本となるものが何なのかわからないことに気がつきました。

そんなとき、公式のリポジトリにあるPlaygroundを写経してみたところ、ベースとなるものがわかった気がしました。

で、もう少しいろいろと調べないといけないのですが、毎回「これって何するやつ?」というのを調べるのが面倒くさいです。

なので、このページにPlaygroundに乗っていたやつを1ページに全て書いてしまい、Cmd + f で検索できたら出力がすぐに確認できて便利だなーと思った、メモ記事になります。

コードは基本的にPlaygroundのままなのですが、公式のやつは絵文字ばっかりつかってて、見づらいので普通のアルファベットとかに直しています。

バージョン

    pod 'RxSwift',    '~> 4.4.0'
    pod 'RxCocoa',    '~> 4.4.0'

Creating and Subscribing to Observables

//以下のimportはこのクラスだけ載せていますが、以降は省略します
import UIKit
import RxSwift
import RxCocoa

class Playground_SubscribingToObservables {
    
    var disposeBag = DisposeBag()
    
    public init(){
        
    }
    
    func neverSample(){
        print("never start === ")
        let neverSequence = Observable<String>.never()
        
        neverSequence.subscribe { _ in
            print("never")
        }.disposed(by: disposeBag)
        
        /*
         何もイベントを発行しない
         */
    }
    
    func emptySample(){
        print("empty start === ")
        Observable<Int>.empty()
            .subscribe{ event in
                print("event \(event)")
            }.disposed(by: disposeBag)
        
        /*
         completedだけemit
         
         empty start ===
         event completed
         */
    }
    
    func just(){
        print("just start ===")
        Observable<String>.just("Hello world")
            .subscribe{ event in
                print("subsc --")
                print("event \(event.event)")
                print("elem \(String(describing: event.element))")
            }.disposed(by: disposeBag)
        
        /*
         1個のelementからObservableを作成
         
         just start ===
         subsc --
         event next(Hello world)
         elem Optional("Hello world")
         subsc --
         event completed
         elem nil
         
         */
    }
    
    func ofSample(){
        print("of start ===")
        Observable<String>.of("a", "b", "c")
            .subscribe(onNext:{ event in
                print("onNext: \(event)")
            }, onCompleted: {
                print("onCompleted")
            }).disposed(by: disposeBag)
        
        /*
         引数のelementsからObservableを作成
         
         of start ===
         onNext: a
         onNext: b
         onNext: c
         onCompleted
         */
    }
    
    func fromSample(){
        print("from start ===")
        Observable<String>.from(["a", "b", "c"])
            .subscribe(onNext: { event in
                print("on next: \(event)")
            }).disposed(by: disposeBag)
        
        /*
         配列からObservableを作成
         
         from start ===
         on next: a
         on next: b
         on next: c
         */
    }
    
    func createSample(){
        print("create start ===")
        let myjust = { (elem:String) -> Observable<String> in
            return Observable.create { observer in
                observer.onNext(elem)
                observer.onCompleted()
                return Disposables.create()
            }
        }
        
        myjust("hello world").subscribe { print($0) }
            .disposed(by: disposeBag)
        
        /*
         独自のObservableを作成
         
         create start ===
         next(hello world)
         completed
         */
    }
    
    func range(){
        print("start range ===")
        //2から4まで3つnextする
        Observable.range(start: 2, count: 3)
            .subscribe{ print($0)}
            .disposed(by: disposeBag)
        
        /*
         startからcount分のelementをemit
         
         start range ===
         next(2)
         next(3)
         next(4)
         completed
         */
    }
    
    func repeatElement(){
        print("start repeat elem ===")
        //5回 k が next
        Observable.repeatElement("k")
            .take(5)
            .subscribe { print($0) }
            .disposed(by: disposeBag )
        
        /*
         引数回elementをemit
         
         start repeat elem ===
         next(k)
         next(k)
         next(k)
         next(k)
         next(k)
         completed
         */
    }
    
    func generate(){
        print("generate start ===")
        
        Observable.generate(
                initialState: 5,
                condition: { $0 < 15 },
                iterate: { $0 + 3 }
            ).subscribe { print($0) }
            .disposed(by: disposeBag)
        
        /*
         複雑な条件のObservableを生成
         
         generate start ===
         next(5)
         next(8)
         next(11)
         next(14)
         completed
         */
    }
    
    func deferredSample(){
        print("deferred start ===")
        
        var count = 1
        
        let mySeqence =  Observable<String>.deferred {
            print("Creating \(count)")
            count += 1
            
            return Observable.create { observer in
                print("Emitting...")
                observer.onNext("foo")
                observer.onNext("bar")
                observer.onNext("baz")
                return Disposables.create()
            }
        }
        
        mySeqence
            .subscribe(onNext: { print($0) } )
            .disposed(by: disposeBag)
        
        mySeqence
            .subscribe(onNext: { print($0) } )
            .disposed(by: disposeBag)
        
        /*
         subscribeされるまで次のObservableを作らない。
         subscribeされたら次のやつを作る
         
         deferred start ===
         Creating 1
         Emitting...
         foo
         bar
         baz
         Creating 2
         Emitting...
         foo
         bar
         baz
         */
    }
    
    func errorSample(){
        print("error ===")
        
        let info:[String:Any] =  ["error_message":"dayo"]
        //Observable<String> の <String> を書かないとエラー
        Observable<String>.error(NSError(domain: "myerror domain",
                                 code: 150, userInfo:info))
            .subscribe(onError:{ (error) in
                let error2 = error as NSError
                print(error2)
            }).disposed(by: disposeBag)
        
        /*
         itemはemitしなけれど、すぐにerrorを発行
         
         error ===
         Error Domain=myerror domain Code=150 "(null)" UserInfo={error_message=dayo}
         */
    }
    
    func doOnSample(){
        print("do on ===")
        
        Observable<String>.from(["dog", "cat", "baz"])
            .do(onNext: { print("doOnNext:", $0) },
                onCompleted: { print("doOnCompleted") })
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        /*
         emitするたびに、実行
         
         do on ===
         doOnNext: dog
         dog
         doOnNext: cat
         cat
         doOnNext: baz
         baz
         doOnCompleted
         */
    }
}

Working with Subjects

class Playground_subjects {
    public init(){}
    let disposeBag = DisposeBag()
    
    func publishSubject(){
        print("PublishSubject ===")
        let subject = PublishSubject<String>()
        subject
            .subscribe(onNext: { print("id1 -> ", "onNext:", $0 )})
            .disposed(by: disposeBag)
        
        subject.onNext("foo")
        subject.onNext("bar")
        
        subject
            .subscribe(onNext: { print("id2 -> ", "onNext:", $0) })
            .disposed(by: disposeBag)
        
        subject.onNext("foo2")
        subject.onNext("bar2")
        
        /*
         subscribeしたときは何もeventを出さない
         次のnextから受け取る
         
         PublishSubject ===
         id1 ->  onNext: foo
         id1 ->  onNext: bar
         id1 ->  onNext: foo2
         id2 ->  onNext: foo2
         id1 ->  onNext: bar2
         id2 ->  onNext: bar2
         */
    }
    
    func replaySubject() {
        print("ReplaySubject ===")
        let subject = ReplaySubject<String>.create(bufferSize: 1)
        
        subject
            .subscribe(onNext: { print("id1 -> ", "onNext:", $0 )})
            .disposed(by: disposeBag)
        
        subject.onNext("foo")
        subject.onNext("bar")
        
        subject
            .subscribe(onNext: { print("id2 -> ", "onNext:", $0) })
            .disposed(by: disposeBag)
        
        subject.onNext("foo2")
        subject.onNext("bar2")
        
        /*
         初期化時のbufferSizeだけ subscribe したときに event が出る
         ReplaySubject ===
         id1 ->  onNext: foo
         id1 ->  onNext: bar
         id2 ->  onNext: bar
         id1 ->  onNext: foo2
         id2 ->  onNext: foo2
         id1 ->  onNext: bar2
         id2 ->  onNext: bar2
         */
    }
    
    func behaviorSubject(){
        print("BehaviorSubject === ")
        let subject = BehaviorSubject(value: "behavior initial value!!")
        
        subject
            .subscribe(onNext: { print("id1 -> ", "onNext:", $0 )})
            .disposed(by: disposeBag)
        
        subject.onNext("foo")
        subject.onNext("bar")
        
        subject
            .subscribe(onNext: { print("id2 -> ", "onNext:", $0) })
            .disposed(by: disposeBag)
        
        subject.onNext("foo2")
        subject.onNext("bar2")
        
        subject
            .subscribe(onNext: { print("id3 -> ", "onNext:", $0 )})
            .disposed(by: disposeBag)
        
        subject.onNext("foo3")
        subject.onNext("bar3")
        
        /*
         subscribeした時点で最新のeventを受け取る
         
         BehaviorSubject ===
         id1 ->  onNext: behavior initial value!!
         id1 ->  onNext: foo
         id1 ->  onNext: bar
         id2 ->  onNext: bar
         id1 ->  onNext: foo2
         id2 ->  onNext: foo2
         id1 ->  onNext: bar2
         id2 ->  onNext: bar2
         id3 ->  onNext: bar2
         id1 ->  onNext: foo3
         id2 ->  onNext: foo3
         id3 ->  onNext: foo3
         id1 ->  onNext: bar3
         id2 ->  onNext: bar3
         id3 ->  onNext: bar3
         */
    }
}

Combining Operators

class Playground_Operators {
    let disposeBag = DisposeBag()
    
    func startWith() {
        print("start with === ")
        Observable.of("foo", "bar", "baz")
            .startWith("1")
            .startWith("2")
            .startWith("3", "3-2", "3-3")
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        /*
         startWithのものの方が先にnextされる。
         startWithは最初に書いたものの方が最後に出力される
         
         start with ===
         3
         3-2
         3-3
         2
         1
         foo
         bar
         baz
         */
    }
    
    func merge(){
        print("start merge ===")
        
        let subject1 = PublishSubject<String>()
        let subject2 = PublishSubject<String>()
        
        Observable.of(subject1, subject2)
            .merge()
            .subscribe(onNext: { print("merged next:", $0)})
            .disposed(by: disposeBag)
        
//        subject1.subscribe(onNext: { print("subject1 next:", $0)})
//            .disposed(by: disposeBag)
//
//        subject2.subscribe(onNext: { print("subject2 next:", $0)})
//            .disposed(by: disposeBag)
        
        subject1.onNext("foo")
        subject1.onNext("bar")
        subject2.onNext("foo2")
        subject2.onNext("bar2")
        subject1.onNext("foo3")
        subject2.onNext("bar3")
        
        /*
         mergeされたものが1つのタイムラインにのっかって出てくる
         
         start merge ===
         merged next: foo
         merged next: bar
         merged next: foo2
         merged next: bar2
         merged next: foo3
         merged next: bar3
        */
    }
    
    func combineLatest(){
        print("combine latest === ")
        let strSubject = PublishSubject<String>()
        let intSubject = PublishSubject<Int>()
        
        Observable.combineLatest(strSubject, intSubject){ strElem, intElem in
            "combine: \(strElem), \(intElem)"
            }
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        strSubject.onNext("foo")
        strSubject.onNext("bar")
        intSubject.onNext(1)
        intSubject.onNext(2)
        strSubject.onNext("baz")
        
        /*
         個別にeventが出るのではなくて、同時に合わさったeventを発行する
         各Observableが少なくとも1回はEventを発行しないと開始しない.
         開始するときは、最新のものが使われる
         combine latest ===
         combine: bar, 1
         combine: bar, 2
         combine: baz, 2
         */
    }
    
    func combineLatest2() {
        print("combineLatest2 === ")
        
        let ken = Observable.just("Ken")
        let arr = Observable.from(["foo", "bar", "baz"])
        let num = Observable.of("1", "2", "3", "4")
        
        Observable.combineLatest([ken, arr, num]) {
            "combined: \($0[0]), \($0[1]), \($0[2]))"
        }.subscribe({ print($0) })
            .disposed(by: disposeBag)
        
        /*
         かなり特殊な例なんじゃないかな?使うことあるか?
         combineLatest2 ===
         next(combined: Ken, foo, 1))
         next(combined: Ken, bar, 1))
         next(combined: Ken, bar, 2))
         next(combined: Ken, baz, 2))
         next(combined: Ken, baz, 3))
         next(combined: Ken, baz, 4))
         completed
        */
    }
    
    func switchLatest() {
        print("switchLatest === ")
        
        let subject1 = BehaviorSubject(value: "foo")
        let subject2 = BehaviorSubject(value: "bar")
        
        let subjectsSubject = BehaviorSubject(value: subject1)
        
        subjectsSubject.asObservable()
            .switchLatest()
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        subject1.onNext("Mike")
        subject1.onNext("Paul")
        
        subjectsSubject.onNext(subject2)
        
        subject1.onNext("Nancy")
        subject2.onNext("Cathy")
        
        subjectsSubject.onNext(subject1) //※1
        
        /*
         event発行のものを途中で切り替えている
         切り替え後はeventを発行しない。
         しかし、また※1 で戻すとeventが流れる
         
         switchLatest ===
         foo
         Mike
         Paul
         bar
         Cathy
         Nancy
         */
    }
}

Transforming Operators

class Playground_TransformingOp {
    
    let disposeBag = DisposeBag()
    
    public init(){}
    
    func map() {
        print("map ===")
        Observable.of(1, 2, 3)
            .map { $0 * $0 }
            .subscribe(onNext: { print($0)})
            .disposed(by: disposeBag)
        
        /*
         map ===
         1
         4
         9
         */
    }
    
    struct Player {
        let score: BehaviorSubject<Int>
        init(score: Int) {
            self.score = BehaviorSubject(value: score)
        }
    }
    
    func flatMap() {
        print("flatmap and flatmapLatest === ")
        
        let tom = Player(score: 80)
        let mike = Player(score: 90)
        
        let player = BehaviorSubject(value: tom)
        
        player
            // asObservableしなくてもSubjectはそのままsubscribeできた。
            // これは以前のバージョンだとできなかったのかも
            //.asObservable()
            //.flatMap{ $0.score.asObservable() }
            
            .flatMap{ $0.score }
            //.flatMapLatest{ $0.score }
            
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        print("tom next")
        tom.score.onNext(85)
        
        print("change player to mike")
        player.onNext(mike)
        
        print("tom next")
        tom.score.onNext(95)
        
        print("mike next")
        mike.score.onNext(100)
        
        print("tom next")
        tom.score.onNext(110)
        
        print("change player to tom")
        player.onNext(tom)
        
        /*
         //playerがmikeを指した後もtomのeventが受け取られている
         flatmap ===
         80
         tom next
         85
         change player to mike
         90
         tom next
         95
         mike next
         100
         tom next
         110
         change player to tom
         110
         
         //tomの値を変えても、playerがmikeを指した後はeventを受け取らない
         //95の値が出ていない
         flatmapLatest ===
         80
         tom next
         85
         change player to mike
         90
         tom next
         mike next
         100
         tom next //ここでは出てない
         change player to tom //ここで変えたから出る
         110
         */
    }
    
    func scan() {
        print("scan ===")
        
        Observable.of(1, 3, 7, 15)
            .scan(4) { aggregateValue, newValue in
                print("scanned:", aggregateValue, newValue)
                return aggregateValue + newValue
            }
            .subscribe(onNext: { print($0) })
            .disposed(by: disposeBag)
        
        /*
         scan(1) のとき ===
         scanned: 1 1
         2
         scanned: 2 3
         5
         scanned: 5 7
         12
         scanned: 12 15
         27
         
         scan(4) のとき ===
         scanned: 4 1
         5
         scanned: 5 3
         8
         scanned: 8 7
         15
         scanned: 15 15
         30
         */
    }
}

Filtering and Conditional Operators

class Playground_FilteringConditionalOperators {
    
    let bag = DisposeBag()
    
    func filter() {
        print("filter ===")
        Observable.of(
            "foo", "bar", "baz",
            "bar2", "foo2", "baz2",
            "foo3", "baz3", "bar3"
            ).filter{ $0.contains("foo") }
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         foo
         foo2
         foo3
         */
    }
    
    func distinctUntilChanged() {
        print("distinctUntilChanged ===")
        Observable.of("foo", "bar", "bar", "foo", "foo", "foo", "baz", "foo")
        .distinctUntilChanged()
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         同じ値が続いたときは、最初のもの以外は無視。
         新しいものが出たら、nextで出す
         foo
         bar
         foo
         baz
         foo
         */
    }
    
    func elementAt() {
        print("elementAt ===")
        Observable.of("1", "2", "3", "4")
            .elementAt(2)
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         indexで返す
         elementAt ===
         3
         */
    }
    
    func single1() {
        print("single1 ===")
        Observable.of("a", "b", "c")
            .single()
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        /*
         最初のelementをemit
         single1 ===
         a
         */
    }
    
    func single2_4() {
        print("single2 ===")
        
        Observable.of("foo", "bar", "baz", "alpha", "beta", "gamma")
            .single { $0 == "alpha" }
            .subscribe { print($0) }
            .disposed(by: bag)
        
        /*
         正常終了
         
         single2 ===
         next(alpha)
         completed
         */
        
        print("single3 ===")
        
        Observable.of("foo", "bar", "baz", "foo", "bar", "baz")
            .single { $0 == "bar" }
            .subscribe { print($0) }
            .disposed(by: bag)
        
        /*
         エラー。該当条件が1つより多くマッチしているため
         
         single3 ===
         next(bar)
         error(Sequence contains more than one element.)
         */
        
        print("single4 ===")
        
        /*
         エラー。該当条件のelmentが見つからない
         
         single4 ===
         error(Sequence doesn't contain any elements.)
         */
        
        Observable.of("foo", "bar", "baz", "alpha", "beta", "gamma")
            .single { $0 == "delta" }
            .subscribe { print($0) }
            .disposed(by: bag)
    }
    
    func take() {
        print("take === ")
        Observable.of("a", "b", "c", "d", "e")
            .take(3)
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         最初からのn個を取る
         take ===
         a
         b
         c
         */
    }
    
    func takeLast() {
        print("takeLast ===")
        Observable.of("a", "b", "c", "d", "e")
            .takeLast(3)
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         後ろからn個を取る
         takeLast ===
         c
         d
         e
         */
    }
    
    func takeWhile() {
        print("takeWhile ===")
        Observable.of(1, 2, 3, 4, 5, 6)
            .takeWhile { $0 < 4 }
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         条件を満たしているものだけemit
         takeWhile ===
         1
         2
         3
         */
    }
    
    func takeUntil() {
        print("takeUntil ===")
        
        let sourceSequence = PublishSubject<String>()
        let referenceSequence = PublishSubject<String>()
        
        sourceSequence
            .takeUntil(referenceSequence)
            .subscribe { print($0) }
            .disposed(by: bag)
        
        sourceSequence.onNext("a")
        sourceSequence.onNext("b")
        sourceSequence.onNext("c")
        
        referenceSequence.onNext("d")
        
        sourceSequence.onNext("e")
        sourceSequence.onNext("f")
        sourceSequence.onNext("g")
        
        /*
         takeUntilの引数がemitするまでemitし続ける。
         引数がemitすると止まる(completed)
         
         takeUntil ===
         next(a)
         next(b)
         next(c)
         completed
         */
    }
    
    func skip() {
        print("skip ===")
        
        Observable.of("a", "b", "c", "d", "e", "f")
            .skip(2)
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         skipで指定した個数分だけとばしてからemit開始
         
         skip ===
         c
         d
         e
         f
         */
    }
    
    func skipWhile() {
        print("skipWhile ===")
        
        Observable.of(1, 2, 3, 4, 5, 6)
            .skipWhile { $0 < 4 }
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         該当条件まではemitしないが、該当条件の境界はemitする
         
         skipWhile ===
         4
         5
         6
         */
    }
    
    //skipWhileWithIndexはdeprecated!
//    func skipWhileWithIndex() {
//        print("skipWhileWithIndex ===")
//
//        Observable.of("a", "b", "c", "d", "e")
//            .skipWhileWithIndex()
//    }
    
    func skipUntil() {
        print("skipUntil ===")
        let sourceSequence = PublishSubject<String>()
        let referenceSequence = PublishSubject<String>()
        
        sourceSequence
            .skipUntil(referenceSequence)
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        sourceSequence.onNext("a")
        sourceSequence.onNext("b")
        sourceSequence.onNext("c")
        
        referenceSequence.onNext("d")
        
        sourceSequence.onNext("e")
        sourceSequence.onNext("f")
        sourceSequence.onNext("g")
        
        /*
         skipUntilの引数がemitするまでは待機。emitしたら流す
         
         skipUntil ===
         e
         f
         g
         */
    }
}

Mathematical and Aggregate Operators

class Playground_Math_AggregateOps {
    
    let bag = DisposeBag()
    
    func toArray() {
        print("toArray ===")
        
        Observable.range(start: 1, count: 10)
            .toArray()
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         sequenceをひとつの配列にまとめてemit
         
         toArray ===
         [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
         */
    }
    
    func reduce() {
        print("reduce === ")
        
        Observable.of(20, 300, 4000)
            .reduce(5, accumulator: +)
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         初期値とaccumulatorを使ってひとつの値に計算してからemit
         
         reduce ===
         4325
         */
        
        Observable.of(2, 3)
            .reduce(5, accumulator: *)
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         30
         */
    }
    
    func concat() {
        print("concat ===")
        
        let subject1 = BehaviorSubject(value: "1-a")
        let subject2 = BehaviorSubject(value: "1-b")
        
        let subjectsSubject = BehaviorSubject(value: subject1)
        
        subjectsSubject
            .concat()
            .subscribe { print($0) }
            .disposed(by: bag)
        
        subject1.onNext("1-c")
        subject1.onNext("1-d")
        
        subjectsSubject.onNext(subject2)
        
        subject2.onNext("2-I would be ignored")
        subject2.onNext("2-e")
        subject1.onCompleted()
        subject2.onNext("2-f")
        
        /*
         最初に指定したものがcompleteするまで待ってから次のものをemit
         
         concat ===
         next(1-a)
         next(1-c)
         next(1-d)
         next(2-e)
         next(2-f)
         */
    }
}

Connectable Operators


func delay(_ delay: Double, closure: @escaping () -> Void) -> DispatchSourceTimer {
    let timer = DispatchSource.makeTimerSource()
    timer.schedule(deadline: .now() + delay)
    timer.setEventHandler {
        closure()
    }
    //resume忘れるとエラーが出る!!
    timer.resume()
    return timer
}

class Playground_ConnectableOps {
    
    let bag = DisposeBag()
    var timer:DispatchSourceTimer?
    var timer2:DispatchSourceTimer?
    var timer3:DispatchSourceTimer?
    
    func withoutOperators() {
        print("withoutOperators ===")
        
        let interval = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        
        _ = interval
            .subscribe(onNext: { print("Subscription: 1, Event: \($0)") })

        timer = delay(3) {
            _ = interval
                .subscribe(onNext: { print("Subscription: 2, Event: \($0)") })
        }
        
        /*
         withoutOperators ===
         Subscription: 1, Event: 0
         Subscription: 1, Event: 1
         Subscription: 1, Event: 2
         Subscription: 1, Event: 3
         Subscription: 2, Event: 0
         Subscription: 1, Event: 4
         Subscription: 2, Event: 1
         Subscription: 1, Event: 5
         Subscription: 2, Event: 2
         Subscription: 1, Event: 6
         Subscription: 2, Event: 3
         */
    }
    
    func samplePublish() {
        print("pubish ===")
        
        let intSequence = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
            .publish()
        
        _ = intSequence
            .subscribe(onNext: { print("subscription 1:, Event: \($0)")})
        
       
        timer2 = delay(2) {
            _ = intSequence
                .subscribe(onNext: { print("Subscription 2:, Event \($0)") })
        }
        timer = delay(4) { _ = intSequence.connect() }
        timer3 = delay(6) {
            _ = intSequence
                .subscribe(onNext: { print("Subscription 3:, Event \($0)")})
        }
        
        /*
         4秒後にconnect() されてからemit開始
         
         pubish ===
         subscription 1:, Event: 0
         Subscription 2:, Event 0
         subscription 1:, Event: 1
         Subscription 2:, Event 1
         Subscription 3:, Event 1
         subscription 1:, Event: 2
         Subscription 2:, Event 2
         Subscription 3:, Event 2
         subscription 1:, Event: 3
         Subscription 2:, Event 3
         Subscription 3:, Event 3
         subscription 1:, Event: 4
         Subscription 2:, Event 4
         Subscription 3:, Event 4
         */
    }
    
    func replay() {
        print("replay ===")
        
        let intSequence = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
            .replay(1)
        
        _ = intSequence
            .subscribe(onNext: { print("subscription 1:, Event: \($0)")})
        
        timer = delay(2) { _ = intSequence.connect() }
        timer2 = delay(4) {
            _ = intSequence
                .subscribe(onNext: { print("Subscription 2:, Event \($0)") })
        }
        
        timer3 = delay(6) {
            _ = intSequence
                .subscribe(onNext: { print("Subscription 3:, Event \($0)")})
        }
        
        /*
         replay ===
         subscription 1:, Event: 0
         Subscription 2:, Event 0
         subscription 1:, Event: 1
         Subscription 2:, Event 1
         subscription 1:, Event: 2
         Subscription 2:, Event 2
         Subscription 3:, Event 2
         subscription 1:, Event: 3
         Subscription 2:, Event 3
         Subscription 3:, Event 3
         */
    }
}

Error Handling Operators

enum TestError: Error {
    case test
}

class Playground_ErrorHandling {
    let bag = DisposeBag()
    
    func catchErrorJustReturn() {
        print("catchErrorJustReturn === ")
        
        let sequence = PublishSubject<String>()
        
        sequence
            .catchErrorJustReturn("a")
            .subscribe { print($0) }
            .disposed(by: bag)
        
        sequence.onNext("b")
        sequence.onNext("c")
        sequence.onNext("d")
        sequence.onError(TestError.test)
        
        /*
         errorになったら catchErrorJustReturn の引数をemitして終了
         
         catchErrorJustReturn ===
         next(b)
         next(c)
         next(d)
         next(a)
         completed
         */
    }
    
    func catchError() {
        print("catchError ===")
        
        let sequence = PublishSubject<String>()
        let recoverySequence = PublishSubject<String>()
        
        sequence
            .catchError {
                print("Error;", $0)
                return recoverySequence
            }
            .subscribe { print($0) }
            .disposed(by: bag)
        
        sequence.onNext("a")
        sequence.onNext("b")
        sequence.onNext("c")
        sequence.onError(TestError.test)
        
        recoverySequence.onNext("recovery")
        
        /*
         errorになったらhandlerを実行する。もしreturnしたら
         そのsequenceをemitする
         
         catchError ===
         next(a)
         next(b)
         next(c)
         Error; test
         next(recovery)
         */
    }
    
    func retry() {
        print("retry ===")
        
        var count = 1
        
        let sequence = Observable<String>.create { observer in
            observer.onNext("a")
            observer.onNext("b")
            observer.onNext("c")
            
            if count == 1 {
                observer.onError(TestError.test)
                print("Error encountered")
                count += 1
            }
            
            observer.onNext("d")
            observer.onNext("e")
            observer.onCompleted()
            return Disposables.create()
        }
        
        sequence
            .retry()
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         retry ===
         a
         b
         c
         Error encountered
         a
         b
         c
         d
         e
         */
    }
    
    func retry2(){
        print("retry2 ===")
        
        var count = 1
        
        let sequence = Observable<String>.create { observer in
            observer.onNext("a")
            observer.onNext("b")
            observer.onNext("c")
            
            if count < 3 {
                observer.onError(TestError.test)
                print("Error encountered. count: \(count)")
                count += 1
            }
            
            observer.onNext("d")
            observer.onNext("e")
            observer.onCompleted()
            return Disposables.create()
        }
        
        sequence
            .retry(4)
            .subscribe(onNext: { print($0) })
            .disposed(by: bag)
        
        /*
         retry2 ===
         a
         b
         c
         Error encountered. count: 1
         a
         b
         c
         Error encountered. count: 2
         a
         b
         c
         d
         e
         */
    }
}

Debugging Operators

class Playground_DebuggingOperators {
    let bag = DisposeBag()
    
    func debug() {
        print("debug ===")
        
        var count = 1
        
        let sequence = Observable<String>.create { observer in
            observer.onNext("a")
            observer.onNext("b")
            observer.onNext("c")
            
            if count < 5 {
                observer.onError(TestError.test)
                print("Error encountered")
                count += 1
            }
            
            observer.onNext("d")
            observer.onNext("e")
            observer.onCompleted()
            
            return Disposables.create()
        }
        
        sequence
            .retry(1)
            .debug()
            .subscribe { print($0) }
            .disposed(by: bag)

        /*
         debug ===
         2019-01-29 19:26:00.410: Playground_DebuggingOperators.swift:41 (debug()) -> subscribed
         2019-01-29 19:26:00.418: Playground_DebuggingOperators.swift:41 (debug()) -> Event next(a)
         next(a)
         2019-01-29 19:26:00.418: Playground_DebuggingOperators.swift:41 (debug()) -> Event next(b)
         next(b)
         2019-01-29 19:26:00.418: Playground_DebuggingOperators.swift:41 (debug()) -> Event next(c)
         next(c)
         Error encountered
         2019-01-29 19:26:00.420: Playground_DebuggingOperators.swift:41 (debug()) -> Event error(test)
         error(test)
         2019-01-29 19:26:00.421: Playground_DebuggingOperators.swift:41 (debug()) -> isDisposed
         */
    }
}

おまけ Void

Observable<Void>って、どうやって値をemitするのかと思ったら Void() ってやればよいみたい

let sequence = Observable<Void>.create{ observer in
    observer.onNext(Void())
    observer.onCompleted()
    return Disposables.create()
}
let bag = DisposeBag()
sequence.subscribe{ print($0) }.disposed(by: bag)

/*
 next(())
 completed
 */
LINEで送る
Pocket

自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

LINEスタンプ作りました!
毎日使える。とぼけたウサギ。LINEスタンプ販売中! 毎日使える。とぼけたウサギ

ページトップへ戻る