Skip to content

Instantly share code, notes, and snippets.

@ElonPark
Forked from kean/AutoRetry.swift
Created October 16, 2020 05:50
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 ElonPark/b061f22c0680783b802be90c584269ff to your computer and use it in GitHub Desktop.
Save ElonPark/b061f22c0680783b802be90c584269ff to your computer and use it in GitHub Desktop.
Smart Auto Retry using RxSwift
// The MIT License (MIT)
//
// Copyright (c) 2017 Alexander Grebenyuk (github.com/kean).
import Foundation
import RxSwift
import RxCocoa
extension ObservableType {
/// Retries the source observable sequence on error using a provided retry
/// strategy.
/// - parameter maxAttemptCount: Maximum number of times to repeat the
/// sequence. `Int.max` by default.
/// - parameter didBecomeReachable: Trigger which is fired when network
/// connection becomes reachable.
/// - parameter shouldRetry: Always retruns `true` by default.
func retry(_ maxAttemptCount: Int = Int.max,
delay: DelayOptions,
didBecomeReachable: Signal<Void> = Reachability.shared.didBecomeReachable,
shouldRetry: @escaping (Error) -> Bool = { _ in true }) -> Observable<E> {
return retryWhen { (errors: Observable<Error>) in
return errors.enumerated().flatMap { attempt, error -> Observable<Void> in
guard shouldRetry(error), maxAttemptCount > attempt + 1 else {
return .error(error)
}
let timer = Observable<Int>.timer(
RxTimeInterval(delay.make(attempt + 1)),
scheduler: MainScheduler.instance
).map { _ in () } // cast to Observable<Void>
return Observable.merge(timer, didBecomeReachable.asObservable())
}
}
}
}
enum DelayOptions {
case immediate()
case constant(time: Double)
case exponential(initial: Double, multiplier: Double, maxDelay: Double)
case custom(closure: (Int) -> Double)
}
extension DelayOptions {
func make(_ attempt: Int) -> Double {
switch self {
case .immediate: return 0.0
case .constant(let time): return time
case .exponential(let initial, let multiplier, let maxDelay):
// if it's first attempt, simply use initial delay, otherwise calculate delay
let delay = attempt == 1 ? initial : initial * pow(multiplier, Double(attempt - 1))
return min(maxDelay, delay)
case .custom(let closure): return closure(attempt)
}
}
}
// The MIT License (MIT)
//
// Copyright (c) 2017 Alexander Grebenyuk (github.com/kean).
import Foundation
import Alamofire
import RxSwift
import RxCocoa
final class Reachability {
static var shared: Reachability {
return AppContainer.shared.reachability
}
/// Monitors general network reachability.
let reachability = NetworkReachabilityManager()
var didBecomeReachable: Signal<Void> { return _didBecomeReachable.asSignal() }
private let _didBecomeReachable = PublishRelay<Void>()
init() {
self.isReachable = _isReachable.asDriver()
if let reachability = self.reachability {
reachability.listener = { [weak self] in
self?.update($0)
}
reachability.startListening()
}
}
private func update(_ status: NetworkReachabilityManager.NetworkReachabilityStatus) {
if case .reachable = status {
_didBecomeReachable.accept(())
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment