「最大でn回のイベントを発行する + Completeしない」Observableの作り方


RxSwiftを扱っていて、最大でn回のイベントを発行するCompleteしないObservableを使いたいときがある。例えば、API Call等の1回のみ行いたいタスクを行うために、下記の条件を満たしたObservableを扱いたい

  1. UIViewController.viewWillAppear(_:)に1回のみフックしてイベントを発行する
  2. Completeしない

1. UIViewController.viewWillAppear(_:)に1回のみフックしてイベントを発行する

1の条件を満たすために下記の2つの条件に分割する

1.1 UIViewController.viewWillAppear(_:)にフックするObservableを生成する
1.2 発行するイベントを1個のみにする

1.1 UIViewController.viewWillAppear(_:)にフックするObservableを生成する

extension Reactive where Base: UIViewController {
    var viewWillAppear: Observable<Void> {
        return sentMessage(#selector(UIViewController.viewWillAppear(_:))).map { _ in () }.shareReplay(1)
    }
}
viewController.rx.viewWillAppear
    .subscribe(...)
    .addDisposableTo(disposeBag)

上のように扱えるが、上の実装ではUIViewController.viewWillAppear(_:)が呼ばれる度にイベントが発行されてしまう

1.2 発行するイベントを1個のみにする

extension Reactive where Base: UIViewController {
    var viewWillAppear: Observable<Void> {
        return sentMessage(#selector(UIViewController.viewWillAppear(_:))).map { _ in () }.shareReplay(1)
    }

    var viewWillAppearOnce: Observable<Void> {
        return viewWillAppear.take(1)
    }
}

takeオペレータを使って、発行するイベントの回数を制御している

しかし、これだと下記のようにCompleteイベントも呼ばれてしまう

next()
complete

Subject等にbindingしているとCompleteイベントが流れてSubjectが終了してしまう。このようなケースは避けたい

2. Completeしない

extension Reactive where Base: UIViewController {
    var viewWillAppear: Observable<Void> {
        return sentMessage(#selector(UIViewController.viewWillAppear(_:))).map { _ in () }.shareReplay(1)
    }

    var viewWillAppearOnce: Observable<Void> {
        return Observable.concat ([viewWillAppear.take(1), Observable.never()])
    }
}

concatオペレータを用いて、CompleteしないObservable.never()とフィルタリングしているviewWillAppear.take(1)を結合して、下記の条件を満たしているObservableを実現している

  1. UIViewController.viewWillAppear(_:)に1回のみフックしてイベントを発行する
  2. Completeしない

参考文献