Search code examples
iosswiftcombine

Swift Combine - Emit the latest value once per second


I have a PassthroughSubject which is connected to a ScrollView and it emits while it scrolls. I want the subject to emit the current scroll value, but only once per second. I tried throttle and debounce, but they don't seem to be doing what I need.

Like this, I can see every time it emits while I'm scrolling, so my base setup of scroll detection is working well.

scrollSubject
    .sink { value in
        print(value)
    }
    .store(in: &subscription)

But when I try to use either of these:

.throttle(for: 1, scheduler: RunLoop.main, latest: false) (tried latest: true also)`

.debounce(for: 1, scheduler: RunLoop.main)

What happens is they are not emitting while I'm scrolling, only after I've stopped it emits the latest value. How is it possible to achieve the desire behaviour?


Solution

  • What you are describing sounds like the sample operator available in other reactive programming libraries. It can be implemented in this way like @Alexander is describing:

    extension Publisher {
      func sample(
        every interval: TimeInterval,
        on runLoop: RunLoop,
        in mode: RunLoop.Mode
      ) -> AnyPublisher<Output, Failure> {
        let timer = Timer.publish(every: interval, on: runLoop, in: mode)
          .autoconnect()
          .mapError { $0 as! Failure }
    
        return combineLatest(timer)
          .map(\.0)
          .eraseToAnyPublisher()
      }
    }