Last active
June 10, 2021 20:28
-
-
Save jamesrochabrun/dd469089980ab02611a2f88b64c41c16 to your computer and use it in GitHub Desktop.
Generic closure based API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 1 Errors | |
enum APIError: Error { | |
case requestFailed(description: String) | |
case jsonConversionFailure(description: String) | |
case invalidData | |
case responseUnsuccessful(description: String) | |
case jsonParsingFailure | |
case noInternet | |
case failedSerialization | |
var customDescription: String { | |
switch self { | |
case let .requestFailed(description): return "Request Failed error -> \(description)" | |
case .invalidData: return "Invalid Data error)" | |
case let .responseUnsuccessful(description): return "Response Unsuccessful error -> \(description)" | |
case .jsonParsingFailure: return "JSON Parsing Failure error)" | |
case let .jsonConversionFailure(description): return "JSON Conversion Failure -> \(description)" | |
case .noInternet: return "No internet connection" | |
case .failedSerialization: return "serialization print for debug failed." | |
} | |
} | |
} | |
// 2 Protocol | |
protocol GenericAPI { | |
var session: URLSession { get } | |
func fetch<T: Decodable>( | |
type: T.Type, | |
with request: URLRequest, | |
completion: @escaping (Result<T, APIError>) -> Void) | |
} | |
extension GenericAPI { | |
// 3 Completion based function | |
private func decodingTask<T: Decodable>( | |
with request: URLRequest, | |
decodingType: T.Type, | |
completionHandler completion: @escaping (Result<T, APIError>) -> Void) | |
-> URLSessionDataTask { | |
let task = session.dataTask(with: request) { data, response, error in | |
guard let httpResponse = response as? HTTPURLResponse else { | |
/// a | |
completion(.failure(.requestFailed(description: error.debugDescription))) | |
return | |
} | |
guard httpResponse.statusCode == 200 else { | |
/// b | |
completion(.failure(.responseUnsuccessful(description: "status code = \(httpResponse.statusCode)"))) | |
return | |
} | |
guard let data = data else { | |
/// c | |
completion(.failure(.invalidData)) | |
return | |
} | |
do { | |
let decoder = JSONDecoder() | |
let genericModel = try decoder.decode(T.self, from: data) | |
/// d | |
completion(.success(genericModel)) | |
} catch let error { | |
/// e | |
completion(.failure(.jsonConversionFailure(description: error.localizedDescription))) | |
} | |
} | |
return task | |
} | |
/// 5 Public method | |
func fetch<T: Decodable>( | |
type: T.Type, | |
with request: URLRequest, | |
completion: @escaping (Result<T, APIError>) -> Void) { | |
/// 6 Calling the task | |
let task = decodingTask(with: request, decodingType: T.self) { result in | |
DispatchQueue.main.async { | |
/// 7 Completion handler with the result. | |
completion(result) | |
} | |
} | |
task.resume() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment