Skip to content

Instantly share code, notes, and snippets.

@jamesrochabrun
Last active June 10, 2021 20:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jamesrochabrun/dd469089980ab02611a2f88b64c41c16 to your computer and use it in GitHub Desktop.
Save jamesrochabrun/dd469089980ab02611a2f88b64c41c16 to your computer and use it in GitHub Desktop.
Generic closure based API
/// 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