やりたいこと
RxSwiftで「x秒間なにもイベントがなかったらそれまでの値をまとめて受け取りたい」という状況はよくあると思います。
例えばダブルタップだと「0.25秒間なにもイベントがなかったとき、それまでのタップ数が2回だったらダブルタップである」という判定ができます。
他言語では
RxJSなどではこのような要件は buffer
と debounce
(throttle
) を組み合わせると簡単に実現できます。
buffer
の引数にどのタイミングでイベントをまとめるかの条件を渡せるので、debounce
を使って「x秒間なにもなかったら」という条件を指定できます 1。
clickStream.buffer(clickStream.debounce(250))
RxSwiftでは
現在 buffer
が条件を受け取れる作りになっていません。
なので自前でなにかしら以下のようなものを作ってやる必要があります 2。
extension ObservableType {
func debouncedBuffer(_ dueTime: RxTimeInterval, scheduler: SchedulerType) -> Observable<[E]> {
var buffer: [E] = []
return self.do(onNext: { value in
buffer.append(value)
}).debounce(dueTime, scheduler: scheduler).flatMap { value -> Observable<[E]> in
defer { buffer.removeAll() }
return Observable<[E]>.just(buffer)
}
}
}
そうするとこのように書けます。
button.rx.tap
.debouncedBuffer(0.25, scheduler: MainScheduler.instance)
.filter { $0.count == 2 }
.subscribe(onNext: { _ in
print("double tap")
})
.disposed(by: disposeBag)
参考
-
swift - Implementing a debounced buffer with RxSwift, is this correct? - Stack Overflow
コードはこのStackOverflowのページを参考にしました
buffer
にObservable
を渡せるようなサンプルコードも載っているので興味ある方は見てみてください。 ↩