2022-12-04 17:21:11 +01:00
|
|
|
//
|
|
|
|
// 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<Model>(
|
|
|
|
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
|
2022-12-04 17:50:15 +01:00
|
|
|
case .forbidden:
|
|
|
|
throw APIClientError.authenticationFailed
|
2022-12-04 17:21:11 +01:00
|
|
|
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
|
2022-12-04 17:50:15 +01:00
|
|
|
case .forbidden:
|
|
|
|
throw APIClientError.authenticationFailed
|
2022-12-04 17:21:11 +01:00
|
|
|
case .notFound:
|
|
|
|
throw APIClientError.itemDoesNotExist
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Helpers
|
|
|
|
|
|
|
|
private extension APIClient {
|
|
|
|
func decode<Model: Decodable>(
|
|
|
|
_ 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
|
2022-12-04 17:50:15 +01:00
|
|
|
case authenticationFailed
|
2022-12-04 17:21:11 +01:00
|
|
|
case itemIsNotFile
|
|
|
|
case itemAlreadyExist
|
|
|
|
case itemDoesNotExist
|
|
|
|
case notSupported
|
|
|
|
}
|