Network Client — Interceptors, Validators & DI

  • Interceptors — Interceptors are a powerful mechanism that can pre-process or post-process a request with defined set of steps.
  • Validators — Validating Request / Response at common place
  • Dependency Injection

Interceptors -

  • Auth Headers
  • Device / User Info in each request
  • Encryption of PayLoad
  • Decryption of PayLoad
class CommonRequestHeaderInterceptors : RequestInterceptor {

func interceptRequest(request: inout URLRequest) {
request.addValue("Bearer " + (APIDataManager.appToken ?? ""), forHTTPHeaderField: "Authorization")
}

}

Validators

  • Serialization / Deserialization
  • Fallback Mechanism — Success / Error DAOs
  • Common Error Generation
  • Token Management
class AppTokenRefreshWrapper : RetryInterceptor{

func retryRequest(onSuccess: @escaping () -> Void, onError: @escaping (APITimeError) -> Void) {

let params : String = "grant_type=client_credentials"

RefreshAppTokenAPI().execute(requestBody: params, onSuccessResponse: { (appToken) in

APIDataManager.appToken = appToken.access_token

onSuccess()

}, onErrorResponse: { (error) in

onError(APITimeError.init(errorCode: RCNetworkConstants.inValidResponse.rawValue, message: RCNetworkConstants.inValidResponse.rawValue))

}) { (error) in

onError(error)

}

}

}

Dependency Injection

class AlamofireNetworkClient : NetworkDispatcher{  func consumeRequest(request: URLRequest, onSuccess: @escaping (HTTPURLResponse, Data?) -> Void, onError: @escaping (APITimeError) -> Void) {    Alamofire.request(request)      .validate()      .responseData { (response) in        guard response.result.isSuccess, let httpResponse = response.response else {          let customError = APITimeError.init(errorCode: "\(response.response?.statusCode ?? -1)",message: response.result.error?.localizedDescription ?? "unexpectedError", receivedResponse: response.data)          onError(customError)          return        }        guard let responseData = response.data else{          let customError = APITimeError.init(errorCode: "\(httpResponse.statusCode)", message: "unexpectedError", receivedResponse: response.data)          onError(customError)          return        }        onSuccess(httpResponse, responseData)    }  }}
class CoreNetworkClientMock : NetworkDispatcher {  private var targetResponse : Data?  private var errorResponse : APITimeError?  init(success:Data?, error:APITimeError?) {    targetResponse = success    errorResponse = error  }  func consumeRequest(request: URLRequest, onSuccess: @escaping (HTTPURLResponse, Data?) -> Void, onError: @escaping (APITimeError) -> Void) {    if targetResponse != nil {      let fakeResponse = HTTPURLResponse.init()      onSuccess(fakeResponse, targetResponse)    }else {      onError(errorResponse!)    }  }}

Integration

public extension APIRequest {  var retryInterceptor : (interceptor : RetryInterceptor, errorCodes : [String])?{    get{      (AppTokenRefreshWrapper(), ["401", "400"])    }  }  var interceptors : (requestInterceptors: [RequestInterceptor],responseInterceptors: [ResponseInterceptor])? {    get {      ([CommonRequestHeaderInterceptors()], [])    }  }  var networkClient : NetworkDispatcher {    get {      CoreNetworkClient()    }  }  var mimeType: String {    get{      ""    }     }  var timeoutInterval : TimeInterval {    get{      30    }  }  var cachingPolicy : URLRequest.CachePolicy {    get{      URLRequest.CachePolicy.useProtocolCachePolicy    }    }

}
  • API call can be declared using structs, its simple
  • Implement Protocols for required Request Type viz GET, POST
struct HomeFeedAPI: GETAPIRequest {     typealias ResponseType = HomeResponseDAO  typealias ErrorResponseType = AppTokenErrorDAO  var endPoint: String {    return PlatformConstants.serverURL + PlatformConstants.homeFeed  }}
struct RefreshAppTokenAPI: POSTAPIRequest {   typealias RequestBodyType = String  typealias ResponseType = AppTokenDAO  typealias ErrorResponseType = AppTokenErrorDAO  var endPoint: String {    return PlatformConstants.serverURL + PlatformConstants.refreshAppToken  }  var interceptors: (requestInterceptors: [RequestInterceptor], responseInterceptors: [ResponseInterceptor])? {    return ([TokenRequestHeaderInterceptor()],[])  }}
HomeFeedAPI().execute(requestParams: queryParams, onSuccessResponse: { [weak self] (response) in      // Received Success Response DAO    }, onErrorResponse: { [weak self] (error) in      // Received Error Response DAO    }) { [weak self] (error) in      // Received Generic Error    }

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store