Skip to content

Commit

Permalink
Big Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Joe Maghzal committed Feb 5, 2023
1 parent 7cadc18 commit b546f28
Show file tree
Hide file tree
Showing 19 changed files with 942 additions and 250 deletions.
9 changes: 6 additions & 3 deletions Sources/NetworkUI/ErrorLayer/ErrorConfigurations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import SwiftUI

public protocol ErrorConfigurations {
func shouldDisplay(_ error: Error) -> Bool
func shouldRetry(_ error: Error) -> Int?
func shouldRetry(_ error: Error) -> Bool
func handle(_ error: Error)
}

public extension ErrorConfigurations {
func shouldDisplay(_ error: Error) -> Bool {
return true
}
func shouldRetry(_ error: Error) -> Int? {
return nil
func shouldRetry(_ error: Error) -> Bool {
return true
}
func handle(_ error: Error) {
}
Expand All @@ -29,4 +29,7 @@ public struct NetworkError: Error, Identifiable, Equatable, Hashable, Codable {
public var title: String?
public var body: String?
public static let cancelled = NetworkError(title: "NetworkUI Cancelled", body: "NetworkUI Cancelled")
public static func unnaceptable(status: ResponseStatus) -> Self {
NetworkError(title: "Unnaceptable Status Code", body: status.description)
}
}
47 changes: 23 additions & 24 deletions Sources/NetworkUI/Network.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,52 @@ import Combine

public actor Network {
internal static var configurations: NetworkConfigurations!
public static func set(configurations: NetworkConfigurations) {
Network.configurations = configurations
}
//MARK: - Request Builder
internal static func requestBuilder<T: EndPoint>(endPoint: T) throws -> URLRequest {
guard let baseURL = endPoint.baseURL ?? configurations.baseURL else {
var requestURL: URL?
if let baseURL = endPoint.baseURL {
let reproccessed = endPoint.route.reproccessed(with: endPoint.reprocess(url: endPoint.route.applied(to: baseURL)))
requestURL = reproccessed
}else if let baseURL = configurations.baseURL {
let reproccessed = endPoint.route.reproccessed(with: configurations.reprocess(url: endPoint.route.applied(to: baseURL)))
requestURL = reproccessed
}else {
throw NetworkError(title: "Error", body: "No Base URL found!")
}
guard let requestURL = endPoint.route.reproccessed(with: configurations.reprocess(url: endPoint.route.applied(to: baseURL))) else {
guard let requestURL else {
throw NetworkError(title: "Error", body: "The constructed URL is invalid!")
}
var request = URLRequest(url: requestURL, timeoutInterval: configurations.timeoutInterval)
request.httpMethod = endPoint.method.rawValue
request.cachePolicy = configurations.cachePolicy
request.configure(headers: endPoint.headers)
if let data = endPoint.body?.data {
request.httpBody = data
}
return request
}
//MARK: - Result Builder
internal static func resultBuilder<Model: Codable>(_ data: Data, model: Model.Type, withLoader: Bool, request: URLRequest) throws -> Model {
internal static func resultBuilder<T: EndPoint, Model: Decodable, ErrorModel: Error & Decodable>(call: NetworkCall<Model, ErrorModel, T>, request: URLRequest, data: Data) async throws -> Model {
print("------Begin Request------")
print(request.cURL(pretty: true))
print("------End Request------")
print("------Begin Response------")
print(data.prettyPrinted)
print("------End Response------")
if withLoader {
NetworkData.shared.isLoading = false
if call.handler.withLoading {
await NetworkData.shared.set(loading: false)
}
if call.errorModel != nil {
guard let model = try? configurations.decoder.decode(Model.self, from: data) else {
let errorModel = try configurations.decoder.decode(ErrorModel.self, from: data)
throw errorModel
}
return model
}
return try configurations.decoder.decode(Model.self, from: data)
}
public static func set(configurations: NetworkConfigurations) {
Network.configurations = configurations
}
}

public struct BaseResponse<T: Codable>: Codable {
//MARK: - Properties
public var error: Bool
public var body: String?
public var title: String?
public var data: T?
public enum CodingKeys: String, CodingKey {
case error, body, title, data
}
public static func error(_ error: NetworkError?) -> BaseResponse<T> {
return BaseResponse(error: true, body: error?.body, title: error?.title)
}
public static func error(body: String, title: String) -> BaseResponse<T> {
return BaseResponse(error: true, body: body, title: title)
}
}
12 changes: 8 additions & 4 deletions Sources/NetworkUI/NetworkConfigurations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,26 @@ public protocol NetworkConfigurations {
var timeoutInterval: TimeInterval {get}
var baseURL: URL? {get}
var retryCount: Int {get}
var cachePolicy: URLRequest.CachePolicy {get}
var decoder: JSONDecoder {get}
func reprocess(url: URL?) -> URL?
}

public extension NetworkConfigurations {
var timeoutInterval: TimeInterval {
90
return 90
}
var baseURL: URL? {
nil
return nil
}
var retryCount: Int {
1
return 1
}
var cachePolicy: URLRequest.CachePolicy {
return .returnCacheDataElseLoad
}
var decoder: JSONDecoder {
JSONDecoder()
return JSONDecoder()
}
func reprocess(url: URL?) -> URL? {
return url
Expand Down
63 changes: 0 additions & 63 deletions Sources/NetworkUI/NetworkRequests/AsyncNetwork.swift

This file was deleted.

41 changes: 0 additions & 41 deletions Sources/NetworkUI/NetworkRequests/CombineNetwork.swift

This file was deleted.

41 changes: 41 additions & 0 deletions Sources/NetworkUI/NetworkRequests/ErrorBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// File.swift
//
//
// Created by Joe Maghzal on 11/13/22.
//

import Foundation

extension Network {
internal static func errorBuilder<T: EndPoint, Model: Decodable, ErrorModel: Error & Decodable>(call: NetworkCall<Model, ErrorModel, T>, error: Error) async throws -> Model {
if call.handler.withLoading {
await NetworkData.shared.set(loading: false)
}
switch call.policy {
case .never:
guard call.handler.withError, configurations.errorLayer.shouldDisplay(error) else {
throw error
}
if let networkError = error as? NetworkError {
NetworkData.shared.error = networkError
}else {
NetworkData.shared.error = NetworkError(title: "Something went wrong!", body: error.localizedDescription)
}
throw error
case .always:
let retryCount = call.endPoint.retryCount ?? configurations.retryCount
let currentCount = NetworkData.shared.retries[call.endPoint.id.description] ?? 0
guard retryCount > currentCount, configurations.errorLayer.shouldRetry(error) else {
throw error
}
return try await call.get()
case .custom(let number):
let currentCount = NetworkData.shared.retries[call.endPoint.id.description] ?? 0
guard number > currentCount, configurations.errorLayer.shouldRetry(error) else {
throw error
}
return try await call.get()
}
}
}
49 changes: 49 additions & 0 deletions Sources/NetworkUI/NetworkRequests/NetworkCall.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// NetworkCall.swift
//
//
// Created by Joe Maghzal on 05/02/2023.
//

import Foundation

public struct NetworkCall<Model: Decodable, ErrorModel: Error & Decodable, Route: EndPoint> {
//MARK: - Properties
internal var handler = NetworkHandler.all
internal var resultModel: Model.Type?
internal var errorModel: ErrorModel.Type?
internal var endPoint: Route
internal var validCode: ((ResponseStatus) throws -> Bool)?
internal var map: ((ResponseStatus) throws -> Model)?
internal var policy = NetworkRetries.always
}

//MARK: - Modifiers
public extension NetworkCall {
func tryDecode<T: Decodable>(using type: T.Type) -> NetworkCall<T, ErrorModel, Route> {
return NetworkCall<T, ErrorModel, Route>(handler: handler, resultModel: type, errorModel: errorModel, endPoint: endPoint, validCode: validCode, map: nil, policy: policy)
}
func tryCatch<T: Error & Decodable>(using type: T.Type) -> NetworkCall<Model, T, Route> {
return NetworkCall<Model, T, Route>(handler: handler, resultModel: resultModel, errorModel: type, endPoint: endPoint, validCode: validCode, map: map, policy: policy)
}
func handling(_ handler: NetworkHandler) -> Self {
return NetworkCall(handler: handler, resultModel: resultModel, errorModel: errorModel, endPoint: endPoint, validCode: validCode, map: map, policy: policy)
}
func validate(_ transform: @escaping (ResponseStatus) throws -> Bool) rethrows -> Self {
return NetworkCall(handler: handler, resultModel: resultModel, errorModel: errorModel, endPoint: endPoint, validCode: transform, map: map, policy: policy)
}
func retryPolicy(_ policy: NetworkRetries) -> Self {
return NetworkCall(handler: handler, resultModel: resultModel, errorModel: errorModel, endPoint: endPoint, validCode: validCode, map: map, policy: policy)
}
func map<T: Decodable>(_ transform: @escaping (ResponseStatus) throws -> T) rethrows -> NetworkCall<T, ErrorModel, Route> {
return NetworkCall<T, ErrorModel, Route>(handler: handler, resultModel: nil, errorModel: errorModel, endPoint: endPoint, validCode: validCode, map: transform, policy: policy)
}
func get() async throws -> Model {
return try await Network.request(call: self)
}
func task() async throws -> Task<Model, Error> {
return Task { () -> Model in
try await get()
}
}
}
37 changes: 37 additions & 0 deletions Sources/NetworkUI/NetworkRequests/Request.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// File.swift
//
//
// Created by Joe Maghzal on 05/02/2023.
//

import Foundation

extension Network {
nonisolated internal static func request<T: EndPoint, Model: Decodable, ErrorModel: Error & Decodable>(call: NetworkCall<Model, ErrorModel, T>) async throws -> Model {
do {
NetworkData.shared.add(call.endPoint.id.description)
let request = try requestBuilder(endPoint: call.endPoint)
if call.handler.withLoading {
await NetworkData.shared.set(loading: true)
}
let networkResult = try await URLSession.shared.data(for: request)
guard !Task.isCancelled else {
throw NetworkError.cancelled
}
let status = ResponseStatus(statusCode: (networkResult.1 as? HTTPURLResponse)?.statusCode ?? 0)
if let validity = call.validCode, !(try validity(status)) {
throw NetworkError.unnaceptable(status: status)
}
if let map = call.map {
return try map(status)
}
return try await resultBuilder(call: call, request: request, data: networkResult.0)
}catch {
return try await errorBuilder(call: call, error: error)
}
}
nonisolated public static func request<T: EndPoint>(for endPoint: T) async throws -> NetworkCall<EmptyData, EmptyData, T> {
return NetworkCall(endPoint: endPoint)
}
}
Loading

0 comments on commit b546f28

Please sign in to comment.