// // APIClient.swift // APIService // // Created by Javier Cicchelli on 04/12/2022. // Copyright © 2022 Röck+Cöde. All rights reserved. // import Foundation struct APIClient { private let session: URLSession private let decoder: JSONDecoder private let makeURLRequest: MakeURLRequestUseCase } // MARK: - Initialisers extension APIClient { init(configuration: URLSessionConfiguration = .default) { self.session = .init(configuration: configuration) self.decoder = .init() self.makeURLRequest = .init() } } // MARK: - Client extension APIClient: Client { func request( endpoint: some Endpoint, model: Model.Type ) async throws -> Model where Model : Decodable { let urlRequest = try makeURLRequest(endpoint: endpoint) let (data, response) = try await session.data(for: urlRequest) guard let httpResponse = response as? HTTPURLResponse, let responseStatus = ResponseStatus(rawValue: httpResponse.statusCode) else { throw APIClientError.responseNotReturned } switch responseStatus { case .ok, .created: return try await decode(data, as: model) case .noContent: throw APIClientError.notSupported case .badRequest: throw APIClientError.itemAlreadyExist case .notFound: throw APIClientError.itemDoesNotExist } } @discardableResult func request( endpoint: some Endpoint ) async throws -> Data { let urlRequest = try makeURLRequest(endpoint: endpoint) let (data, response) = try await session.data(for: urlRequest) guard let httpResponse = response as? HTTPURLResponse, let responseStatus = ResponseStatus(rawValue: httpResponse.statusCode) else { throw APIClientError.responseNotReturned } switch responseStatus { case .ok, .noContent: return data case .created: throw APIClientError.notSupported case .badRequest: throw APIClientError.itemIsNotFile case .notFound: throw APIClientError.itemDoesNotExist } } } // MARK: - Helpers private extension APIClient { func decode( _ data: Data, as model: Model.Type ) async throws -> Model { do { decoder.dateDecodingStrategy = .formatted(.isoZulu) return try decoder.decode(model, from: data) } catch { decoder.dateDecodingStrategy = .iso8601 return try decoder.decode(model, from: data) } } } // MARK: - Errors public enum APIClientError: Error { case responseNotReturned case itemIsNotFile case itemAlreadyExist case itemDoesNotExist case notSupported }