Skip to content

Instantly share code, notes, and snippets.

@SergeyPetrachkov
Created April 23, 2019 08:00
Show Gist options
  • Select an option

  • Save SergeyPetrachkov/9eb594e4f10e92d17fc94b7ca6036b02 to your computer and use it in GitHub Desktop.

Select an option

Save SergeyPetrachkov/9eb594e4f10e92d17fc94b7ca6036b02 to your computer and use it in GitHub Desktop.
Swagger helper with generic requestData
enum ServiceError: Error {
case timeout
case nilData
}
extension SwaggerClientAPI {
private enum HeadersKeys: String {
case authorization = "Authorization"
}
class func setBearer(_ token: String) {
SwaggerClientAPI.customHeaders = [HeadersKeys.authorization.rawValue: "Bearer \(token)"]
}
class func clearBearer() {
SwaggerClientAPI.customHeaders.removeValue(forKey: HeadersKeys.authorization.rawValue)
}
}
/// A protocol which every api response with array of items must conform to
protocol SequenceApiContainer {
associatedtype T = Sequence
/// array of items of any type
var data: T? { get set }
}
/// A handler for swagger style responses
class SwaggerApiResponseHandler {
/// Request any data from service and return response model or throw an error
/// - Parameters:
/// - request: api request model provided by you swagger client (Models folder)
/// - functor: method from your API class (APIs folder)
/// - returns: unwrapped model of type declared by functor in `completion`
/// - throws: Either Swagger handled error or `ServiceError.nilData` in any other case
///
/// ```
/// let apiRequest = GetCompanyDetailsRequest(companyId: request.companyId,
/// userLocation: request.geoPoint,
/// locationId: request.cityId)
/// let swaggerModelResponse = try SwaggerApiResponseHandler.requestData(request: apiRequest,
/// functor: CompanyAPI.companyGetCompanyDetails)
/// return Company(swaggerModel: swaggerModelResponse)
/// ```
///
static func requestData<Request, Response>(request: Request,
functor: @escaping ((_ request: Request, _ completion: @escaping (Response?, Error?) -> Void) -> Void)) throws -> Response {
var result: Response?
var outputError: Error?
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
functor(request) { successfulResponse, failure in
do {
let unwrappedResponse = try SwaggerApiResponseHandler.unwrapResponse((successfulResponse, failure))
result = unwrappedResponse
dispatchGroup.leave()
} catch let error {
outputError = error
dispatchGroup.leave()
}
}
_ = dispatchGroup.wait(wallTimeout: .distantFuture)
if let error = outputError {
throw error
}
if let result = result {
return result
} else {
throw ServiceError.nilData
}
}
class func handleResponse<ResponseType: SequenceApiContainer>(_ response: (successResponse: ResponseType?, failure: Error?),
success: ((ResponseType.T) -> Void),
failure: (Error) -> Void) {
if let error = response.failure {
handleError(error: error) { outputError in
failure(outputError)
}
} else {
if let successResponse = response.successResponse?.data {
success(successResponse)
} else {
let error = NSError(domain: "com.actonica.boomerang.manager", code: -2, userInfo: [NSLocalizedDescriptionKey: "server.error.generic".localized])
failure(error)
}
}
}
class func handleResponse<ResponseType>(_ response: (successResponse: ResponseType?, failure: Error?),
success: ((ResponseType) -> Void),
failure: (Error) -> Void) {
if let error = response.failure {
handleError(error: error) { outputError in
failure(outputError)
}
} else {
if let successResponse = response.successResponse {
success(successResponse)
} else {
let error = NSError(domain: "com.actonica.boomerang.manager", code: -2, userInfo: [NSLocalizedDescriptionKey: "server.error.generic".localized])
failure(error)
}
}
}
fileprivate class func handleError(error: Error, failure: (Error) -> Void) {
// Try extract internal error from returned error
switch error {
case let errorResponse as ErrorResponse:
switch errorResponse {
/// code, data, error. in future we can handle different codes. now when internet connection is interrupted error contains info about it, data is empty and code is 500
case .error(_, let data, let error):
if let data = data,
let serverError = try? JSONDecoder().decode(ErrorBase.self, from: data) {
failure(NSError(domain: "com.actonica.manager::handlerError", code: serverError.errorCode, userInfo: [NSLocalizedDescriptionKey: serverError.failReason ?? ""]))
} else {
failure(error)
}
}
// if web service returned error
default:
failure(error)
}
}
class func unwrapResponse<ResponseType>(_ response: (successResponse: ResponseType?, failure: Error?)) throws -> ResponseType {
if let error = response.failure {
try unwrapError(error: error)
} else {
if let successResponse = response.successResponse {
return successResponse
}
}
let error = NSError(domain: "SwaggerApiResponseHandler::unwrapResponse", code: -2, userInfo: [NSLocalizedDescriptionKey: "server.error.generic".localized])
throw error
}
class func unwrapError(error: Error) throws {
// Try extract internal error from returned error
switch error {
case let errorResponse as ErrorResponse:
switch errorResponse {
/// code, data, error. in future we can handle different codes. now when internet connection is interrupted error contains info about it, data is empty and code is 500
case .error(_, let data, let error):
if let data = data,
let serverError = try? JSONDecoder().decode(ErrorBase.self, from: data) {
throw NSError(domain: "SwaggerApiResponseHandler::unwrapError", code: serverError.errorCode, userInfo: [NSLocalizedDescriptionKey: serverError.failReason ?? ""])
} else {
throw error
}
}
// if web service returned error
default:
throw error
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment