swiftdelaycombineretry-logicpublisher

Retry with delay in Swift Combine?


Tried these "solutions":

Combine framework retry after delay?

https://www.donnywals.com/retrying-a-network-request-with-a-delay-in-combine/

But all of them doesn't work:

let retryCount = 3
let delay = 5

func requestOneTime() -> Future<(), Error> { ... }

requestOneTime()
    .eraseToAnyPublisher()
    .retryWithDelay(retries: retryCount, delay: delay, scheduler: DispatchQueue.global())

The result is always the same:

Instance method 'delay(for:tolerance:scheduler:options:)' requires that 'DispatchObject' conform to 'Scheduler'

or

Instance method 'retryWithDelay(retries:delay:scheduler:)' requires that 'DispatchObject' conform to 'Scheduler'

I even tried to retry URLSession.shared.dataTaskPublisher with delay - the result is the same too. Why nothing works? Is caused by a minimum supported version in the project (iOS 13)?

UPDATED

Figured out that this code doesn't work:

.retryWithDelay(retries: retryCount, delay: delay, scheduler: DispatchQueue.global())

but this code works:

.retryWithDelay(retries: retryCount, delay: 5, scheduler: DispatchQueue.global())

but if it is a typical type mismatch then how to declare delay variable of type S.SchedulerTimeType.Stride correctly?


Solution

  • If you are using DispatchQueue.global() as the scheduler, then S is DispatchQueue, and so S.SchedulerTimeType.Stride is DispatchQueue.SchedulerTimeType.Stride. This is a struct declared in DispatchQueue.SchedulerTimeType, not Int.

    Incidentally, DispatchQueue.SchedulerTimeType.Stride conforms to ExpressibleByIntegerLiteral. That's why passing 5 directly works, since 5 is an integer literal.

    If you want to declare a named constant for the delay, explicitly statethe type DispatchQueue.SchedulerTimeType.Stride.

    let delay: DispatchQueue.SchedulerTimeType.Stride = 5
    

    Alternatively,

    let delay: DispatchQueue.SchedulerTimeType.Stride = .seconds(5)
    

    or keep delay as an Int, and pass .seconds(delay) instead.