`take`してもcompletedさせない

  • 3
    いいね
  • 2
    コメント

ちょっとした小ネタです。(カレンダー埋めさせてください...!)

takeを使ってイベント送信を絞る

takeを使って、イベント送信を指定した回数だけ絞ることができるのですが、
takeで指定した回数分イベントが流れると、completedが流れます

let subject = PublishSubject<Int>()

subject.take(1)
    .subscribe { print($0) }
    .addDisposableTo(disposeBag)

subject.onNext(1)
subject.onNext(2)
subject.onNext(3)
output
next(1)
completed

ちょっとした問題

ただ、場合によっては、 completed が流れることで他のストリームに影響がでてしまうこともあります。

let subject = PublishSubject<Int>()

subject.take(1)
    .bindTo(otherSubject)
    .addDisposableTo(disposeBag)
// subjectでイベントが1回送信されたあと、`completed`してしまうことで
// bindした別のストリームに影響がでることもある。

なので、takeで絞りつつも、completedが流れないようにしてみます。

concat(merge)neverを活用する

takeで絞ったストリームと、neverconcatで1つのストリームに合成します。
そうすることで、takeで絞ったイベントが実行された後は、neverがあることでcompletedせずにすみます

Observable.of(subject.take(1), Observable.never())
    .concat()
    .subscribe { print($0) }
    .addDisposableTo(disposeBag)

subject.onNext(1)
subject.onNext(2)
subject.onNext(3)
output
next(1)
// completedにならない!!

余談(別解)

take(1)の代わりに、singleを使っても同じことができます。
ただ、singleの場合は、2回目以降イベントが送信されるとerrorが流れてくるので、それを握りつぶしてあげる必要があります。

Observable.of(subject.single().catchError { _ in Observable.never() }, Observable.never())
    .concat()
    .subscribe { print($0) }
    .addDisposableTo(disposeBag)

ちょっとした例

以前にこちらの記事を書いたのですが、この例だと

Observable.of(rx.viewWillAppear.take(1), Observable.never())
    .concat()
    .subscribe(onNext: {
        print("viewWillAppearが呼ばれた!")
    })
    .addDisposableTo(disposeBag)

となります。