Search code examples
iosswiftswiftuicombine

Combine - Delay publisher's send


What's the best way, to delay a moment when a publisher sends some data in Swift Combine? Let's assume following situation:

private var publisher: PassthroughSubject<Progress, Error>

// closure called every second:
startWithProgress() { [weak self] progress in
    self.publisher.send(.init(progress: progress))

    // How to call this 0.5 second after the above `send`:
    self.publisher.send(.init(progress: progress + 0.5))
}

I checked Delay API, but it seems I'd need to create another publisher to make use of it, which is suboptimal in my case. I also checked throttle and debounce, but those also don't allow me to send 2 updates one after another, with a given delay between them.


Solution

  • There's a difference between delaying the act of sending the values - which you can do with DispatchQueue.asyncAfter - and creating a combine pipeline that delays upstream values.

    You haven't specified any details about what you are actually trying to accomplish, so it's hard to give a definitive answer.

    If I was to generalize, it looks like that you want a pipeline that for each upstream value it emits the value, then emits the value + 0.5 again, but delayed. This could be done like below, as an example:

    let duplicateAndDelay = publisher
       .flatMap { [($0, 0), ($0 + 0.5, 0.5)].publisher } // duplicate
       .flatMap { (progress, delay) in 
          Just(progress)
             .delay(for: .seconds(delay), scheduler: RunLoop.main) // delay
       }
    

    Then you can just send once:

    startWithProgress() { [weak self] progress in
       self?.publisher.send(progress)
    }
    

    and return the duplicateAndDelay publisher, instead of the publisher publisher, to be subscribed to.