RxSwift Training


背景

  • チームでRxSwiftのオンボーディングをする機会ができた
  • オンボーディング用の資料

前提

  • Projectで使われているRxSwiftのコードが理解できることが目的で、RxSwiftの完璧な理解は目的としていない
  • Rx.playgroundを使う
  • 問題形式で進めます
  • playgroundの章ごとに問題があるので、事前に該当の章を理解してから取り組んでください🙇‍♂️

用意

Rx.playgroundをrunできるよに

  1. RxSwiftをクローン
  2. Rx.xcworkspaceを開く
  3. RxSwiftMy Mac でビルド
  4. RxExample > RxPlayground を開く

Onboarding

Introduction

(問1) Observableを変数に保持して、保持した変数をsubscribeする

_ = Observable<String>.create { observerOfString in
        print("Observable created")
        observerOfString.on(.next("😉"))
        observerOfString.on(.next("😉"))
        observerOfString.on(.completed)
        return Disposables.create()
    }
    .subscribe { event in
        print(event)
    }


Answer
let observable = Observable<String>.create { observerOfString in
    print("Observable created")
    observerOfString.on(.next("😉"))
    observerOfString.on(.next("😉"))
    observerOfString.on(.completed)
    return Disposables.create()
}

observable.subscribe { event) in
    print(event)
}


(問2) 下記のEventをObservableから流して、subscribeする

next(1)
next(2)
next(3)
completed


Answer
let observable = Observable<Int>.create { observer in
    observer.onNext(1)
    observer.onNext(2)
    observer.onNext(3)
    observer.onCompleted()
    return Disposables.create()
}

observable.subscribe { event in
    print(event)
}


(問3) 下記の実行結果はどうなるか?

_ = Observable<Int>.create { observer in
        observer.onNext(1)
        observer.onError(TestError.test)
        observer.onNext(3)
        observer.onCompleted()
        return Disposables.create()
    }
    .subscribe { event in
        print(event)
    }


Answer
next(1)
error(test)


Creating and Subscribing to Observables

(問4) 下記のEventが流れるObservableをObservable.createを使わずに生成して、subscribeする

next([1])
completed


Answer

Answer

Observable.just([1])
    .subscribe { event in
        print(event)
    }
    .disposed(by: disposeBag)


(問5) 下記のEventが流れるObservableを生成して、subscribeする

next(true)
next(false)
next(true)
error(test) // TestError.testを使用


Answer
Observable<Bool>.create { observer in
        observer.onNext(true)
        observer.onNext(false)
        observer.onNext(true)
        observer.onError(TestError.test)
        return Disposables.create()
    }
    .subscribe { event in
        print(event)
    }


Working with Subjects

(問6) 下記を実行したときの結果

let subject = BehaviorSubject(value: 1)
subject.onNext(2)
subject.onNext(3)
subject.onError(TestError.test)
subject.onNext(4)

subject.subscribe(onNext: { element in
        print(element)
    })
    .disposed(by: disposeBag)


Answer
error(test)


(問7) (問6)のコードを修正して下記の実行結果を得るには?

(BehaviorSubjectを使用、subscribe方法は同じ)

next(1)
next(2)
next(3)
error(test)


Answer
let subject = BehaviorSubject(value: 1)

subject.subscribe({ event in
        print(event)
    })
    .disposed(by: disposeBag)

subject.onNext(2)
subject.onNext(3)
subject.onError(TestError.test)


(問8) 下記の実行結果

class TestModel {
    let publishSubject = PublishSubject<Int>()

    private let behaviorSubject = BehaviorSubject<String>(value: "")
    var observable: Observable<String> {
        return behaviorSubject.asObservable()
    }

    private let disposeBag = DisposeBag()

    init() {
        publishSubject.subscribe(onNext: { [weak self] element in
                let str = element % 2 == 0 ? "A" : "B"
                self?.behaviorSubject.onNext(str)
            })
            .disposed(by: disposeBag)
    }
}

let model = TestModel()
model.observable
    .subscribe { print($0) }
    .disposed(by: disposeBag)

model.publishSubject.onNext(1)
model.publishSubject.onNext(2)
model.publishSubject.onNext(3)


Answer
next("")
next("B")
next("A")
next("B")


番外編 (RelayとTraits)

https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Subjects.md#relays
https://github.com/ReactiveX/RxSwift/blob/master/Documentation/Traits.md

Combination Operators

(問9) combineLatest内の ??? の箇所を埋める

let disposeBag = DisposeBag()

let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()

let observable = Observable.combineLatest(
     // ???
    )

 observable.subscribe(onNext: { element1, element2 in
         print("\(element1) \(element2)")
     })
     .disposed(by: disposeBag)

subject1.onNext("🅰️")
subject2.onNext("①")
subject1.onNext("🅱️")
subject2.onNext("②")

// ① ①
// B ①
// ② ①
// ② ②


Answer
let disposeBag = DisposeBag()

let subject1 = PublishSubject<String>()
let subject2 = PublishSubject<String>()

let observable = Observable.combineLatest(
    Observable.merge(subject1, subject2),
    subject2
    )

 observable.subscribe(onNext: { element1, element2 in
         print("\(element1) \(element2)")
     })
     .disposed(by: disposeBag)

subject1.onNext("A")
subject2.onNext("①")
subject1.onNext("B")
subject2.onNext("②")


Transforming Operators

(問10) (問8)のTestModelクラスをsubscribeとBehaviorSubjectを使わずに書き換える


Answer
class TestModel {
    let publishSubject = PublishSubject<Int>()

    let observable: Observable<String>

    private let disposeBag = DisposeBag()

    init() {
        observable =  publishSubject
            .map { $0 % 2 == 0 ? "A" : "B" }
            .startWith("")
    }
}


(問11) 下記のコードを1箇所のみを修正して、期待する結果を出力させる

Result

next(false)
next(true)
next(false)

Code

func isEven(num: Int) -> Observable<Bool> {
    let isEven = num % 2 == 0
    return Observable.just(isEven)
}

let subject = PublishSubject<Int>()

subject
    .map { isEven(num: $0) }
    .subscribe { event in
        print(event)
    }
    .disposed(by: disposeBag)

subject.onNext(1)
subject.onNext(2)
subject.onNext(3)


Answer

mapflatMap


Filtering and Conditional Operators

(問12) 下記のコードの??の箇所にオペレータを入れて、期待する結果を出力させる

Result

🐱
🐷
🐱

Code

Observable.of("🐱", "🐷", "🐱", "🐵", "🐵", "🐵", "🐱", "🐱")
    // . ??
    // . ??
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)


Answer
Observable.of("🐱", "🐷", "🐱", "🐵", "🐵", "🐵", "🐱", "🐱")
    .filter { $0 != "🐵" }
    .distinctUntilChanged()
    .subscribe(onNext: { print($0) })
    .disposed(by: disposeBag)
}


番外編 (Hot and Cold)

概要
https://github.com/ReactiveX/RxSwift/blob/master/Documentation/HotAndColdObservables.md

Example
https://github.com/ReactiveX/RxSwift/blob/master/RxExample/RxExample/Examples/SimpleValidation/SimpleValidationViewController.swift

Next Action

RxExample を触ろう