@@ -25,35 +25,41 @@ public enum Retry {
2525 ( pow ( 2 , max ( 0 , attempt - 1 ) ) * Decimal( baseDelay) as NSDecimalNumber ) . uint32Value
2626 }
2727
28+ public struct State {
29+ public var retriesLeft : Int
30+ public var currentTry = 0
31+ public var lastError : Swift . Error ?
32+
33+ public var isFirstIteration : Bool { currentTry == 0 }
34+ public var hasRetriesLeft : Bool { retriesLeft > 0 }
35+ mutating func advance( ) {
36+ currentTry += 1
37+ retriesLeft -= 1
38+ }
39+ }
40+
2841 @discardableResult
2942 public static func attempt< T> ( _ label: String ,
3043 delay: Double = 5 ,
3144 retries: Int = 5 ,
3245 logger: RetryLogging = DefaultLogger ( ) ,
3346 _ block: ( ) throws -> T ) throws -> T {
34- var retriesLeft = retries
35- var currentTry = 1
36- var lastError : Swift . Error ?
47+ var state = State ( retriesLeft: retries)
3748 while true {
38- if currentTry > 1 {
39- logger. onStartOfRetry ( label: label, attempt: currentTry)
49+ if !state . isFirstIteration {
50+ logger. onStartOfRetry ( label: label, attempt: state . currentTry)
4051 }
4152 do {
4253 return try block ( )
4354 } catch let Error . abort( with: error) {
4455 throw Error . abort ( with: error)
4556 } catch {
46- logger. onError ( label: label, error: error)
47- lastError = error
48- guard retriesLeft > 0 else { break }
49- let delay = backedOffDelay ( baseDelay: delay, attempt: currentTry)
50- logger. onStartOfDelay ( label: label, delay: Double ( delay) )
51- sleep ( delay)
52- currentTry += 1
53- retriesLeft -= 1
57+ if catchHandler ( label, delay: delay, logger: logger, error: error, state: & state) {
58+ break
59+ }
5460 }
5561 }
56- throw Error . retryLimitExceeded ( lastError: lastError)
62+ throw Error . retryLimitExceeded ( lastError: state . lastError)
5763 }
5864
5965 @discardableResult
@@ -62,29 +68,85 @@ public enum Retry {
6268 retries: Int = 5 ,
6369 logger: RetryLogging = DefaultLogger ( ) ,
6470 _ block: ( ) async throws -> T ) async throws -> T {
65- var retriesLeft = retries
66- var currentTry = 1
67- var lastError : Swift . Error ?
71+ var state = State ( retriesLeft: retries)
6872 while true {
69- if currentTry > 1 {
70- logger. onStartOfRetry ( label: label, attempt: currentTry)
73+ if !state . isFirstIteration {
74+ logger. onStartOfRetry ( label: label, attempt: state . currentTry)
7175 }
7276 do {
7377 return try await block ( )
7478 } catch let Error . abort( with: error) {
7579 throw Error . abort ( with: error)
7680 } catch {
77- logger. onError ( label: label, error: error)
78- lastError = error
79- guard retriesLeft > 0 else { break }
80- let delay = backedOffDelay ( baseDelay: delay, attempt: currentTry)
81- logger. onStartOfDelay ( label: label, delay: Double ( delay) )
82- sleep ( delay)
83- currentTry += 1
84- retriesLeft -= 1
81+ if catchHandler ( label, delay: delay, logger: logger, error: error, state: & state) {
82+ break
83+ }
84+ }
85+ }
86+ throw Error . retryLimitExceeded ( lastError: state. lastError)
87+ }
88+
89+ @discardableResult
90+ public static func attempt< T> ( _ label: String ,
91+ delay: Double = 5 ,
92+ retries: Int = 5 ,
93+ logger: RetryLogging = DefaultLogger ( ) ,
94+ _ block: ( State ) throws -> T ) throws -> T {
95+ var state = State ( retriesLeft: retries)
96+ while true {
97+ if !state. isFirstIteration {
98+ logger. onStartOfRetry ( label: label, attempt: state. currentTry)
99+ }
100+ do {
101+ return try block ( state)
102+ } catch let Error . abort( with: error) {
103+ throw Error . abort ( with: error)
104+ } catch {
105+ if catchHandler ( label, delay: delay, logger: logger, error: error, state: & state) {
106+ break
107+ }
108+ }
109+ }
110+ throw Error . retryLimitExceeded ( lastError: state. lastError)
111+ }
112+
113+ @discardableResult
114+ public static func attempt< T> ( _ label: String ,
115+ delay: Double = 5 ,
116+ retries: Int = 5 ,
117+ logger: RetryLogging = DefaultLogger ( ) ,
118+ _ block: ( State ) async throws -> T ) async throws -> T {
119+ var state = State ( retriesLeft: retries)
120+ while true {
121+ if !state. isFirstIteration {
122+ logger. onStartOfRetry ( label: label, attempt: state. currentTry)
123+ }
124+ do {
125+ return try await block ( state)
126+ } catch let Error . abort( with: error) {
127+ throw Error . abort ( with: error)
128+ } catch {
129+ if catchHandler ( label, delay: delay, logger: logger, error: error, state: & state) {
130+ break
131+ }
85132 }
86133 }
87- throw Error . retryLimitExceeded ( lastError: lastError)
134+ throw Error . retryLimitExceeded ( lastError: state. lastError)
135+ }
136+
137+ static func catchHandler( _ label: String ,
138+ delay: Double ,
139+ logger: RetryLogging ,
140+ error: Swift . Error ,
141+ state: inout State ) -> Bool {
142+ logger. onError ( label: label, error: error)
143+ state. lastError = error
144+ guard state. hasRetriesLeft else { return true }
145+ let delay = backedOffDelay ( baseDelay: delay, attempt: state. currentTry + 1 )
146+ logger. onStartOfDelay ( label: label, delay: Double ( delay) )
147+ sleep ( delay)
148+ state. advance ( )
149+ return false
88150 }
89151}
90152
0 commit comments