@@ -72,9 +72,11 @@ open class RestApiCaller : NSObject {
7272 let errorDeserializer : ( any Deserializer ) ?
7373 /// Cookie storage used by the session (if provided).
7474 let httpCookieStorage : HTTPCookieStorage ?
75+
76+ private let interceptorLock = NSLock ( )
7577
76- /// Registered request/response interceptors.
77- var interceptors : [ any URLRequestInterceptor ] ?
78+ /// Registered request/response interceptors (empty by default) .
79+ var interceptors : [ URLRequestInterceptor ] = [ ]
7880
7981 /// Closure used to generate dynamic headers prior to each request.
8082 public let headerGenerator : HeaderGenerator ?
@@ -150,19 +152,15 @@ open class RestApiCaller : NSObject {
150152
151153 @inline ( __always)
152154 private func callInvokeInterceptors( _ request: inout URLRequest ) {
153- if let interceptors {
154- for interceptor in interceptors {
155- interceptor. invokeRequest ( request: & request, for: session)
156- }
155+ for interceptor in interceptors {
156+ interceptor. invokeRequest ( request: & request, for: session)
157157 }
158158 }
159159 @inline ( __always)
160160 private func callReceiveInterceptors( _ data: Data , _ response: HTTPURLResponse ) {
161- if let interceptors {
162- // we revers interceptor chain when receiving...
163- for interceptor in interceptors. reversed ( ) {
164- interceptor. receiveResponse ( data: data, response: response, for: session)
165- }
161+ // reverse the order for response handling so the most recently added interceptor observes the response first
162+ for interceptor in interceptors. reversed ( ) {
163+ interceptor. receiveResponse ( data: data, response: response, for: session)
166164 }
167165 }
168166
@@ -279,10 +277,9 @@ open class RestApiCaller : NSObject {
279277 let httpStatus = httpResponse. status
280278
281279 // For requests without deserialization and no error just return the status
282- if type ( of: responseDeserializer) == VoidDeserializer . self || httpStatus == . noContent {
283- if httpStatus. type == . success {
284- return ( nil , httpResponse. status)
285- }
280+ if shouldBypassDeserialization ( responseDeserializer, status: httpStatus) ,
281+ httpStatus. type == . success {
282+ return ( nil , httpStatus)
286283 }
287284
288285 guard !data. isEmpty else {
@@ -291,52 +288,50 @@ open class RestApiCaller : NSObject {
291288
292289 // Postcondition: We have a response object or error that needs to be parsed!
293290
294- let contentType = httpResponse . value ( forHTTPHeaderField : HTTPHeaderKeys . ContentType . rawValue )
291+ _ = try validatedMimeType ( from : httpResponse )
295292
296- // Note: some servers return also encoding i.e. Content-Type: application/json; charset=utf-8 take the first part
293+ if httpStatus. type == . success {
294+ let transformedResponse = try decodeSuccessfulResponse ( data: data, response: httpResponse, deserializer: responseDeserializer)
295+ return ( transformedResponse, httpStatus)
296+ }
297+
298+ throw try buildErrorResponse ( data: data, response: httpResponse, status: httpStatus)
299+ }
300+
301+ private func shouldBypassDeserialization< T: Deserializer > ( _ deserializer: T , status: HTTPStatusCode ) -> Bool {
302+ ( deserializer is VoidDeserializer ) || status == . noContent
303+ }
304+
305+ private func validatedMimeType( from response: HTTPURLResponse ) throws -> MimeType {
306+ let contentType = response. value ( forHTTPHeaderField: HTTPHeaderKeys . ContentType. rawValue)
297307 let firstContentMimeType = contentType? . components ( separatedBy: " ; " ) . first
298308
299- guard let firstContentMimeType, let _ = MimeType ( rawValue: firstContentMimeType) else {
309+ guard let firstContentMimeType,
310+ let mimeType = MimeType ( rawValue: firstContentMimeType) else {
300311 throw RestError . invalidMimeType ( contentType)
301312 }
302-
303- // Postcondition: Response or error ContentTyp is supported
304-
305- if httpStatus. type == . success {
306-
307- // Postcondition: httpStatus in 200...299
308- if httpStatus == . ok {
309- // Postcondition: httpStatus is 200 we need to deserialize
310- do {
311- let transformedResponse = try responseDeserializer. deserialize ( data)
312- return ( transformedResponse, httpResponse. status)
313- } catch {
314- throw RestError . malformedResponse ( httpResponse, data, error)
315- }
316- } else {
317- // Postcondition: httpStatus is 201...299
318- // Note: we skipt data in this case
319- return ( nil , httpStatus)
320- }
321-
322- } else {
323-
324- // Postcondition: httpStatus not 2XX. We have an error and error data
325- var failedRestCallError : RestError
326-
313+ return mimeType
314+ }
315+
316+ private func decodeSuccessfulResponse< T: Deserializer > ( data: Data , response: HTTPURLResponse , deserializer: T ) throws -> T . ResponseType ? {
317+ if response. status == . ok {
327318 do {
328- let errorJson = try errorDeserializer? . deserialize ( data)
329- failedRestCallError = RestError . failedRestCall ( httpResponse, httpStatus, error: errorJson)
319+ return try deserializer. deserialize ( data)
330320 } catch {
331- throw RestError . malformedResponse ( httpResponse , data, error)
321+ throw RestError . malformedResponse ( response , data, error)
332322 }
333-
334- throw failedRestCallError
335-
336323 }
337-
324+ return nil
338325 }
339326
327+ private func buildErrorResponse( data: Data , response: HTTPURLResponse , status: HTTPStatusCode ) throws -> RestError {
328+ do {
329+ let errorPayload = try errorDeserializer? . deserialize ( data)
330+ return RestError . failedRestCall ( response, status, error: errorPayload)
331+ } catch {
332+ throw RestError . malformedResponse ( response, data, error)
333+ }
334+ }
340335
341336
342337// MARK: Public API that can be used from other classes or subclass
@@ -346,10 +341,11 @@ open class RestApiCaller : NSObject {
346341 /// - Parameter interceptor: Interceptor appended to the invocation chain.
347342 public func registerRequestInterceptor( _ interceptor: any URLRequestInterceptor ) {
348343 logger. info ( " Registering request interceptor: \( interceptor) " )
349- if self . interceptors == nil {
350- self . interceptors = [ any URLRequestInterceptor ] ( )
351- }
352- interceptors!. append ( interceptor)
344+
345+ interceptorLock. lock ( )
346+ defer { interceptorLock. unlock ( ) }
347+
348+ interceptors. append ( interceptor)
353349 }
354350
355351 /// Executes an asynchronous `GET` request and decodes the JSON response.
0 commit comments