Last active
November 7, 2022 07:55
-
-
Save vinczebalazs/f33370c794069601f856589793d9709b to your computer and use it in GitHub Desktop.
Protocol and generic based networking example.
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
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