Skip to content

Commit 5a5f904

Browse files
committed
Initial commit
1 parent 57bc124 commit 5a5f904

11 files changed

Lines changed: 842 additions & 0 deletions

Package.resolved

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// swift-tools-version: 5.10
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "NetworkManager",
8+
platforms: [.iOS(.v13)],
9+
products: [
10+
// Products define the executables and libraries a package produces, making them visible to other packages.
11+
.library(
12+
name: "NetworkManager",
13+
targets: ["NetworkManager"]),
14+
],
15+
dependencies: [
16+
.package(url: "https://github.com/Alamofire/Alamofire", exact: "5.8.0"),
17+
.package(url: "https://github.com/konkab/AlamofireNetworkActivityLogger", exact: "3.4.0")
18+
],
19+
targets: [
20+
// Targets are the basic building blocks of a package, defining a module or a test suite.
21+
// Targets can depend on other targets in this package and products from dependencies.
22+
.target(
23+
name: "NetworkManager",
24+
dependencies: ["Alamofire", "AlamofireNetworkActivityLogger"]
25+
),
26+
.testTarget(
27+
name: "NetworkManagerTests",
28+
dependencies: ["NetworkManager"],
29+
resources: [.process("Resources")]),
30+
]
31+
)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# NetworkManager
2+
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
//
2+
// HTTPURLResponse.swift
3+
// NetworkManager
4+
//
5+
// Created by ahmed suliman on 19/07/2022.
6+
//
7+
8+
import Foundation
9+
10+
public enum NetworkError: Error {
11+
case nullData
12+
case decodingError
13+
case data
14+
case offline
15+
case invalidURL
16+
case undefined
17+
case custom(Error)
18+
19+
}
20+
21+
public enum HTTPStatusCode: Error {
22+
/// The response class representation of status codes, these get grouped by their first digit.
23+
enum ResponseType: Equatable, Error {
24+
/// - informational: This class of status code indicates a provisional response, consisting only of the Status-Line and optional headers, and is terminated by an empty line.
25+
case informational(Informational)
26+
/// - success: This class of status codes indicates the action requested by the client was received, understood, accepted, and processed successfully.
27+
case success(Success)
28+
/// - redirection: This class of status code indicates the client must take additional action to complete the request.
29+
case redirection(Redirection)
30+
/// - clientError: This class of status code is intended for situations in which the client seems to have erred.
31+
case clientError(ClientError)
32+
/// - serverError: This class of status code indicates the server failed to fulfill an apparently valid request.
33+
case serverError(ServerError)
34+
/// - undefined: The class of the status code cannot be resolved.
35+
case undefined
36+
37+
init(code: Int) {
38+
switch code {
39+
case (100...199):
40+
self = .informational(HTTPStatusCode.Informational(rawValue: code) ?? .undefined)
41+
case (200...299):
42+
self = .success(HTTPStatusCode.Success(rawValue: code) ?? .undefined)
43+
case (300...399):
44+
self = .redirection(HTTPStatusCode.Redirection(rawValue: code) ?? .undefined)
45+
case (400...499):
46+
self = .clientError(HTTPStatusCode.ClientError(rawValue: code) ?? .undefined)
47+
case (500...599):
48+
self = .serverError(HTTPStatusCode.ServerError(rawValue: code) ?? .undefined)
49+
default:
50+
self = .undefined
51+
}
52+
}
53+
54+
var pass: Bool {
55+
switch self {
56+
case .success, .redirection, .clientError: return true
57+
default: return false
58+
}
59+
}
60+
}
61+
62+
//
63+
// Informational - 1xx
64+
//
65+
enum Informational: Int {
66+
/// - continue: The server has received the request headers and the client should proceed to send the request body.
67+
case `continue` = 100
68+
/// - switchingProtocols: The requester has asked the server to switch protocols and the server has agreed to do so.
69+
case switchingProtocols = 101
70+
/// - processing: This code indicates that the server has received and is processing the request, but no response is available yet.
71+
case processing = 102
72+
case undefined = 0
73+
}
74+
//
75+
// Success - 2xx
76+
//
77+
78+
enum Success: Int {
79+
/// - ok: Standard response for successful HTTP requests.
80+
case ok = 200
81+
/// - created: The request has been fulfilled, resulting in the creation of a new resource.
82+
case created = 201
83+
/// - accepted: The request has been accepted for processing, but the processing has not been completed.
84+
case accepted = 202
85+
/// - nonAuthoritativeInformation: The server is a transforming proxy (e.g. a Web accelerator) that received a 200 OK from its origin,
86+
/// but is returning a modified version of the origin's response.
87+
case nonAuthoritativeInformation = 203
88+
/// - noContent: The server successfully processed the request and is not returning any content.
89+
case noContent = 204
90+
/// - resetContent: The server successfully processed the request, but is not returning any content.
91+
case resetContent = 205
92+
/// - partialContent: The server is delivering only part of the resource (byte serving) due to a range header sent by the client.
93+
case partialContent = 206
94+
/// - multiStatus: The message body that follows is an XML message and can contain a number of separate response codes, depending on how many sub-requests were made.
95+
case multiStatus = 207
96+
/// - alreadyReported: The members of a DAV binding have already been enumerated in a previous reply to this request, and are not being included again.
97+
case alreadyReported = 208
98+
/// - IMUsed: The server has fulfilled a request for the resource, and the response is a representation of the result of one or more instance-manipulations applied to the current instance.
99+
case IMUsed = 226
100+
case undefined = 0
101+
}
102+
//
103+
// Redirection - 3xx
104+
//
105+
106+
enum Redirection: Int {
107+
/// - multipleChoices: Indicates multiple options for the resource from which the client may choose
108+
case multipleChoices = 300
109+
/// - movedPermanently: This and all future requests should be directed to the given URI.
110+
case movedPermanently = 301
111+
/// - found: The resource was found.
112+
case found = 302
113+
/// - seeOther: The response to the request can be found under another URI using a GET method.
114+
case seeOther = 303
115+
/// - notModified: Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-None-Match.
116+
case notModified = 304
117+
/// - useProxy: The requested resource is available only through a proxy, the address for which is provided in the response.
118+
case useProxy = 305
119+
/// - switchProxy: No longer used. Originally meant "Subsequent requests should use the specified proxy.
120+
case switchProxy = 306
121+
/// - temporaryRedirect: The request should be repeated with another URI.
122+
case temporaryRedirect = 307
123+
/// - permenantRedirect: The request and all future requests should be repeated using another URI.
124+
case permenantRedirect = 308
125+
case undefined = 0
126+
}
127+
128+
//
129+
// Client Error - 4xx
130+
//
131+
132+
enum ClientError: Int {
133+
/// - badRequest: The server cannot or will not process the request due to an apparent client error.
134+
case badRequest = 400
135+
/// - unauthorized: Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided.
136+
case unauthorized = 401
137+
/// - paymentRequired: The content available on the server requires payment.
138+
case paymentRequired = 402
139+
/// - forbidden: The request was a valid request, but the server is refusing to respond to it.
140+
case forbidden = 403
141+
/// - notFound: The requested resource could not be found but may be available in the future.
142+
case notFound = 404
143+
/// - methodNotAllowed: A request method is not supported for the requested resource. e.g. a GET request on a form which requires data to be presented via POST
144+
case methodNotAllowed = 405
145+
/// - notAcceptable: The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request.
146+
case notAcceptable = 406
147+
/// - proxyAuthenticationRequired: The client must first authenticate itself with the proxy.
148+
case proxyAuthenticationRequired = 407
149+
/// - requestTimeout: The server timed out waiting for the request.
150+
case requestTimeout = 408
151+
/// - conflict: Indicates that the request could not be processed because of conflict in the request, such as an edit conflict between multiple simultaneous updates.
152+
case conflict = 409
153+
/// - gone: Indicates that the resource requested is no longer available and will not be available again.
154+
case gone = 410
155+
/// - lengthRequired: The request did not specify the length of its content, which is required by the requested resource.
156+
case lengthRequired = 411
157+
/// - preconditionFailed: The server does not meet one of the preconditions that the requester put on the request.
158+
case preconditionFailed = 412
159+
/// - payloadTooLarge: The request is larger than the server is willing or able to process.
160+
case payloadTooLarge = 413
161+
/// - URITooLong: The URI provided was too long for the server to process.
162+
case URITooLong = 414
163+
/// - unsupportedMediaType: The request entity has a media type which the server or resource does not support.
164+
case unsupportedMediaType = 415
165+
/// - rangeNotSatisfiable: The client has asked for a portion of the file (byte serving), but the server cannot supply that portion.
166+
case rangeNotSatisfiable = 416
167+
/// - expectationFailed: The server cannot meet the requirements of the Expect request-header field.
168+
case expectationFailed = 417
169+
/// - teapot: This HTTP status is used as an Easter egg in some websites.
170+
case teapot = 418
171+
/// - misdirectedRequest: The request was directed at a server that is not able to produce a response.
172+
case misdirectedRequest = 421
173+
/// - unprocessableEntity: The request was well-formed but was unable to be followed due to semantic errors.
174+
case unprocessableEntity = 422
175+
/// - locked: The resource that is being accessed is locked.
176+
case locked = 423
177+
/// - failedDependency: The request failed due to failure of a previous request (e.g., a PROPPATCH).
178+
case failedDependency = 424
179+
/// - upgradeRequired: The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field.
180+
case upgradeRequired = 426
181+
/// - preconditionRequired: The origin server requires the request to be conditional.
182+
case preconditionRequired = 428
183+
/// - tooManyRequests: The user has sent too many requests in a given amount of time.
184+
case tooManyRequests = 429
185+
/// - requestHeaderFieldsTooLarge: The server is unwilling to process the request because either an individual header field, or all the header fields collectively, are too large.
186+
case requestHeaderFieldsTooLarge = 431
187+
/// - noResponse: Used to indicate that the server has returned no information to the client and closed the connection.
188+
case noResponse = 444
189+
/// - unavailableForLegalReasons: A server operator has received a legal demand to deny access to a resource or to a set of resources that includes the requested resource.
190+
case unavailableForLegalReasons = 451
191+
/// - SSLCertificateError: An expansion of the 400 Bad Request response code, used when the client has provided an invalid client certificate.
192+
case SSLCertificateError = 495
193+
/// - SSLCertificateRequired: An expansion of the 400 Bad Request response code, used when a client certificate is required but not provided.
194+
case SSLCertificateRequired = 496
195+
/// - HTTPRequestSentToHTTPSPort: An expansion of the 400 Bad Request response code, used when the client has made a HTTP request to a port listening for HTTPS requests.
196+
case HTTPRequestSentToHTTPSPort = 497
197+
/// - clientClosedRequest: Used when the client has closed the request before the server could send a response.
198+
case clientClosedRequest = 499
199+
case undefined
200+
}
201+
202+
//
203+
// Server Error - 5xx
204+
//
205+
206+
enum ServerError: Int {
207+
/// - internalServerError: A generic error message, given when an unexpected condition was encountered and no more specific message is suitable.
208+
case internalServerError = 500
209+
/// - notImplemented: The server either does not recognize the request method, or it lacks the ability to fulfill the request.
210+
case notImplemented = 501
211+
/// - badGateway: The server was acting as a gateway or proxy and received an invalid response from the upstream server.
212+
case badGateway = 502
213+
/// - serviceUnavailable: The server is currently unavailable (because it is overloaded or down for maintenance). Generally, this is a temporary state.
214+
case serviceUnavailable = 503
215+
/// - gatewayTimeout: The server was acting as a gateway or proxy and did not receive a timely response from the upstream server.
216+
case gatewayTimeout = 504
217+
/// - HTTPVersionNotSupported: The server does not support the HTTP protocol version used in the request.
218+
case HTTPVersionNotSupported = 505
219+
/// - variantAlsoNegotiates: Transparent content negotiation for the request results in a circular reference.
220+
case variantAlsoNegotiates = 506
221+
/// - insufficientStorage: The server is unable to store the representation needed to complete the request.
222+
case insufficientStorage = 507
223+
/// - loopDetected: The server detected an infinite loop while processing the request.
224+
case loopDetected = 508
225+
/// - notExtended: Further extensions to the request are required for the server to fulfill it.
226+
case notExtended = 510
227+
/// - networkAuthenticationRequired: The client needs to authenticate to gain network access.
228+
case networkAuthenticationRequired = 511
229+
/// The class (or group) which the status code belongs to.
230+
case undefined = 0
231+
}
232+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//
2+
// NetworkManager.swift
3+
// NetworkManager
4+
//
5+
// Created by ahmed suliman on 17/10/2023.
6+
//
7+
8+
import Foundation
9+
import class Alamofire.Session
10+
import class Alamofire.ServerTrustManager
11+
import class Alamofire.DisabledTrustEvaluator
12+
import struct Alamofire.HTTPHeaders
13+
import enum Alamofire.AFError
14+
import AlamofireNetworkActivityLogger
15+
16+
public enum RequestParameter {
17+
case dict([String: Any])
18+
case queryItem([URLQueryItem])
19+
case object(Codable)
20+
}
21+
22+
public enum RequestQuery {
23+
case path
24+
case json
25+
}
26+
27+
public protocol RequestData {
28+
associatedtype ResponseType: Decodable
29+
30+
var baseUrl: URL? { get }
31+
var endPoint: String? { get }
32+
var headers: HTTPHeaders { get }
33+
var method: NetworkMethod { get }
34+
var query: RequestQuery { get }
35+
var parameters: RequestParameter { get }
36+
}
37+
38+
public protocol NetworkManagerProtocol {
39+
func request<T: RequestData>(req: T) async -> Swift.Result<T.ResponseType, Error>
40+
}
41+
42+
public class NetworkManager: NetworkManagerProtocol {
43+
44+
public static let shared: NetworkManagerProtocol = NetworkManager()
45+
46+
private init() {
47+
#if DEBUG
48+
NetworkActivityLogger.shared.level = .debug
49+
NetworkActivityLogger.shared.startLogging()
50+
#endif
51+
}
52+
53+
private func session(host: String) -> Session {
54+
let manager = ServerTrustManager(evaluators: [host: DisabledTrustEvaluator()])
55+
let configuration = URLSessionConfiguration.af.default
56+
return Session(configuration: configuration, serverTrustManager: manager)
57+
}
58+
59+
public func request<T: RequestData>(req: T) async -> Swift.Result<T.ResponseType, Error> {
60+
guard let request = URLRequest.prepareRequest(req: req) else {
61+
return .failure(NetworkError.invalidURL)
62+
}
63+
64+
let session = session(host: req.baseUrl?.host ?? "")
65+
let task = session.request(request).serializingDecodable(T.ResponseType.self)
66+
67+
guard let httpResponse = await task.response.response,
68+
case let httpStatus = HTTPStatusCode.ResponseType(code: httpResponse.statusCode),
69+
httpStatus.pass else {
70+
71+
if let httpResponse = await task.response.response {
72+
return .failure(HTTPStatusCode.ResponseType(code: httpResponse.statusCode))
73+
}
74+
75+
return .failure(HTTPStatusCode.ResponseType.undefined)
76+
}
77+
78+
switch await task.result {
79+
case .success(let data):
80+
return .success(data)
81+
82+
case .failure(let error):
83+
return .failure(error)
84+
}
85+
86+
}
87+
}

0 commit comments

Comments
 (0)