Skip to content

Instantly share code, notes, and snippets.

@vinczebalazs
Last active November 7, 2022 07:55
Show Gist options
  • Save vinczebalazs/f33370c794069601f856589793d9709b to your computer and use it in GitHub Desktop.
Save vinczebalazs/f33370c794069601f856589793d9709b to your computer and use it in GitHub Desktop.
Protocol and generic based networking example.
import Foundation
// Model.
struct Employee: Codable {
let id: Int
let name: String
}
// Networking.
enum HTTPMethod: String {
case delete = "DELETE"
case get = "GET"
case post = "POST"
case put = "PUT"
}
protocol APIRequest {
associatedtype Result
var endpoint: String { get }
var method: HTTPMethod { get }
}
enum APIError: Error {
case invalidResponse
case httpError(Int)
}
final class APIClient {
// MARK: Public Properties
static let shared = APIClient()
// MARK: Private Properties
private let baseURL = URL(string: "https://example.com")!
private lazy var urlSession: URLSession = {
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = [
"Accept": "application/json",
"Content-Type": "application/json"]
return URLSession(configuration: config)
}()
// MARK: Initializers
private init() {}
// MARK: Public Methods
func execute<RequestType: APIRequest>(_ request: RequestType,
completion: @escaping (Result<RequestType.Result, Error>) -> ())
where RequestType.Result: Decodable {
urlSession.dataTask(with: urlRequest(for: request)) { (data, response, error) in
if let error = error {
completion(.failure(error))
} else {
if let urlResponse = response as? HTTPURLResponse {
if urlResponse.statusCode >= 400 {
completion(.failure(APIError.httpError(urlResponse.statusCode)))
} else {
do {
completion(.success(try JSONDecoder().decode(RequestType.Result.self, from: data!)))
} catch let _error {
completion(.failure(_error))
}
}
} else {
completion(.failure(APIError.invalidResponse))
}
}
}.resume()
}
// MARK: Private Methods
private func urlRequest<RequestType: APIRequest>(for request: RequestType) -> URLRequest {
var urlComponents = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)!
urlComponents.path = "/\(request.endpoint)"
var urlRequest = URLRequest(url: urlComponents.url!)
urlRequest.httpMethod = request.method.rawValue
return urlRequest
}
}
// Example usage.
struct EmployeesRequest: APIRequest {
typealias Result = [Employee]
let endpoint = "employees"
let method = HTTPMethod.get
}
APIClient.shared.execute(EmployeesRequest(), completion: {
switch $0 {
case let .success(result):
print(result)
case let .failure(error):
print(error)
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment