Skip to content

Instantly share code, notes, and snippets.

@azamsharp
Created August 10, 2023 20:04
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save azamsharp/91919badd2c70c75732ad715b1c3e01e to your computer and use it in GitHub Desktop.
Save azamsharp/91919badd2c70c75732ad715b1c3e01e to your computer and use it in GitHub Desktop.
//
// HTTPClient.swift
// GroceryApp
//
// Created by Mohammad Azam on 5/7/23.
//
import Foundation
enum NetworkError: Error {
case badRequest
case serverError(String)
case decodingError(Error)
case invalidResponse
case invalidURL
case httpError(Int)
}
extension NetworkError: LocalizedError {
var errorDescription: String? {
switch self {
case .badRequest:
return NSLocalizedString("Unable to perform request", comment: "badRequestError")
case .serverError(let errorMessage):
return NSLocalizedString(errorMessage, comment: "serverError")
case .decodingError:
return NSLocalizedString("Unable to decode successfully.", comment: "decodingError")
case .invalidResponse:
return NSLocalizedString("Invalid response", comment: "invalidResponse")
case .invalidURL:
return NSLocalizedString("Invalid URL", comment: "invalidURL")
case .httpError(_):
return NSLocalizedString("Bad request", comment: "badRequest")
}
}
}
enum HTTPMethod {
case get([URLQueryItem])
case post(Data?)
case delete
var name: String {
switch self {
case .get:
return "GET"
case .post:
return "POST"
case .delete:
return "DELETE"
}
}
}
struct Resource<T: Codable> {
let url: URL
var method: HTTPMethod = .get([])
var modelType: T.Type
}
struct HTTPClient {
static let shared = HTTPClient()
private let session: URLSession
private init() {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = ["Content-Type": "application/json"]
self.session = URLSession(configuration: configuration)
}
func load<T: Codable>(_ resource: Resource<T>) async throws -> T {
var request = URLRequest(url: resource.url)
switch resource.method {
case .get(let queryItems):
var components = URLComponents(url: resource.url, resolvingAgainstBaseURL: false)
components?.queryItems = queryItems
guard let url = components?.url else {
throw NetworkError.badRequest
}
request = URLRequest(url: url)
case .post(let data):
request.httpMethod = resource.method.name
request.httpBody = data
case .delete:
request.httpMethod = resource.method.name
}
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw NetworkError.invalidResponse
}
guard (200..<300).contains(httpResponse.statusCode) else {
throw NetworkError.httpError(httpResponse.statusCode)
}
do {
let result = try JSONDecoder().decode(resource.modelType, from: data)
return result
} catch {
throw NetworkError.decodingError(error)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment