swiftcombine

How to trigger Timer.publish() right away?


I created a timer via combine which emits Date and ignores errors using this code:

let timer: AnyPublisher<Date, Never> = Timer.publish(every: 5, on: .main, in: RunLoop.Mode.common)
  .autoconnect()
  .map { _ in Date() }
  .replaceError(with: Date())
  .eraseToAnyPublisher()

(I'm sure there are better ways than mapping and replacing the error, but for this example, I wanted to keep the type simple, AnyPublisher<Date, Never>.)

The timer fires correctly, but there is a delay between when the timer is created to when it fires the first time (i.e. it waits 5 seconds). With NSTimer, we can invoke timer.fire() to force it to fire immediately.

Is there an equivalent way to force a timer to post immediately when using Timer.publish()?


Alternatively, is there a way to merge Just(Date()) with the above Timer.publish so that it fires immediately and every 5 seconds, while still keeping the AnyPublisher<Date, Never> type?


Solution

  • I'm sure there are better ways than mapping and replacing the error

    Timer.TimerPublisher.Failure is Never, so you don't need any mapping because it can't fail.

    Furthermore, Timer.TimerPublisher.Output is already the current Date(), so you don't need to map the output either.

    To emit the current date immediately on subscription, you want to combine a Deferred { Just(Date()) } with the timer publisher. The Deferred means the current date won't be computed until the subscription happens.

    Here is one way to put it all together:

    let timer = Deferred { Just(Date()) }
        .append(Timer.publish(every: 5, on: .main, in: .common).autoconnect())
        .eraseToAnyPublisher()