struct APIService: APIServiceProtocol {
func fetch<T: Decodable>(_ type: T.Type, url: URL?, headerPara: [String: String], completion: @escaping(Result<T,APIError>) -> Void) {
guard let url = url else {
let error = APIError.badURL
completion(Result.failure(error))
return
}
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
guard let encoded = try? JSONEncoder().encode(headerPara) else {
print("Failed to encode order")
return
}
request.httpBody = encoded
URLSession.shared.dataTask(with: request) { (data , response, error) in
if let error = error as? URLError {
completion(Result.failure(APIError.url(error)))
}else if let response = response as? HTTPURLResponse, !(200...299).contains(response.statusCode) {
completion(Result.failure(APIError.badResponse(statusCode: response.statusCode)))
}else if let data = data {
let decoder = JSONDecoder()
do {
let result = try decoder.decode(type, from: data)
completion(Result.success(result))
}catch {
completion(Result.failure(APIError.parsing(error as? DecodingError)))
}
}
}.resume()
// let task = URLSession.shared.dataTask(with: url) {(data , response, error) in
//
// if let error = error as? URLError {
// completion(Result.failure(APIError.url(error)))
// }else if let response = response as? HTTPURLResponse, !(200...299).contains(response.statusCode) {
// completion(Result.failure(APIError.badResponse(statusCode: response.statusCode)))
// }else if let data = data {
// let decoder = JSONDecoder()
// do {
// let result = try decoder.decode(type, from: data)
// completion(Result.success(result))
//
// }catch {
// completion(Result.failure(APIError.parsing(error as? DecodingError)))
// }
//
// }
// }
//
// task.resume()
}
func fetchBreeds(url: URL?, completion: @escaping(Result<[Breed], APIError>) -> Void) {
guard let url = url else {
let error = APIError.badURL
completion(Result.failure(error))
return
}
let task = URLSession.shared.dataTask(with: url) {(data , response, error) in
if let error = error as? URLError {
completion(Result.failure(APIError.url(error)))
}else if let response = response as? HTTPURLResponse, !(200...299).contains(response.statusCode) {
completion(Result.failure(APIError.badResponse(statusCode: response.statusCode)))
}else if let data = data {
let decoder = JSONDecoder()
do {
let breeds = try decoder.decode([Breed].self, from: data)
completion(Result.success(breeds))
}catch {
completion(Result.failure(APIError.parsing(error as? DecodingError)))
}
}
}
task.resume()
}
}
protocol APIServiceProtocol {
func fetchBreeds(url: URL?, completion: @escaping(Result<[Breed], APIError>) -> Void)
func fetch<T: Decodable>(_ type: T.Type, url: URL?, headerPara: [String: String], completion: @escaping(Result<T,APIError>) -> Void)
}
enum APIError: Error, CustomStringConvertible {
case badURL
case badResponse(statusCode: Int)
case url(URLError?)
case parsing(DecodingError?)
case unknown
var localizedDescription: String {
// user feedback
switch self {
case .badURL, .parsing, .unknown:
return "Sorry, something went wrong."
case .badResponse(_):
return "Sorry, the connection to our server failed."
case .url(let error):
return error?.localizedDescription ?? "Something went wrong."
}
}
var description: String {
//info for debugging
switch self {
case .unknown: return "unknown error"
case .badURL: return "invalid URL"
case .url(let error):
return error?.localizedDescription ?? "url session error"
case .parsing(let error):
return "parsing error \(error?.localizedDescription ?? "")"
case .badResponse(statusCode: let statusCode):
return "bad response with status code \(statusCode)"
}
}
}
class APIMannager: ObservableObject {
@Published var isLoading: Bool = false
@Published var errorMessage: String? = nil
let service: APIServiceProtocol
init(service: APIServiceProtocol = APIService()) {
self.service = service
}
func requestPost<T: Decodable>(url: String, headerPara: [String: String], completion: @escaping(Result<T,APIError>) -> Void) {
isLoading = true
errorMessage = nil
let url = URL(string: "\(url)")!
service.fetch(T.self, url: url, headerPara: headerPara) { [unowned self] result in
DispatchQueue.main.async {
self.isLoading = false
switch result {
case .failure(let error):
self.errorMessage = error.localizedDescription
// print(error.description)
print(error)
case .success(let resultT):
completion(Result.success(resultT))
}
}
}
}
}
网友评论