Skip to content

Instantly share code, notes, and snippets.

@IanKeen
Last active June 1, 2022 17:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IanKeen/952663cdb8f2ddfbee7c9cb396e5091f to your computer and use it in GitHub Desktop.
Save IanKeen/952663cdb8f2ddfbee7c9cb396e5091f to your computer and use it in GitHub Desktop.
Combine: RetryWhen
import Combine
import Foundation
public extension Publisher {
func retryWhen(max: Int = .max, delay: DispatchQueue.SchedulerTimeType.Stride = 0, _ predicate: @escaping Publishers.RetryWhen<Self>.Predicate) -> Publishers.RetryWhen<Self> {
.init(upstream: self, max: max, delay: delay, predicate: predicate)
}
}
extension Publishers {
public struct RetryWhen<Upstream: Publisher>: Publisher {
public typealias Output = Upstream.Output
public typealias Failure = Upstream.Failure
public typealias Predicate = (Result<Output, Failure>) -> Bool
private let upstream: Upstream
private let delay: DispatchQueue.SchedulerTimeType.Stride
private let max: Int
private let predicate: Predicate
init(upstream: Upstream, max: Int, delay: DispatchQueue.SchedulerTimeType.Stride, predicate: @escaping Predicate) {
self.upstream = upstream
self.max = max
self.delay = delay
self.predicate = { max > 0 && predicate($0) }
}
public func receive<S: Subscriber>(subscriber: S) where Failure == S.Failure, Output == S.Input {
func retry() -> AnyPublisher<Output, Failure> {
Just(())
.delay(for: delay, scheduler: DispatchQueue.main).flatMap { _ in
Self(upstream: upstream, max: max - 1, delay: delay, predicate: predicate)
}
.eraseToAnyPublisher()
}
upstream
.catch { error in
predicate(.failure(error))
? retry()
: Fail(error: error).eraseToAnyPublisher()
}
.flatMap { value in
predicate(.success(value))
? retry()
: Just(value).setFailureType(to: Failure.self).eraseToAnyPublisher()
}
.receive(subscriber: subscriber)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment