@@ -25,8 +25,12 @@ public class {{ options.name }}Client {
2525
2626 public var decodingQueue = DispatchQueue ( label: " {{ options.name }}Client " , qos: . utility, attributes: . concurrent)
2727
28+ /// The maximum number a request will be retried due to `401` responses
2829 public var maxUnauthorizedRetryCount = 1
2930
31+ /// The maximum number a request will be retried due to network connection errors and timeouts
32+ public var maxRetryCount = 8
33+
3034 public init( baseURL: String, configuration: URLSessionConfiguration = . default, defaultHeaders: [ String: String] = [ : ] , behaviours: [ { { options. name } } RequestBehaviour] = [ ] ) {
3135 self . baseURL = baseURL
3236 self . behaviours = self . behaviours + behaviours
@@ -41,11 +45,18 @@ public class {{ options.name }}Client {
4145 /// - Parameters:
4246 /// - request: The API request to make
4347 /// - behaviours: A list of behaviours that will be run for this request. Merged with APIClient.behaviours
48+ /// - currentUnauthorizedRetryCount: The current number of retries for this request due to `401` responses
49+ /// - currentRetryCount: The current number of retries for this request due to network connection errors and timeouts
4450 /// - completionQueue: The queue that complete will be called on
4551 /// - complete: A closure that gets passed the {{ options.name }}Response
4652 /// - Returns: A cancellable request. Not that cancellation will only work after any validation RequestBehaviours have run
4753 @discardableResult
48- public func makeRequest< T> ( _ request: { { options. name } } Request< T> , behaviours: [ { { options. name } } RequestBehaviour] = [ ] , currentNumberOfRetries: Int = 0 , completionQueue: DispatchQueue = DispatchQueue . main, complete: @escaping ( { { options. name } } Response< T> ) - > Void) - > Cancellable{ { options. name } } Request? {
54+ public func makeRequest< T> ( _ request: { { options. name } } Request< T> ,
55+ behaviours: [ { { options. name } } RequestBehaviour] = [ ] ,
56+ currentUnauthorizedRetryCount: Int = 0 ,
57+ currentRetryCount: Int = 0 ,
58+ completionQueue: DispatchQueue = DispatchQueue . main,
59+ complete: @escaping ( { { options. name } } Response< T> ) - > Void) - > Cancellable{ { options. name } } Request? {
4960 // create composite behaviour to make it easy to call functions on array of behaviours
5061 let requestBehaviour = { { options. name } } RequestBehaviourGroup( request: request, behaviours: self . behaviours + behaviours)
5162
@@ -76,7 +87,13 @@ public class {{ options.name }}Client {
7687 requestBehaviour. validate ( urlRequest) { result in
7788 switch result {
7889 case . success( let urlRequest) :
79- self . makeNetworkRequest ( request: request, urlRequest: urlRequest, cancellableRequest: cancellableRequest, requestBehaviour: requestBehaviour, currentNumberOfRetries: currentNumberOfRetries, completionQueue: completionQueue, complete: complete)
90+ self . makeNetworkRequest ( request: request,
91+ urlRequest: urlRequest,
92+ cancellableRequest: cancellableRequest,
93+ requestBehaviour: requestBehaviour,
94+ currentUnauthorizedRetryCount: currentUnauthorizedRetryCount,
95+ currentRetryCount: currentRetryCount,
96+ completionQueue: completionQueue, complete: complete)
8097 case . failure( let error) :
8198 let error = APIClientError . validationError ( error)
8299 let response = { { options. name } } Response< T> ( request: request, result: . failure( error) , urlRequest: urlRequest)
@@ -87,7 +104,14 @@ public class {{ options.name }}Client {
87104 return cancellableRequest
88105 }
89106
90- private func makeNetworkRequest< T> ( request: { { options. name } } Request< T> , urlRequest: URLRequest, cancellableRequest: Cancellable{ { options. name } } Request, requestBehaviour: { { options. name } } RequestBehaviourGroup, currentNumberOfRetries: Int, complet ionQueue: DispatchQueue, complet e: @escaping ( { { options. name } } Response< T> ) -> Void) {
107+ private func makeNetworkRequest< T> ( request: { { options. name } } Request< T> ,
108+ urlRequest: URLRequest,
109+ cancellableRequest: Cancellable{ { options. name } } Request,
110+ requestBehaviour: { { options. name } } RequestBehaviourGroup,
111+ currentUnauthorizedRetryCount: Int,
112+ currentRetryCount: Int,
113+ completionQueue: DispatchQueue,
114+ complete: @escaping ( { { options. name } } Response< T> ) - > Void) {
91115 requestBehaviour. beforeSend ( )
92116 if request. service. isUpload {
93117 let body = NSMutableData ( )
@@ -141,47 +165,80 @@ public class {{ options.name }}Client {
141165 }
142166 body. appendString ( " -- \( boundary) -- \r \n " )
143167 } else {
144- let task = self . session. dataTask ( with: urlRequest, completionHandler: { [ weak self] data, response, error -> Void in
145- // Handle response
146- self ? . decodingQueue. async {
147- guard let response = response as? HTTPURLResponse else {
148- var apiError : APIClientError
149- if let error = error {
150- apiError = APIClientError . networkError ( error)
151- } else {
152- apiError = APIClientError . networkError ( URLRequestError . responseInvalid)
153- }
154- let result : APIResult < T > = . failure( apiError)
155- requestBehaviour. onFailure ( urlRequest: urlRequest, response: HTTPURLResponse ( ) , error: apiError)
168+ let task = performRequest ( request: request,
169+ urlRequest: urlRequest,
170+ cancellableRequest: cancellableRequest,
171+ requestBehaviour: requestBehaviour,
172+ currentUnauthorizedRetryCount: currentUnauthorizedRetryCount,
173+ currentRetryCount: currentRetryCount,
174+ completionQueue: completionQueue,
175+ complete: complete)
156176
157- let response = { { options. name } } Response< T> ( request: request, result: result, urlRequest: urlRequest)
158- requestBehaviour. onResponse ( response: response. asAny ( ) )
177+ self . decodingQueue. async {
178+ task. resume ( )
179+ }
159180
160- completionQueue . async {
161- complete ( response )
162- }
181+ cancellableRequest . sessionTask = task
182+ }
183+ }
163184
164- return
185+ private func performRequest< T> ( request: { { options. name } } Request< T> ,
186+ urlRequest: URLRequest ,
187+ cancellableRequest: Cancellable { { options. name } } Request,
188+ requestBehaviour: { { options. name } } RequestBehaviourGroup,
189+ currentUnauthorizedRetryCount: Int,
190+ currentRetryCount: Int,
191+ completionQueue: DispatchQueue,
192+ complete: @escaping ( { { options. name } } Response< T> ) - > Void) -> URLSessionDataTask {
193+ let maxRetryCount = maxRetryCount
194+ return session. dataTask ( with: urlRequest, completionHandler: { [ weak self] data, response, error -> Void in
195+ // Handle response
196+ self ? . decodingQueue. async {
197+ let newRetryCount = currentRetryCount + 1
198+ if API . shouldRetryRequest ( currentRetryCount: newRetryCount,
199+ maxRetryCount: maxRetryCount,
200+ response: response) {
201+ let requestDelay = API . nextExponentialBackoffRequestDelay ( currentRetryCount: newRetryCount)
202+ self ? . decodingQueue. asyncAfter ( deadline: . now( ) + . seconds( requestDelay) ) { [ weak self] in
203+ self ? . makeNetworkRequest ( request: request,
204+ urlRequest: urlRequest,
205+ cancellableRequest: cancellableRequest,
206+ requestBehaviour: requestBehaviour,
207+ currentUnauthorizedRetryCount: currentUnauthorizedRetryCount,
208+ currentRetryCount: newRetryCount,
209+ completionQueue: completionQueue,
210+ complete: complete)
165211 }
166-
212+ } else if let response = response as? HTTPURLResponse {
167213 self ? . handleResponse ( request: request,
168214 requestBehaviour: requestBehaviour,
169215 data: data,
170216 response: response,
171217 error: error,
172218 urlRequest: urlRequest,
173- currentNumberOfRetries: currentNumberOfRetries,
219+ currentUnauthorizedRetryCount: currentUnauthorizedRetryCount,
220+ currentRetryCount: currentRetryCount,
174221 completionQueue: completionQueue,
175222 complete: complete)
176- }
177- } )
223+ } else {
224+ var apiError : APIClientError
225+ if let error = error {
226+ apiError = APIClientError . networkError ( error)
227+ } else {
228+ apiError = APIClientError . networkError ( URLRequestError . responseInvalid)
229+ }
230+ let result : APIResult < T > = . failure( apiError)
231+ requestBehaviour. onFailure ( urlRequest: urlRequest, response: HTTPURLResponse ( ) , error: apiError)
178232
179- self . decodingQueue. async {
180- task. resume ( )
181- }
233+ let response = { { options. name } } Response< T> ( request: request, result: result, urlRequest: urlRequest)
234+ requestBehaviour. onResponse ( response: response. asAny ( ) )
182235
183- cancellableRequest. sessionTask = task
184- }
236+ completionQueue. async {
237+ complete ( response)
238+ }
239+ }
240+ }
241+ } )
185242 }
186243
187244 private func handleResponse< T> ( request: { { options. name } } Request< T> ,
@@ -190,7 +247,8 @@ public class {{ options.name }}Client {
190247 response: HTTPURLResponse ,
191248 error: Error ? ,
192249 urlRequest: URLRequest ,
193- currentNumberOfRetries: Int ,
250+ currentUnauthorizedRetryCount: Int ,
251+ currentRetryCount: Int ,
194252 completionQueue: DispatchQueue ,
195253 complete: @escaping ( { { options. name } } Response< T> ) -> Void ) {
196254 let result : APIResult < T >
@@ -209,11 +267,16 @@ public class {{ options.name }}Client {
209267 }
210268
211269 if response. statusCode == HttpStatusCode . unauthorized. rawValue
212- && currentNumberOfRetries < maxUnauthorizedRetryCount
270+ && currentUnauthorizedRetryCount < maxUnauthorizedRetryCount
213271 && IDKit . isSessionAvailable {
214272 IDKit . apiInducedRefresh { [ weak self] error in
215273 guard let error = error else {
216- self ? . makeRequest ( request, behaviours: requestBehaviour. behaviours, currentNumberOfRetries: currentNumberOfRetries + 1 , completionQueue: completionQueue, complete: complete)
274+ self ? . makeRequest ( request,
275+ behaviours: requestBehaviour. behaviours,
276+ currentUnauthorizedRetryCount: currentUnauthorizedRetryCount + 1 ,
277+ currentRetryCount: currentRetryCount,
278+ completionQueue: completionQueue,
279+ complete: complete)
217280 return
218281 }
219282
0 commit comments