Edited at
RxSwiftDay 20

`take`してもcompletedさせない

More than 1 year has passed since last update.

(追記) : こちらで詳しく書き直しています。

続・takeしてもcompletedにさせない

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


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)

となります。