From 82aaae3fbdb43b8c8afcbf0c579bc273b83e28e7 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Mon, 21 Aug 2023 21:48:03 +0300 Subject: [PATCH 01/95] feat: add decoding capability in case of failure caused due to HTTP status code Resolves: none. --- RxNetworkKit.xcodeproj/project.pbxproj | 10 +++++- ...ve+URLSessionAdaptedDownloadResponse.swift | 10 +++--- ...tive+URLSessionAdaptedUploadResponse.swift | 10 +++--- Source/Error/NetworkClientError.swift | 2 +- Source/Error/NetworkError.swift | 13 +++++--- Source/Error/NetworkServerError.swift | 2 +- Source/HTTP/DefaultHTTPErrorBody.swift | 14 ++++++++ Source/HTTP/HTTPErrorBody.swift | 10 ++++++ Source/Manager/NetworkManager.swift | 30 ++++++++++------- .../Observable/Observable+Decodable.swift | 22 +++++++------ .../Single/Single+Decodable.swift | 33 ++++++++++--------- .../Single/Single+VerifyResponse.swift | 4 +-- 12 files changed, 106 insertions(+), 54 deletions(-) create mode 100644 Source/HTTP/DefaultHTTPErrorBody.swift create mode 100644 Source/HTTP/HTTPErrorBody.swift diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index 371acf1..2e37ca2 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -57,6 +57,8 @@ 0B77E0BB29D968DE0077FBC0 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BA29D968DE0077FBC0 /* RxRelay */; }; 0B77E0BD29D968DE0077FBC0 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BC29D968DE0077FBC0 /* RxSwift */; }; 0B77E0C029D969370077FBC0 /* RxSwiftExt in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BF29D969370077FBC0 /* RxSwiftExt */; }; + C6A9BEF62A93E2AE00459E32 /* DefaultHTTPErrorBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */; }; + C6A9BEF82A93E2D600459E32 /* HTTPErrorBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -108,6 +110,8 @@ 0B77E08629D965D30077FBC0 /* NetworkDownloadRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkDownloadRouter.swift; sourceTree = ""; }; 0B77E08729D965D30077FBC0 /* NetworkRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkRouter.swift; sourceTree = ""; }; 0B77E08829D965D30077FBC0 /* NetworkUploadRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkUploadRouter.swift; sourceTree = ""; }; + C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultHTTPErrorBody.swift; sourceTree = ""; }; + C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPErrorBody.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -215,11 +219,13 @@ 0B77E06129D965D30077FBC0 /* HTTP */ = { isa = PBXGroup; children = ( + C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */, + C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */, 0B77E06229D965D30077FBC0 /* HTTPMIMEType.swift */, 0B77E06329D965D30077FBC0 /* HTTPMethod.swift */, 0B77E06429D965D30077FBC0 /* HTTPScheme.swift */, - 0B77E06529D965D30077FBC0 /* HTTPURLResponse+StatusCode.swift */, 0B77E06629D965D30077FBC0 /* HTTPStatusCode.swift */, + 0B77E06529D965D30077FBC0 /* HTTPURLResponse+StatusCode.swift */, ); path = HTTP; sourceTree = ""; @@ -416,6 +422,7 @@ 0B77E0B529D965D30077FBC0 /* NetworkRouter.swift in Sources */, 0B77E09529D965D30077FBC0 /* RequestRetryPolicy.swift in Sources */, 0B77E0B329D965D30077FBC0 /* Reactive+Curl.swift in Sources */, + C6A9BEF82A93E2D600459E32 /* HTTPErrorBody.swift in Sources */, 0B77E0A929D965D30077FBC0 /* NetworkError.swift in Sources */, 0B77E09F29D965D30077FBC0 /* NWPath+InterfaceType.swift in Sources */, 0B77E09C29D965D30077FBC0 /* HTTPScheme.swift in Sources */, @@ -442,6 +449,7 @@ 0B77E08B29D965D30077FBC0 /* URLSession+DownloadTask.swift in Sources */, 0B77E0A729D965D30077FBC0 /* NetworkServerError.swift in Sources */, 0B77E0B629D965D30077FBC0 /* NetworkUploadRouter.swift in Sources */, + C6A9BEF62A93E2AE00459E32 /* DefaultHTTPErrorBody.swift in Sources */, 0B77E0AB29D965D30077FBC0 /* Single+Retry.swift in Sources */, 0B77E08F29D965D30077FBC0 /* URLSession+UploadTask.swift in Sources */, 0B77E08C29D965D30077FBC0 /* Reactive+URLSessionAdaptedDownloadResponse.swift in Sources */, diff --git a/Source/Custom Requests/Download/Reactive+URLSessionAdaptedDownloadResponse.swift b/Source/Custom Requests/Download/Reactive+URLSessionAdaptedDownloadResponse.swift index 63318e6..99adaba 100644 --- a/Source/Custom Requests/Download/Reactive+URLSessionAdaptedDownloadResponse.swift +++ b/Source/Custom Requests/Download/Reactive+URLSessionAdaptedDownloadResponse.swift @@ -14,10 +14,11 @@ extension Reactive where Base: URLSession { /// /// - Parameters: /// - request: `URLRequest` used to create upload task and its observables. + /// - httpErrorType: `HTTPErrorBody` http error body type. /// - apiErrorType: `NetworkAPIError` type for expected error in HTTP response body. /// /// - Returns: a `Observable` object of `DownloadEvent` type. - func downloadResponse(request: URLRequest, apiErrorType: AE.Type) -> Observable { + func downloadResponse(request: URLRequest, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable { let observables = downloadResponse(request: request) let progressObservable = observables .0 @@ -25,7 +26,7 @@ extension Reactive where Base: URLSession { .asObservable() let responseObservable = observables .1 - .decodable(AE.self) + .decodable(E.self, apiErrorType: AE.self) .map { DownloadEvent.completedWithData(data: $0.data) } .asObservable() let mergedObservable = responseObservable.merge(with: progressObservable) @@ -36,10 +37,11 @@ extension Reactive where Base: URLSession { /// - Parameters: /// - request: `URLRequest` used to create upload task and its observables. /// - url: `URL` used to save downloaded file to disk. + /// - httpErrorType: `HTTPErrorBody` http error body type. /// - apiErrorType: `NetworkAPIError` type for expected error in HTTP response body. /// /// - Returns: a `Observable` object of `DownloadEvent` type. - func downloadResponse(request: URLRequest, saveTo url: URL, apiErrorType: AE.Type) -> Observable { + func downloadResponse(request: URLRequest, saveTo url: URL, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable { let observables = downloadResponse(request: request, saveTo: url) let progressObservable = observables .0 @@ -47,7 +49,7 @@ extension Reactive where Base: URLSession { .asObservable() let responseObservable = observables .1 - .decodable(AE.self) + .decodable(E.self, apiErrorType: AE.self) .map { _ in DownloadEvent.completed } .asObservable() let mergedObservable = responseObservable.merge(with: progressObservable) diff --git a/Source/Custom Requests/Upload/Reactive+URLSessionAdaptedUploadResponse.swift b/Source/Custom Requests/Upload/Reactive+URLSessionAdaptedUploadResponse.swift index 3c53b84..88ae8a4 100644 --- a/Source/Custom Requests/Upload/Reactive+URLSessionAdaptedUploadResponse.swift +++ b/Source/Custom Requests/Upload/Reactive+URLSessionAdaptedUploadResponse.swift @@ -16,10 +16,11 @@ extension Reactive where Base: URLSession { /// - request: `URLRequest` used to create upload task and its observables. /// - file: `UploadFile` object to be uploaded. /// - modelType: `Decodable` type for model in HTTP response body. + /// - httpErrorType: `HTTPErrorBody` http error body type. /// - apiErrorType: `NetworkAPIError` type for expected error in HTTP response body. /// /// - Returns: a `Observable` object of `UploadEvent` type. - func uploadResponse(request: URLRequest, file: UploadFile, modelType: T.Type, apiErrorType: AE.Type) -> Observable> { + func uploadResponse(request: URLRequest, file: UploadFile, modelType: T.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable> { let observables = uploadResponse(request: request, file: file) let progressObservable = observables .0 @@ -27,7 +28,7 @@ extension Reactive where Base: URLSession { .asObservable() let responseObservable = observables .1 - .decodable(T.self, errorType: AE.self) + .decodable(T.self, httpErrorType: E.self, apiErrorType: AE.self) .map { UploadEvent.completed(model: $0) } .asObservable() let mergedObservable = responseObservable.merge(with: progressObservable) @@ -39,10 +40,11 @@ extension Reactive where Base: URLSession { /// - request: `URLRequest` used to create upload task and its observables. /// - formData: `UploadFormData` object that includes parameters and files to be uploaded. /// - modelType: `Decodable` type for model in HTTP response body. + /// - httpErrorType: `HTTPErrorBody` http error body type. /// - apiErrorType: `NetworkAPIError` type for expected error in HTTP response body. /// /// - Returns: a `Observable` object of `UploadEvent` type. - func uploadResponse(request: URLRequest, formData: UploadFormData, modelType: T.Type, apiErrorType: AE.Type) -> Observable> { + func uploadResponse(request: URLRequest, formData: UploadFormData, modelType: T.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable> { let observables = uploadResponse(request: request, formData: formData) let progressObservable = observables .0 @@ -50,7 +52,7 @@ extension Reactive where Base: URLSession { .asObservable() let responseObservable = observables .1 - .decodable(T.self, errorType: AE.self) + .decodable(T.self, httpErrorType: E.self, apiErrorType: AE.self) .map { UploadEvent.completed(model: $0) } .asObservable() let mergedObservable = responseObservable.merge(with: progressObservable) diff --git a/Source/Error/NetworkClientError.swift b/Source/Error/NetworkClientError.swift index 42623bf..b0e8ee4 100644 --- a/Source/Error/NetworkClientError.swift +++ b/Source/Error/NetworkClientError.swift @@ -7,7 +7,7 @@ /// Client-side (transport) error public enum NetworkClientError: Error { - case http(HTTPStatusCode) + case http(HTTPStatusCode, HTTPErrorBody?) case serialization(Error) case transport(Error) } diff --git a/Source/Error/NetworkError.swift b/Source/Error/NetworkError.swift index 279b1c4..bcb136f 100644 --- a/Source/Error/NetworkError.swift +++ b/Source/Error/NetworkError.swift @@ -14,15 +14,20 @@ public enum NetworkError: Error { /// Creates `NetworkError` instance. /// /// - Parameter response: `HTTPURLResponse` used to get response status code. - init?(_ response: HTTPURLResponse?) { + init?(_ response: HTTPURLResponse?, data: Data?, errorType: E.Type) { if let response = response, - let httpStatusCode = HTTPStatusCode(rawValue: response.statusCode) { + let httpStatusCode = response.status { + // Get Error body from response data if possible. + var httpErrorBody: E? + if let data = data { + httpErrorBody = try? JSONDecoder().decode(errorType.self, from: data) + } // Get Error from response status code switch httpStatusCode.responseType { case .clientError: - self = .client(.http(httpStatusCode)) + self = .client(.http(httpStatusCode, httpErrorBody)) case .serverError: - self = .server(.http(httpStatusCode)) + self = .server(.http(httpStatusCode, httpErrorBody)) default: return nil } diff --git a/Source/Error/NetworkServerError.swift b/Source/Error/NetworkServerError.swift index 75a80a1..6c059c7 100644 --- a/Source/Error/NetworkServerError.swift +++ b/Source/Error/NetworkServerError.swift @@ -7,6 +7,6 @@ /// Server-side error public enum NetworkServerError: Error { - case http(HTTPStatusCode) + case http(HTTPStatusCode, HTTPErrorBody?) case generic(Error) } diff --git a/Source/HTTP/DefaultHTTPErrorBody.swift b/Source/HTTP/DefaultHTTPErrorBody.swift new file mode 100644 index 0000000..68f849f --- /dev/null +++ b/Source/HTTP/DefaultHTTPErrorBody.swift @@ -0,0 +1,14 @@ +// +// DefaultHTTPErrorBody.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 21/08/2023. +// + +import Foundation + +struct DefaultHTTPErrorBody: HTTPErrorBody { + let statusCode: Int? + let message: String? + let supportId: String? +} diff --git a/Source/HTTP/HTTPErrorBody.swift b/Source/HTTP/HTTPErrorBody.swift new file mode 100644 index 0000000..0366e4b --- /dev/null +++ b/Source/HTTP/HTTPErrorBody.swift @@ -0,0 +1,10 @@ +// +// HTTPErrorBody.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 21/08/2023. +// + +import Foundation + +public protocol HTTPErrorBody: Decodable { } diff --git a/Source/Manager/NetworkManager.swift b/Source/Manager/NetworkManager.swift index b45c3ae..ac8dc31 100644 --- a/Source/Manager/NetworkManager.swift +++ b/Source/Manager/NetworkManager.swift @@ -30,10 +30,11 @@ public class NetworkManager { /// /// - Parameters: /// - router: `Router` object used to create request. + /// - httpErrorType: `HTTPErrorBody` http error body type. /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. /// /// - Returns: `Completable` observable encapsulating data request. - public func request(_ router: NetworkRouter, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Completable { + public func request(_ router: NetworkRouter, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Completable { let originalRequest = router.asURLRequest() let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) @@ -45,7 +46,7 @@ public class NetworkManager { let observable = session .rx .response(request: adaptedRequest) - .decodable(AE.self) + .decodable(E.self, apiErrorType: AE.self) .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } @@ -54,10 +55,11 @@ public class NetworkManager { /// /// - Parameters: /// - router: `Router` object used to create request. + /// - httpErrorType: `HTTPErrorBody` http error body type. /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. /// /// - Returns: `Single` observable encapsulating data request. - public func request(_ router: NetworkRouter, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Single { + public func request(_ router: NetworkRouter, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Single { let originalRequest = router.asURLRequest() let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: session) @@ -68,7 +70,7 @@ public class NetworkManager { let observable = session .rx .response(request: adaptedRequest) - .decodable(T.self, errorType: AE.self) + .decodable(T.self, httpErrorType: E.self, apiErrorType: AE.self) .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } @@ -77,10 +79,11 @@ public class NetworkManager { /// /// - Parameters: /// - router: `Router` object used to create request. + /// - httpErrorType: `HTTPErrorBody` http error body type. /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. /// /// - Returns: `Observable` object encapsulating download request. - public func download(_ router: NetworkRouter, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable { + public func download(_ router: NetworkRouter, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable { let originalRequest = router.asURLRequest() let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: session) @@ -90,7 +93,7 @@ public class NetworkManager { } let observable = session .rx - .downloadResponse(request: adaptedRequest, apiErrorType: AE.self) + .downloadResponse(request: adaptedRequest, httpErrorType: E.self, apiErrorType: AE.self) .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } @@ -100,10 +103,11 @@ public class NetworkManager { /// - Parameters: /// - router: `Router` object used to create request. /// - fileURL: `URL` used to save downloaded file to disk. + /// - httpErrorType: `HTTPErrorBody` http error body type. /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. /// /// - Returns: `Observable` object encapsulating download request. - public func download(_ router: NetworkRouter, _ fileURL: URL, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable { + public func download(_ router: NetworkRouter, _ fileURL: URL, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable { let originalRequest = router.asURLRequest() let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: session) @@ -113,7 +117,7 @@ public class NetworkManager { } let observable = session .rx - .downloadResponse(request: adaptedRequest, saveTo: fileURL, apiErrorType: AE.self) + .downloadResponse(request: adaptedRequest, saveTo: fileURL, httpErrorType: E.self, apiErrorType: AE.self) .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } @@ -123,10 +127,11 @@ public class NetworkManager { /// - Parameters: /// - router: `Router` object used to create request. /// - file: `UploadFile` object including file details for upload. + /// - httpErrorType: `HTTPErrorBody` http error body type. /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. /// /// - Returns: `Observable` object encapsulating upload request. - public func upload(_ router: NetworkUploadRouter, _ file: UploadFile, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable> { + public func upload(_ router: NetworkUploadRouter, _ file: UploadFile, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable> { let originalRequest = router.asURLRequest() let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: session) @@ -136,7 +141,7 @@ public class NetworkManager { } let observable = session .rx - .uploadResponse(request: adaptedRequest, file: file, modelType: T.self, apiErrorType: AE.self) + .uploadResponse(request: adaptedRequest, file: file, modelType: T.self, httpErrorType: E.self, apiErrorType: AE.self) .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } @@ -146,10 +151,11 @@ public class NetworkManager { /// - Parameters: /// - router: `Router` object used to create request. /// - formData: `UploadFormData` object including parameters and files for upload. + /// - httpErrorType: `HTTPErrorBody` http error body type. /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. /// /// - Returns: `Observable` object encapsulating upload request. - public func upload(_ router: NetworkUploadRouter, _ formData: UploadFormData, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable> { + public func upload(_ router: NetworkUploadRouter, _ formData: UploadFormData, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable> { let originalRequest = router.asURLRequest() let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: session) @@ -159,7 +165,7 @@ public class NetworkManager { } let observable = session .rx - .uploadResponse(request: adaptedRequest, formData: formData, modelType: T.self, apiErrorType: AE.self) + .uploadResponse(request: adaptedRequest, formData: formData, modelType: T.self, httpErrorType: E.self, apiErrorType: AE.self) .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } diff --git a/Source/Rx Extensions/Rx+NetworkOps/Observable/Observable+Decodable.swift b/Source/Rx Extensions/Rx+NetworkOps/Observable/Observable+Decodable.swift index c8e7851..084cca8 100644 --- a/Source/Rx Extensions/Rx+NetworkOps/Observable/Observable+Decodable.swift +++ b/Source/Rx Extensions/Rx+NetworkOps/Observable/Observable+Decodable.swift @@ -12,33 +12,35 @@ extension Observable where Element == (response: HTTPURLResponse, data: Data) { /// Creates `Completable` observable + handles transport errors. /// /// - Parameters: - /// - errorType: `Decodable` api error type. + /// - httpErrorType: `Decodable` http error body type. + /// - apiErrorType: `Decodable` api error type. /// /// - Returns: Observable to be observed for values. - func decodable(_ errorType: E.Type) -> Completable { + func decodable(_ httpErrorType: E.Type, apiErrorType: AE.Type) -> Completable { asSingle() // catch any transport errors if thrown. .catchTransportError() - // verify response status code. - .verifyResponse() + // verify response status code and decode error body if possible. + .verifyResponse(E.self) // serialize data into given error type and throw it. - .decode(E.self) + .decode(AE.self) } /// Creates `Single` observable with Decodable element type + handles transport and serialization errors. /// /// - Parameters: /// - modelType: `Decodable` model type. - /// - errorType: `Decodable` api error type. + /// - httpErrorType: `Decodable` http error body type. + /// - apiErrorType: `Decodable` api error type. /// /// - Returns: Observable to be observed for values. - func decodable(_ modelType: M.Type, errorType: E.Type) -> Single { + func decodable(_ modelType: M.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Single { asSingle() // catch any transport errors if thrown. .catchTransportError() - // verify response status code. - .verifyResponse() + // verify response status code and decode error body if possible. + .verifyResponse(E.self) // serialize data into given model type or error type if serialization failed. - .decode(M.self, errorType: E.self) + .decode(M.self, errorType: AE.self) // catch any serialization errors if found. .catchSerializationError() } diff --git a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+Decodable.swift b/Source/Rx Extensions/Rx+NetworkOps/Single/Single+Decodable.swift index 07a112b..592ee9b 100644 --- a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+Decodable.swift +++ b/Source/Rx Extensions/Rx+NetworkOps/Single/Single+Decodable.swift @@ -13,48 +13,51 @@ extension PrimitiveSequence where Trait == SingleTrait, Element == (response: HT /// Creates `Completable` observable + handles transport errors. /// /// - Parameters: - /// - errorType: `Decodable` api error type. + /// - httpErrorType: `Decodable` http error body type. + /// - apiErrorType: `Decodable` api error type. /// /// - Returns: Observable to be observed for values. - func decodable(_ errorType: E.Type) -> Completable { + func decodable(_ httpErrorType: E.Type, apiErrorType: AE.Type) -> Completable { self // catch any transport errors if thrown. .catchTransportError() - // verify response status code. - .verifyResponse() + // verify response status code and decode error body if possible. + .verifyResponse(E.self) // serialize data into given error type and throw it. - .decode(E.self) + .decode(AE.self) } /// Creates `Single` observable with the same element type + handles transport errors. /// /// - Parameters: - /// - errorType: `Decodable` api error type. + /// - httpErrorType: `Decodable` http error body type. + /// - apiErrorType: `Decodable` api error type. /// /// - Returns: Observable to be observed for values. - func decodable(_ errorType: E.Type) -> Single { + func decodable(_ httpErrorType: E.Type, apiErrorType: AE.Type) -> Single { self // catch any transport errors if thrown. .catchTransportError() - // verify response status code. - .verifyResponse() + // verify response status code and decode error body if possible. + .verifyResponse(E.self) // serialize data into given error type and throw it. - .decode(E.self) + .decode(AE.self) } /// Creates `Single` observable with Decodable element type + handles transport and serialization errors. /// /// - Parameters: /// - modelType: `Decodable` model type. - /// - errorType: `Decodable` api error type. + /// - httpErrorType: `Decodable` http error body type. + /// - apiErrorType: `Decodable` api error type. /// /// - Returns: Observable to be observed for values. - func decodable(_ modelType: M.Type, errorType: E.Type) -> Single { + func decodable(_ modelType: M.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Single { self // catch any transport errors if thrown. .catchTransportError() - // verify response status code. - .verifyResponse() + // verify response status code and decode error body if possible. + .verifyResponse(E.self) // serialize data into given model type or error type if serialization failed. - .decode(M.self, errorType: E.self) + .decode(M.self, errorType: AE.self) // catch any serialization errors if found. .catchSerializationError() } diff --git a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+VerifyResponse.swift b/Source/Rx Extensions/Rx+NetworkOps/Single/Single+VerifyResponse.swift index 5b6bc48..650f548 100644 --- a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+VerifyResponse.swift +++ b/Source/Rx Extensions/Rx+NetworkOps/Single/Single+VerifyResponse.swift @@ -13,9 +13,9 @@ extension PrimitiveSequence where Trait == SingleTrait, Element == (response: HT /// Verifies response's status code. /// /// - Returns: `Single` observable to be observed for values. - func verifyResponse() -> Single { + func verifyResponse(_ httpErrorType: E.Type) -> Single { map { (response: HTTPURLResponse, data: Data) in - if let networkError = NetworkError.init(response) { + if let networkError = NetworkError.init(response, data: data, errorType: E.self) { throw networkError } else { return (response, data) From df519d51c56f2c77e45d4b29e8d1951c2cc4a0bf Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Mon, 21 Aug 2023 23:16:16 +0300 Subject: [PATCH 02/95] feat: override User-Agent HTTP header in session configuration (#26) Resolves: none. --- RxNetworkKit.xcodeproj/project.pbxproj | 12 ++++++ Source/Manager/NetworkManager.swift | 3 ++ .../ProcessInfo+operatingSystemName.swift | 37 +++++++++++++++++++ ...onfiguration+setAdditionalHTTPHeader.swift | 21 +++++++++++ ...Configuration+setUserAgentHTTPHeader.swift | 21 +++++++++++ 5 files changed, 94 insertions(+) create mode 100644 Source/Manager/ProcessInfo+operatingSystemName.swift create mode 100644 Source/Manager/URLSessionConfiguration+setAdditionalHTTPHeader.swift create mode 100644 Source/Manager/URLSessionConfiguration+setUserAgentHTTPHeader.swift diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index 2e37ca2..a644167 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -59,6 +59,9 @@ 0B77E0C029D969370077FBC0 /* RxSwiftExt in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BF29D969370077FBC0 /* RxSwiftExt */; }; C6A9BEF62A93E2AE00459E32 /* DefaultHTTPErrorBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */; }; C6A9BEF82A93E2D600459E32 /* HTTPErrorBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */; }; + C6A9BEFA2A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */; }; + C6A9BEFD2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */; }; + C6A9BEFF2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -112,6 +115,9 @@ 0B77E08829D965D30077FBC0 /* NetworkUploadRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkUploadRouter.swift; sourceTree = ""; }; C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultHTTPErrorBody.swift; sourceTree = ""; }; C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPErrorBody.swift; sourceTree = ""; }; + C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+setAdditionalHTTPHeader.swift"; sourceTree = ""; }; + C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+setUserAgentHTTPHeader.swift"; sourceTree = ""; }; + C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+operatingSystemName.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -212,6 +218,9 @@ isa = PBXGroup; children = ( 0B77E06029D965D30077FBC0 /* NetworkManager.swift */, + C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */, + C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */, + C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */, ); path = Manager; sourceTree = ""; @@ -431,6 +440,7 @@ 0B77E0AE29D965D30077FBC0 /* Single+VerifyResponse.swift in Sources */, 0B77E0B429D965D30077FBC0 /* NetworkDownloadRouter.swift in Sources */, 0B77E09A29D965D30077FBC0 /* HTTPMIMEType.swift in Sources */, + C6A9BEFF2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift in Sources */, 0B77E08E29D965D30077FBC0 /* UploadFile.swift in Sources */, 0B77E09729D965D30077FBC0 /* RequestRetrier.swift in Sources */, 0B77E09E29D965D30077FBC0 /* HTTPStatusCode.swift in Sources */, @@ -441,7 +451,9 @@ 0B77E0B129D965D30077FBC0 /* Observable+Decodable.swift in Sources */, 0B77E09929D965D30077FBC0 /* NetworkManager.swift in Sources */, 0B77E08A29D965D30077FBC0 /* DownloadEvent.swift in Sources */, + C6A9BEFD2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift in Sources */, 0B77E09129D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift in Sources */, + C6A9BEFA2A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift in Sources */, 0B77E0A629D965D30077FBC0 /* DefaultNetworkAPIError.swift in Sources */, 0B77E0A429D965D30077FBC0 /* NetworkReachability.swift in Sources */, 0B77E0B229D965D30077FBC0 /* Completable+Retry.swift in Sources */, diff --git a/Source/Manager/NetworkManager.swift b/Source/Manager/NetworkManager.swift index ac8dc31..db90b27 100644 --- a/Source/Manager/NetworkManager.swift +++ b/Source/Manager/NetworkManager.swift @@ -21,6 +21,9 @@ public class NetworkManager { /// - requestInterceptor: `NetworkRequestInterceptor` object used for intercepting requests. /// - eventMonitor: `NetworkEventMonitor` object for monitoring events for session. public init(configuration: URLSessionConfiguration, requestInterceptor: NetworkRequestInterceptor, eventMonitor: NetworkEventMonitor) { + // Apply User-Agent header as a part of HTTP aditional headers. + configuration.setUserAgentHTTPHeader() + // Initialize manager's properties. self.session = .init(configuration: configuration, delegate: eventMonitor, delegateQueue: nil) self.requestInterceptor = requestInterceptor self.eventMonitor = eventMonitor diff --git a/Source/Manager/ProcessInfo+operatingSystemName.swift b/Source/Manager/ProcessInfo+operatingSystemName.swift new file mode 100644 index 0000000..17ba7d6 --- /dev/null +++ b/Source/Manager/ProcessInfo+operatingSystemName.swift @@ -0,0 +1,37 @@ +// +// ProcessInfo+operatingSystemName.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 21/08/2023. +// + +import Foundation +#if canImport(UIKit) +import UIKit +#endif + +extension ProcessInfo { + var operatingSystemName: String { + var osName: String = "" +#if os(iOS) +#if targetEnvironment(macCatalyst) + osName = "macOS - Catalyst" +#else + if UIDevice.current.userInterfaceIdiom == .pad { + osName = "iPadOS" + } else { + osName = "iOS" + } +#endif +#elseif os(macOS) + osName = "macOS" +#elseif os(watchOS) + osName = "watchOS" +#elseif os(tvOS) + osName = "tvOS" +#elseif os(visionOS) + osName = "visionOS" +#endif + return osName + } +} diff --git a/Source/Manager/URLSessionConfiguration+setAdditionalHTTPHeader.swift b/Source/Manager/URLSessionConfiguration+setAdditionalHTTPHeader.swift new file mode 100644 index 0000000..7f4139f --- /dev/null +++ b/Source/Manager/URLSessionConfiguration+setAdditionalHTTPHeader.swift @@ -0,0 +1,21 @@ +// +// URLSessionConfiguration+setAdditionalHTTPHeader.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 21/08/2023. +// + +import Foundation + +extension URLSessionConfiguration { + /// Sets additional HTTP header for a given key and value. + /// + /// - Parameters: + /// - key: `AnyHashable` key for the HTTP header. + /// - value: `Any` value for the HTTP header. + func setAdditionalHTTPHeader(_ key: AnyHashable, value: Any) { + var modifiedAdditionalHTTPHeaders = httpAdditionalHeaders ?? [:] + modifiedAdditionalHTTPHeaders[key] = value + httpAdditionalHeaders = modifiedAdditionalHTTPHeaders + } +} diff --git a/Source/Manager/URLSessionConfiguration+setUserAgentHTTPHeader.swift b/Source/Manager/URLSessionConfiguration+setUserAgentHTTPHeader.swift new file mode 100644 index 0000000..ae43e45 --- /dev/null +++ b/Source/Manager/URLSessionConfiguration+setUserAgentHTTPHeader.swift @@ -0,0 +1,21 @@ +// +// URLSessionConfiguration+setUserAgentHTTPHeader.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 21/08/2023. +// + +import Foundation + +extension URLSessionConfiguration { + /// Sets `User-Agent` header as an additional HTTP header. + func setUserAgentHTTPHeader() { + let mainBundle = Bundle.main + let frameworkBundle = Bundle.init(for: NetworkManager.self) + let mainBundleIndentifier = mainBundle.bundleIdentifier ?? "Unknown Client Identifier" + let frameworkBundleVersion = frameworkBundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String + let osName = ProcessInfo.processInfo.operatingSystemName + let osVersion = ProcessInfo.processInfo.operatingSystemVersionString + setAdditionalHTTPHeader("User-Agent", value: "RxNetworkKit/\(frameworkBundleVersion) (\(osName) \(osVersion)-\(mainBundleIndentifier))") + } +} From 91e75caa154f529e4369fd9242e59aed464a8d58 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 24 Aug 2023 18:51:23 +0300 Subject: [PATCH 03/95] Add macOS example (#27) * feat: move iOS example project into new sub folder Resolves: none. * fix: update framework search paths Resolves: none. * feat: add empt macOS example project to workspace Resolves: none. * fix: update framework scheme name Resolves: none. * feat: add files to macOS example directory Resolves: none. * fix: apply public access modifier to DefaultHTTPErrorBody and DefaultNetworkAPIError Resolves: none. * refactor: add description comment for ProcessInfo extension Resolves: none. * fix: add RxNetworkKit bridging header reference Resolves: none. * refactor: apply project recommended settings Resolves: none. * feat: connect viewModel to tableview UI Resolves: none. * feat: complete ViewController class in macOS Example Resolves: none. * refactor: remove old un-needed file Resolves: none. * fix: apply correct image scale for error view Resolves: none. --- .../iOS Example.xcodeproj/project.pbxproj | 38 +- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/swiftpm/Package.resolved | 0 .../xcschemes/iOS Example.xcscheme | 2 +- .../iOS}/iOS Example/App/AppDelegate.swift | 0 .../AccentColor.colorset/Contents.json | 0 .../AppIcon.appiconset/Contents.json | 0 .../App/Assets.xcassets/Contents.json | 0 .../App/Base.lproj/LaunchScreen.storyboard | 0 .../iOS}/iOS Example/App/Info.plist | 0 .../iOS}/iOS Example/App/SceneDelegate.swift | 0 .../ViewController+NetworkEventMonitor.swift | 0 ...Controller+NetworkRequestInterceptor.swift | 0 .../Controller/ViewController.swift | 0 .../iOS}/iOS Example/Model/Model.swift | 0 .../iOS Example/Network/DownloadRouter.swift | 0 .../iOS}/iOS Example/Network/Router.swift | 0 .../iOS Example/View Model/ViewModel.swift | 0 .../View/Base.lproj/Main.storyboard | 0 .../iOS}/iOS Example/View/TableViewCell.swift | 0 .../iOS}/iOS Example/View/ViewLoadType.swift | 0 .../iOS}/iOS Example/View/ViewState.swift | 0 .../macOS Example.xcodeproj/project.pbxproj | 527 ++++++++++++++++++ .../macOS/macOS Example/App/AppDelegate.swift | 33 ++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 58 ++ .../App/Assets.xcassets/Contents.json | 6 + .../App/macOS_Example.entitlements | 14 + .../ViewController+NetworkEventMonitor.swift | 26 + ...Controller+NetworkRequestInterceptor.swift | 24 + .../Controller/ViewController.swift | 219 ++++++++ .../macOS/macOS Example/Model/Model.swift | 21 + .../Network/DownloadRouter.swift | 34 ++ .../macOS/macOS Example/Network/Router.swift | 38 ++ .../macOS Example/View Model/ViewModel.swift | 69 +++ .../View/Base.lproj/Main.storyboard | 290 ++++++++++ .../macOS Example/View/TableCellView.swift | 19 + .../macOS Example/View/ViewLoadType.swift | 12 + .../macOS/macOS Example/View/ViewState.swift | 12 + RxNetworkKit.xcodeproj/project.pbxproj | 18 +- ...working.xcscheme => RxNetworkKit.xcscheme} | 2 +- .../contents.xcworkspacedata | 5 +- Source/Error/DefaultNetworkAPIError.swift | 2 +- Source/HTTP/DefaultHTTPErrorBody.swift | 2 +- .../ProcessInfo+operatingSystemName.swift | 1 + 46 files changed, 1454 insertions(+), 29 deletions(-) rename {Example => Examples/iOS}/iOS Example.xcodeproj/project.pbxproj (96%) rename {Example => Examples/iOS}/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename {Example => Examples/iOS}/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {Example => Examples/iOS}/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved (100%) rename {Example => Examples/iOS}/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme (99%) rename {Example => Examples/iOS}/iOS Example/App/AppDelegate.swift (100%) rename {Example => Examples/iOS}/iOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json (100%) rename {Example => Examples/iOS}/iOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {Example => Examples/iOS}/iOS Example/App/Assets.xcassets/Contents.json (100%) rename {Example => Examples/iOS}/iOS Example/App/Base.lproj/LaunchScreen.storyboard (100%) rename {Example => Examples/iOS}/iOS Example/App/Info.plist (100%) rename {Example => Examples/iOS}/iOS Example/App/SceneDelegate.swift (100%) rename {Example => Examples/iOS}/iOS Example/Controller/ViewController+NetworkEventMonitor.swift (100%) rename {Example => Examples/iOS}/iOS Example/Controller/ViewController+NetworkRequestInterceptor.swift (100%) rename {Example => Examples/iOS}/iOS Example/Controller/ViewController.swift (100%) rename {Example => Examples/iOS}/iOS Example/Model/Model.swift (100%) rename {Example => Examples/iOS}/iOS Example/Network/DownloadRouter.swift (100%) rename {Example => Examples/iOS}/iOS Example/Network/Router.swift (100%) rename {Example => Examples/iOS}/iOS Example/View Model/ViewModel.swift (100%) rename {Example => Examples/iOS}/iOS Example/View/Base.lproj/Main.storyboard (100%) rename {Example => Examples/iOS}/iOS Example/View/TableViewCell.swift (100%) rename {Example => Examples/iOS}/iOS Example/View/ViewLoadType.swift (100%) rename {Example => Examples/iOS}/iOS Example/View/ViewState.swift (100%) create mode 100644 Examples/macOS/macOS Example.xcodeproj/project.pbxproj create mode 100644 Examples/macOS/macOS Example/App/AppDelegate.swift create mode 100644 Examples/macOS/macOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Examples/macOS/macOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Examples/macOS/macOS Example/App/Assets.xcassets/Contents.json create mode 100644 Examples/macOS/macOS Example/App/macOS_Example.entitlements create mode 100644 Examples/macOS/macOS Example/Controller/ViewController+NetworkEventMonitor.swift create mode 100644 Examples/macOS/macOS Example/Controller/ViewController+NetworkRequestInterceptor.swift create mode 100644 Examples/macOS/macOS Example/Controller/ViewController.swift create mode 100644 Examples/macOS/macOS Example/Model/Model.swift create mode 100644 Examples/macOS/macOS Example/Network/DownloadRouter.swift create mode 100644 Examples/macOS/macOS Example/Network/Router.swift create mode 100644 Examples/macOS/macOS Example/View Model/ViewModel.swift create mode 100644 Examples/macOS/macOS Example/View/Base.lproj/Main.storyboard create mode 100644 Examples/macOS/macOS Example/View/TableCellView.swift create mode 100644 Examples/macOS/macOS Example/View/ViewLoadType.swift create mode 100644 Examples/macOS/macOS Example/View/ViewState.swift rename RxNetworkKit.xcodeproj/xcshareddata/xcschemes/{RxNetworking.xcscheme => RxNetworkKit.xcscheme} (98%) diff --git a/Example/iOS Example.xcodeproj/project.pbxproj b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj similarity index 96% rename from Example/iOS Example.xcodeproj/project.pbxproj rename to Examples/iOS/iOS Example.xcodeproj/project.pbxproj index ba9f041..404a301 100644 --- a/Example/iOS Example.xcodeproj/project.pbxproj +++ b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj @@ -7,8 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 0B568E5829DEDBEC00E51B21 /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B568E5329DED93D00E51B21 /* RxNetworkKit.framework */; }; - 0B568E5929DEDBEC00E51B21 /* RxNetworkKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0B568E5329DED93D00E51B21 /* RxNetworkKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 1A78320FA3392498E8F9EA82 /* RxDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = 819F858F2F093441234733F7 /* RxDataSources */; }; 39055093EB07D0FE09141EEC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF62C70BEC0D6E6B113DF07D /* AppDelegate.swift */; }; 3AEB576B0CFFB902AAB9A011 /* ViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C3894E572FDCE188F82EDC /* ViewState.swift */; }; @@ -24,11 +22,13 @@ 9083E2B5C56C9978FA9A88AF /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4C68630CA1328C530E000D /* ViewModel.swift */; }; A592421CEFFDBE0D3F6A504D /* ViewController+NetworkRequestInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636BE87DD1664C2D7FAECEDF /* ViewController+NetworkRequestInterceptor.swift */; }; BBC4FB455DB9EFC760A182E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23795F79829D049A83D55440 /* Assets.xcassets */; }; + C68C3F692A9402DC0036FF9D /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C68C3F682A9401960036FF9D /* RxNetworkKit.framework */; }; + C68C3F6A2A9402DC0036FF9D /* RxNetworkKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C68C3F682A9401960036FF9D /* RxNetworkKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DD59F1300712C00933636CC2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D82BA0503FE7CC526E5586A /* SceneDelegate.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 0B568E5229DED93D00E51B21 /* PBXContainerItemProxy */ = { + C68C3F672A9401960036FF9D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 0B77E0FA29D977390077FBC0 /* RxNetworkKit.xcodeproj */; proxyType = 2; @@ -44,7 +44,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 0B568E5929DEDBEC00E51B21 /* RxNetworkKit.framework in Embed Frameworks */, + C68C3F6A2A9402DC0036FF9D /* RxNetworkKit.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -53,7 +53,7 @@ /* Begin PBXFileReference section */ 0B568E4C29DED67300E51B21 /* RxNetworkKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RxNetworkKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0B77E0FA29D977390077FBC0 /* RxNetworkKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RxNetworkKit.xcodeproj; path = ../RxNetworkKit.xcodeproj; sourceTree = ""; }; + 0B77E0FA29D977390077FBC0 /* RxNetworkKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RxNetworkKit.xcodeproj; path = ../../RxNetworkKit.xcodeproj; sourceTree = ""; }; 0B77E10429D97A3E0077FBC0 /* iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 23795F79829D049A83D55440 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 417337DBE1D0E25D426FCAD6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -78,7 +78,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0B568E5829DEDBEC00E51B21 /* RxNetworkKit.framework in Frameworks */, + C68C3F692A9402DC0036FF9D /* RxNetworkKit.framework in Frameworks */, 1A78320FA3392498E8F9EA82 /* RxDataSources in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -107,14 +107,6 @@ name = Frameworks; sourceTree = ""; }; - 0B568E4F29DED93D00E51B21 /* Products */ = { - isa = PBXGroup; - children = ( - 0B568E5329DED93D00E51B21 /* RxNetworkKit.framework */, - ); - name = Products; - sourceTree = ""; - }; 0B77E10529D97A3E0077FBC0 /* Products */ = { isa = PBXGroup; children = ( @@ -181,6 +173,14 @@ path = "View Model"; sourceTree = ""; }; + C68C3F642A9401920036FF9D /* Products */ = { + isa = PBXGroup; + children = ( + C68C3F682A9401960036FF9D /* RxNetworkKit.framework */, + ); + name = Products; + sourceTree = ""; + }; EDB69B1E108A08763BE6EEC7 = { isa = PBXGroup; children = ( @@ -246,7 +246,7 @@ projectDirPath = ""; projectReferences = ( { - ProductGroup = 0B568E4F29DED93D00E51B21 /* Products */; + ProductGroup = C68C3F642A9401920036FF9D /* Products */; ProjectRef = 0B77E0FA29D977390077FBC0 /* RxNetworkKit.xcodeproj */; }, ); @@ -258,11 +258,11 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - 0B568E5329DED93D00E51B21 /* RxNetworkKit.framework */ = { + C68C3F682A9401960036FF9D /* RxNetworkKit.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; path = RxNetworkKit.framework; - remoteRef = 0B568E5229DED93D00E51B21 /* PBXContainerItemProxy */; + remoteRef = C68C3F672A9401960036FF9D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ @@ -397,7 +397,7 @@ DEVELOPMENT_TEAM = Z3SPAA69G7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Example", + "$(PROJECT_DIR)", ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "iOS Example/App/Info.plist"; @@ -436,7 +436,7 @@ DEVELOPMENT_TEAM = Z3SPAA69G7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Example", + "$(PROJECT_DIR)", ); GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "iOS Example/App/Info.plist"; diff --git a/Example/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/iOS/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Example/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Examples/iOS/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/Example/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/iOS/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Example/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Examples/iOS/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Example/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Examples/iOS/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved similarity index 100% rename from Example/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved rename to Examples/iOS/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/Example/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme b/Examples/iOS/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme similarity index 99% rename from Example/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme rename to Examples/iOS/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme index 3b0a0b9..ab95b45 100644 --- a/Example/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme +++ b/Examples/iOS/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme @@ -1,6 +1,6 @@ Bool { + return true + } + + +} + diff --git a/Examples/macOS/macOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/macOS/macOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Examples/macOS/macOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/macOS/macOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/macOS/macOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..3f00db4 --- /dev/null +++ b/Examples/macOS/macOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/macOS/macOS Example/App/Assets.xcassets/Contents.json b/Examples/macOS/macOS Example/App/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/macOS/macOS Example/App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/macOS/macOS Example/App/macOS_Example.entitlements b/Examples/macOS/macOS Example/App/macOS_Example.entitlements new file mode 100644 index 0000000..40b639e --- /dev/null +++ b/Examples/macOS/macOS Example/App/macOS_Example.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/Examples/macOS/macOS Example/Controller/ViewController+NetworkEventMonitor.swift b/Examples/macOS/macOS Example/Controller/ViewController+NetworkEventMonitor.swift new file mode 100644 index 0000000..50b5451 --- /dev/null +++ b/Examples/macOS/macOS Example/Controller/ViewController+NetworkEventMonitor.swift @@ -0,0 +1,26 @@ +// +// ViewController+NetworkEventMonitor.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 01/04/2023. +// + +import Foundation +import RxNetworkKit + +extension ViewController: NetworkEventMonitor { + func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { + debugPrint("") + } + func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { + debugPrint(session) + } + func urlSession(_ session: URLSession, didCreateTask task: URLSessionTask) { + debugPrint("Task created!") + } + func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + guard let error else { return } + debugPrint("Task did finish with error: \(error.localizedDescription)!") + } + func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { } +} diff --git a/Examples/macOS/macOS Example/Controller/ViewController+NetworkRequestInterceptor.swift b/Examples/macOS/macOS Example/Controller/ViewController+NetworkRequestInterceptor.swift new file mode 100644 index 0000000..e625612 --- /dev/null +++ b/Examples/macOS/macOS Example/Controller/ViewController+NetworkRequestInterceptor.swift @@ -0,0 +1,24 @@ +// +// ViewController+NetworkRequestInterceptor.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 01/04/2023. +// + +import Foundation +import RxNetworkKit + +extension ViewController: NetworkRequestInterceptor { + func adapt(_ request: URLRequest, for session: URLSession) -> URLRequest { + return request + } + func retryMaxAttempts(_ request: URLRequest, for session: URLSession) -> Int { + 5 + } + func retryPolicy(_ request: URLRequest, for session: URLSession) -> NetworkRequestRetryPolicy { + .constant(time: 5_000) + } + func shouldRetry(_ request: URLRequest, for session: URLSession, dueTo error: NetworkError) -> Bool { + true + } +} diff --git a/Examples/macOS/macOS Example/Controller/ViewController.swift b/Examples/macOS/macOS Example/Controller/ViewController.swift new file mode 100644 index 0000000..bf53473 --- /dev/null +++ b/Examples/macOS/macOS Example/Controller/ViewController.swift @@ -0,0 +1,219 @@ +// +// ViewController.swift +// macOS Example +// +// Created by Loay Ashraf on 22/08/2023. +// + +import Cocoa +import RxSwift +import RxCocoa +import RxNetworkKit + +class ViewController: NSViewController { + @IBOutlet weak var tableView: NSTableView! + @IBOutlet weak var reachbilityView: NSView! + @IBOutlet weak var reachabilityLabel: NSTextField! + @IBOutlet weak var errorView: NSStackView! + @IBOutlet weak var errorImageView: NSImageView! + @IBOutlet weak var errorLabel: NSTextField! + @IBOutlet weak var errorRetryButton: NSButton! + @IBOutlet weak var activityIndicator: NSProgressIndicator! + private var viewModel: ViewModel! + private var items: BehaviorRelay<[Model]> = .init(value: []) + private var refreshItems: PublishRelay = .init() + private let disposeBag: DisposeBag = .init() + /// View has loaded successfully. + override func viewDidLoad() { + super.viewDidLoad() + setupViewModel() + setupUI() + bindUI() + } + /// View is about to appear on screen. + override func viewWillAppear() { + super.viewWillAppear() + viewModel.viewState.accept(.loading(loadType: .initial)) + } + /// called when `Refresh Items` menu items is tapped. + /// + /// - Parameter sender: `NSMenuItem` object that sent event. + @IBAction func refreshItems(_ sender: NSMenuItem) { + refreshItems.accept(()) + } + /// Initializes ViewModel object. + private func setupViewModel() { + let manager = NetworkManager(configuration: .default, requestInterceptor: self, eventMonitor: self) + viewModel = .init(networkManager: manager) + } + /// Sets up views. + private func setupUI() { + tableView.dataSource = self + tableView.delegate = self + reachbilityView.wantsLayer = true + reachbilityView.layer?.backgroundColor = NSColor.systemIndigo.cgColor + } + // MARK: - Bindings + /// Binds view state and events. + private func bindUI() { + bindUIState() + bindUIEvents() + } + // MARK: UI State Bindings + /// Binds view state. + private func bindUIState() { + bindTableViewIsHidden() + bindTableViewItems() + bindActivityIndicatorIsAnimating() + bindErrorViewIsHidden() + bindErrorViewText() + bindReachabilityText() + } + /// Presents new reachability status. + /// + /// - Parameter status: `NetworkReachabilityStatus` object to be presented. + private func presentReachabilityStatus(_ status: NetworkReachabilityStatus) { + var reachabilityText = "" + switch status { + case .reachable(let interfaceType): + reachabilityText = "Network is reachable via \(interfaceType.rawValue)." + case .unReachable: + reachabilityText = "Network is unreachable." + } + self.reachabilityLabel.stringValue = reachabilityText + self.reachbilityView.isHidden = false + DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { + self.reachabilityLabel.stringValue = "" + self.reachbilityView.isHidden = true + } + } + // MARK: UI Events Bindings + /// Binds view events. + private func bindUIEvents() { + // Bind Refresh Items' tap event to viewModel's viewState. + bindRefreshItemsEvent() + // Bind errorView's retryButton to viewModel's viewState. + bindErrorViewRetryEvent() + } + /// Bind tableView's isHidden property to view state. + private func bindTableViewIsHidden() { + viewModel.viewState + .map({ $0 == .loading(loadType: .initial) || $0 == .error }) + .bind(to: tableView.rx.isHidden) + .disposed(by: disposeBag) + } + /// Bind viewModel's users sequence to tableView's items. + private func bindTableViewItems() { + viewModel.users + .drive(items) + .disposed(by: disposeBag) + items + .subscribe(onNext: { _ in + self.tableView.reloadData() + }) + .disposed(by: disposeBag) + } + /// Downloads image data to be displayed inside `TableViewCell` object + /// + /// - Parameters: + /// - url: `URL` used to download image data. + /// - cell: `TableViewCell` that the image data will be applied to. + private func downloadTableViewCellImage(using url: URL, applyTo cell: TableCellView) { + viewModel.downloadImage(DownloadRouter.default(url: url)) + .observe(on: MainScheduler.instance) + .subscribe(onNext: { + switch $0 { + case .completedWithData(let data): + guard let data = data, let image = NSImage(data: data) else { return } + cell.customImageView.image = image + default: + return + } + }) + .disposed(by: self.disposeBag) + } + /// Bind activityIndicator's isAnimating property to view state. + private func bindActivityIndicatorIsAnimating() { + viewModel.viewState + .map({ $0 == .loading(loadType: .initial) }) + .subscribe(onNext: { shouldAnimate in + if shouldAnimate { + self.activityIndicator.startAnimation(self) + } else { + self.activityIndicator.stopAnimation(self) + } + }) + .disposed(by: disposeBag) + } + /// Bind errorView's isHidden property to view state. + private func bindErrorViewIsHidden() { + viewModel.viewState + .map({ $0 != .error }) + .bind(to: errorView.rx.isHidden) + .disposed(by: disposeBag) + } + /// Bind viewModel's error sequence to errorLabel's text. + private func bindErrorViewText() { + viewModel.error + .map({ $0.localizedDescription }) + .drive(errorLabel.rx.text) + .disposed(by: disposeBag) + } + /// Bind NetworkReachability's status sequence to reachabiliytView and reachabilityLabel. + private func bindReachabilityText() { + NetworkReachability.shared.status + .observe(on: MainScheduler.instance) + .bind(onNext: { + self.presentReachabilityStatus($0) + }) + .disposed(by: disposeBag) + } + /// Bind Refresh Items' tap event to viewModel's viewState. + private func bindRefreshItemsEvent() { + refreshItems + .map({ .loading(loadType: .refresh) }) + .bind(to: viewModel.viewState) + .disposed(by: disposeBag) + } + /// Bind errorView's retryButton to viewModel's viewState. + private func bindErrorViewRetryEvent() { + errorRetryButton.rx + .tap + .map({ .loading(loadType: .initial) }) + .bind(to: viewModel.viewState) + .disposed(by: disposeBag) + } + /// Navigates to user profile. + /// + /// - Parameter url: HTML `URL` to user profile. + private func navigateToUserProfile(with url: URL) { + NSWorkspace.shared.open(url) + } +} + +extension ViewController: NSTableViewDataSource { + func numberOfRows(in tableView: NSTableView) -> Int { + items.value.count + } + func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { + 130 + } + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + let user = items.value[row] + guard let cellView = tableView.makeView(withIdentifier: tableColumn!.identifier, owner: nil) as? TableCellView else { return nil } + cellView.customTextField.stringValue = user.login + downloadTableViewCellImage(using: user.avatarURL, applyTo: cellView) + return cellView + } +} + +extension ViewController: NSTableViewDelegate { + func tableViewSelectionDidChange(_ notification: Notification) { + let tableView = notification.object as! NSTableView + let rowIndex = tableView.selectedRow + guard rowIndex >= 0 else { return } + tableView.deselectRow(rowIndex) + let user = items.value[rowIndex] + navigateToUserProfile(with: user.htmlURL) + } +} diff --git a/Examples/macOS/macOS Example/Model/Model.swift b/Examples/macOS/macOS Example/Model/Model.swift new file mode 100644 index 0000000..a7104db --- /dev/null +++ b/Examples/macOS/macOS Example/Model/Model.swift @@ -0,0 +1,21 @@ +// +// Model.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 20/03/2023. +// + +import Foundation + +struct Model: Decodable { + let id: Int + let avatarURL: URL + let htmlURL: URL + let login: String + enum CodingKeys: String, CodingKey { + case id + case avatarURL = "avatar_url" + case htmlURL = "html_url" + case login + } +} diff --git a/Examples/macOS/macOS Example/Network/DownloadRouter.swift b/Examples/macOS/macOS Example/Network/DownloadRouter.swift new file mode 100644 index 0000000..cffa683 --- /dev/null +++ b/Examples/macOS/macOS Example/Network/DownloadRouter.swift @@ -0,0 +1,34 @@ +// +// DownloadRouter.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 01/04/2023. +// + +import Foundation +import RxNetworkKit + +enum DownloadRouter: NetworkDownloadRouter { + case `default`(url: URL) + var scheme: HTTPScheme { + .https + } + var domain: String { + "" + } + var path: String { + "" + } + var headers: [String : String] { + [:] + } + var parameters: [String : String]? { + nil + } + var url: URL? { + switch self { + case .default(let url): + return url + } + } +} diff --git a/Examples/macOS/macOS Example/Network/Router.swift b/Examples/macOS/macOS Example/Network/Router.swift new file mode 100644 index 0000000..564a378 --- /dev/null +++ b/Examples/macOS/macOS Example/Network/Router.swift @@ -0,0 +1,38 @@ +// +// Router.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 01/04/2023. +// + +import Foundation +import RxNetworkKit + +enum Router: NetworkRouter { + case `default` + var scheme: HTTPScheme { + .https + } + var method: HTTPMethod { + .get + } + var domain: String { + "api.github.com" + } + var path: String { + "users" + } + var headers: [String : String] { + ["Accept": "application/vnd.github+json"] + } + var parameters: [String : String]? { + nil + } + var body: [String : Any]? { + nil + } + var url: URL? { + let urlString = scheme.rawValue + domain + "/" + path + return URL(string: urlString) + } +} diff --git a/Examples/macOS/macOS Example/View Model/ViewModel.swift b/Examples/macOS/macOS Example/View Model/ViewModel.swift new file mode 100644 index 0000000..dfe174f --- /dev/null +++ b/Examples/macOS/macOS Example/View Model/ViewModel.swift @@ -0,0 +1,69 @@ +// +// ViewModel.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 31/03/2023. +// + +import Foundation +import RxSwift +import RxCocoa +import RxNetworkKit + +class ViewModel { + // MARK: Input + // Input sequence is the same as view state (only for loading states) + private(set) var viewState: PublishRelay = .init() + // MARK: Output + private(set) var users: Driver<[Model]>! + private(set) var error: Driver! + // MARK: Properties and Dependencies + private let networkManager: NetworkManager + private let disposeBag = DisposeBag() + /// Creates `ViewModel` instance + /// + /// - Parameter networkManager: `NetworkManager` object used for making network API calls. + init(networkManager: NetworkManager) { + self.networkManager = networkManager + bindOutput() + } + /// Creates observable sequence that results in image data. + /// + /// - Parameter router: `DownloadRouter` used to download image data. + /// + /// - Returns: observable sequence that results in image data. + func downloadImage(_ router: DownloadRouter) -> Observable { + networkManager.download(router) + } + /// Binds output sequence to input sequence. + private func bindOutput() { + // Create default sequence with default API call request. + let loadObservable = viewState + .filter( { ![.idle, .loading(loadType: .paginate), .error].contains($0) }) + .flatMapLatest{ _ in + let single: Single<[Model]> = self.networkManager.request(Router.default) + return single + .asObservable() + .materialize() + } + .share() + // Initialize users and error sequence outputs + self.users = loadObservable + .compactMap { $0.element } + .asDriver(onErrorJustReturn: []) + self.error = loadObservable + .compactMap { $0.error as? NetworkError } + .asDriver(onErrorJustReturn: NetworkError.client(.transport(NSError(domain: "", code: 1, userInfo: nil)))) + // Bind output sequence to input sequence (view state) + self.users + .asObservable() + .map({ _ in .idle }) + .bind(to: viewState) + .disposed(by: disposeBag) + self.error + .asObservable() + .map({ _ in .error }) + .bind(to: viewState) + .disposed(by: disposeBag) + } +} diff --git a/Examples/macOS/macOS Example/View/Base.lproj/Main.storyboard b/Examples/macOS/macOS Example/View/Base.lproj/Main.storyboard new file mode 100644 index 0000000..0ca0524 --- /dev/null +++ b/Examples/macOS/macOS Example/View/Base.lproj/Main.storyboard @@ -0,0 +1,290 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSAllRomanInputSourcesLocaleIdentifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/macOS/macOS Example/View/TableCellView.swift b/Examples/macOS/macOS Example/View/TableCellView.swift new file mode 100644 index 0000000..b0e3550 --- /dev/null +++ b/Examples/macOS/macOS Example/View/TableCellView.swift @@ -0,0 +1,19 @@ +// +// TableCellView.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 01/04/2023. +// + +import Cocoa + +class TableCellView: NSTableCellView { + @IBOutlet weak var customTextField: NSTextField! + @IBOutlet weak var customImageView: NSImageView! + override func awakeFromNib() { + super.awakeFromNib() + customImageView.wantsLayer = true + customImageView.layer?.cornerRadius = 50.0 + customImageView.layer?.cornerCurve = .continuous + } +} diff --git a/Examples/macOS/macOS Example/View/ViewLoadType.swift b/Examples/macOS/macOS Example/View/ViewLoadType.swift new file mode 100644 index 0000000..3aab992 --- /dev/null +++ b/Examples/macOS/macOS Example/View/ViewLoadType.swift @@ -0,0 +1,12 @@ +// +// ViewLoadType.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 01/04/2023. +// + +enum ViewLoadType { + case initial + case refresh + case paginate +} diff --git a/Examples/macOS/macOS Example/View/ViewState.swift b/Examples/macOS/macOS Example/View/ViewState.swift new file mode 100644 index 0000000..e5cd81d --- /dev/null +++ b/Examples/macOS/macOS Example/View/ViewState.swift @@ -0,0 +1,12 @@ +// +// ViewState.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 01/04/2023. +// + +enum ViewState: Equatable { + case idle + case loading(loadType: ViewLoadType) + case error +} diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index a644167..38e97e1 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -22,7 +22,6 @@ 0B77E09529D965D30077FBC0 /* RequestRetryPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05B29D965D30077FBC0 /* RequestRetryPolicy.swift */; }; 0B77E09629D965D30077FBC0 /* RequestAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05C29D965D30077FBC0 /* RequestAdapter.swift */; }; 0B77E09729D965D30077FBC0 /* RequestRetrier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05D29D965D30077FBC0 /* RequestRetrier.swift */; }; - 0B77E09829D965D30077FBC0 /* RxNetworking.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B77E05E29D965D30077FBC0 /* RxNetworking.h */; }; 0B77E09929D965D30077FBC0 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06029D965D30077FBC0 /* NetworkManager.swift */; }; 0B77E09A29D965D30077FBC0 /* HTTPMIMEType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06229D965D30077FBC0 /* HTTPMIMEType.swift */; }; 0B77E09B29D965D30077FBC0 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06329D965D30077FBC0 /* HTTPMethod.swift */; }; @@ -57,6 +56,7 @@ 0B77E0BB29D968DE0077FBC0 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BA29D968DE0077FBC0 /* RxRelay */; }; 0B77E0BD29D968DE0077FBC0 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BC29D968DE0077FBC0 /* RxSwift */; }; 0B77E0C029D969370077FBC0 /* RxSwiftExt in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BF29D969370077FBC0 /* RxSwiftExt */; }; + C6049B162A95307800E5727E /* RxNetworkKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C6049B152A95307800E5727E /* RxNetworkKit.h */; }; C6A9BEF62A93E2AE00459E32 /* DefaultHTTPErrorBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */; }; C6A9BEF82A93E2D600459E32 /* HTTPErrorBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */; }; C6A9BEFA2A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */; }; @@ -82,7 +82,6 @@ 0B77E05B29D965D30077FBC0 /* RequestRetryPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestRetryPolicy.swift; sourceTree = ""; }; 0B77E05C29D965D30077FBC0 /* RequestAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestAdapter.swift; sourceTree = ""; }; 0B77E05D29D965D30077FBC0 /* RequestRetrier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestRetrier.swift; sourceTree = ""; }; - 0B77E05E29D965D30077FBC0 /* RxNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxNetworking.h; sourceTree = ""; }; 0B77E06029D965D30077FBC0 /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; 0B77E06229D965D30077FBC0 /* HTTPMIMEType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPMIMEType.swift; sourceTree = ""; }; 0B77E06329D965D30077FBC0 /* HTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; @@ -113,6 +112,7 @@ 0B77E08629D965D30077FBC0 /* NetworkDownloadRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkDownloadRouter.swift; sourceTree = ""; }; 0B77E08729D965D30077FBC0 /* NetworkRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkRouter.swift; sourceTree = ""; }; 0B77E08829D965D30077FBC0 /* NetworkUploadRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkUploadRouter.swift; sourceTree = ""; }; + C6049B152A95307800E5727E /* RxNetworkKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxNetworkKit.h; sourceTree = ""; }; C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultHTTPErrorBody.swift; sourceTree = ""; }; C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPErrorBody.swift; sourceTree = ""; }; C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+setAdditionalHTTPHeader.swift"; sourceTree = ""; }; @@ -164,7 +164,7 @@ 0B77E07029D965D30077FBC0 /* Error */, 0B77E07629D965D30077FBC0 /* Rx Extensions */, 0B77E08529D965D30077FBC0 /* Router */, - 0B77E05E29D965D30077FBC0 /* RxNetworking.h */, + C6049B152A95307800E5727E /* RxNetworkKit.h */, ); path = Source; sourceTree = ""; @@ -345,7 +345,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 0B77E09829D965D30077FBC0 /* RxNetworking.h in Headers */, + C6049B162A95307800E5727E /* RxNetworkKit.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -383,7 +383,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastUpgradeCheck = 1420; + LastUpgradeCheck = 1430; TargetAttributes = { 0B77DFD529D964D40077FBC0 = { CreatedOnToolsVersion = 14.2; @@ -609,6 +609,7 @@ isa = XCBuildConfiguration; buildSettings = { BUILD_LIBRARY_FOR_DISTRIBUTION = NO; + CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; @@ -616,6 +617,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -627,6 +629,8 @@ ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -643,6 +647,7 @@ isa = XCBuildConfiguration; buildSettings = { BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; @@ -650,6 +655,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -661,6 +667,8 @@ ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; diff --git a/RxNetworkKit.xcodeproj/xcshareddata/xcschemes/RxNetworking.xcscheme b/RxNetworkKit.xcodeproj/xcshareddata/xcschemes/RxNetworkKit.xcscheme similarity index 98% rename from RxNetworkKit.xcodeproj/xcshareddata/xcschemes/RxNetworking.xcscheme rename to RxNetworkKit.xcodeproj/xcshareddata/xcschemes/RxNetworkKit.xcscheme index 5483893..1a7445c 100644 --- a/RxNetworkKit.xcodeproj/xcshareddata/xcschemes/RxNetworking.xcscheme +++ b/RxNetworkKit.xcodeproj/xcshareddata/xcschemes/RxNetworkKit.xcscheme @@ -1,6 +1,6 @@ + location = "container:Examples/iOS/iOS Example.xcodeproj"> + + diff --git a/Source/Error/DefaultNetworkAPIError.swift b/Source/Error/DefaultNetworkAPIError.swift index ee320b3..e9af7dd 100644 --- a/Source/Error/DefaultNetworkAPIError.swift +++ b/Source/Error/DefaultNetworkAPIError.swift @@ -5,6 +5,6 @@ // Created by Loay Ashraf on 19/02/2023. // -struct DefaultNetworkAPIError: NetworkAPIError { +public struct DefaultNetworkAPIError: NetworkAPIError { let message: String } diff --git a/Source/HTTP/DefaultHTTPErrorBody.swift b/Source/HTTP/DefaultHTTPErrorBody.swift index 68f849f..89238f8 100644 --- a/Source/HTTP/DefaultHTTPErrorBody.swift +++ b/Source/HTTP/DefaultHTTPErrorBody.swift @@ -7,7 +7,7 @@ import Foundation -struct DefaultHTTPErrorBody: HTTPErrorBody { +public struct DefaultHTTPErrorBody: HTTPErrorBody { let statusCode: Int? let message: String? let supportId: String? diff --git a/Source/Manager/ProcessInfo+operatingSystemName.swift b/Source/Manager/ProcessInfo+operatingSystemName.swift index 17ba7d6..bd87aae 100644 --- a/Source/Manager/ProcessInfo+operatingSystemName.swift +++ b/Source/Manager/ProcessInfo+operatingSystemName.swift @@ -11,6 +11,7 @@ import UIKit #endif extension ProcessInfo { + /// Name of current operating system (iOS, iPadOS, watchOS, etc.) var operatingSystemName: String { var osName: String = "" #if os(iOS) From 1949782c15bdf44650f87c46e61bd538025d6b43 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 24 Aug 2023 19:03:30 +0300 Subject: [PATCH 04/95] Apply new version (0.0.2) (#28) * fix: remove tinted button warning Resolves: none. * version: 0.0.2 Resolves: none. * fix: update version for podSpec file Resolves: none. --- .../iOS/iOS Example.xcodeproj/project.pbxproj | 7 ++++--- .../iOS Example/View/Base.lproj/Main.storyboard | 17 +++++++++-------- .../macOS Example.xcodeproj/project.pbxproj | 15 ++------------- RxNetworkKit.xcodeproj/project.pbxproj | 4 ++-- RxNetworkKitX.podspec | 4 ++-- 5 files changed, 19 insertions(+), 28 deletions(-) diff --git a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj index 404a301..26242a9 100644 --- a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj +++ b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj @@ -221,8 +221,9 @@ 8FDDFD68FFEB45324727C0BA /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1420; - LastUpgradeCheck = 1420; + LastUpgradeCheck = 1430; TargetAttributes = { 26E9FD5EEA5FDD0B3C83D665 = { DevelopmentTeam = Z3SPAA69G7; @@ -411,7 +412,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 0.0.2; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitExample; PRODUCT_NAME = "iOS Example"; SDKROOT = iphoneos; @@ -450,7 +451,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 0.0.2; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitExample; PRODUCT_NAME = "iOS Example"; SDKROOT = iphoneos; diff --git a/Examples/iOS/iOS Example/View/Base.lproj/Main.storyboard b/Examples/iOS/iOS Example/View/Base.lproj/Main.storyboard index ac580a6..bfe0c45 100644 --- a/Examples/iOS/iOS Example/View/Base.lproj/Main.storyboard +++ b/Examples/iOS/iOS Example/View/Base.lproj/Main.storyboard @@ -1,8 +1,9 @@ - + - + + @@ -11,7 +12,7 @@ - + @@ -20,7 +21,7 @@ - + @@ -80,7 +81,7 @@ diff --git a/Examples/macOS/macOS Example.xcodeproj/project.pbxproj b/Examples/macOS/macOS Example.xcodeproj/project.pbxproj index b0cc09e..f1b5bf1 100644 --- a/Examples/macOS/macOS Example.xcodeproj/project.pbxproj +++ b/Examples/macOS/macOS Example.xcodeproj/project.pbxproj @@ -20,7 +20,6 @@ C69FBA1A2A9408740062BFD7 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69FBA192A9408740062BFD7 /* ViewController.swift */; }; C69FBA1C2A9408770062BFD7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C69FBA1B2A9408770062BFD7 /* Assets.xcassets */; }; C69FBA1F2A9408770062BFD7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C69FBA1D2A9408770062BFD7 /* Main.storyboard */; }; - C69FBA2F2A940AD80062BFD7 /* RxDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = C69FBA2E2A940AD80062BFD7 /* RxDataSources */; }; C69FBA322A940B090062BFD7 /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C69FBA312A940B090062BFD7 /* RxNetworkKit.framework */; }; C69FBA332A940B090062BFD7 /* RxNetworkKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C69FBA312A940B090062BFD7 /* RxNetworkKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ @@ -75,7 +74,6 @@ buildActionMask = 2147483647; files = ( C69FBA322A940B090062BFD7 /* RxNetworkKit.framework in Frameworks */, - C69FBA2F2A940AD80062BFD7 /* RxDataSources in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -203,7 +201,6 @@ ); name = "macOS Example"; packageProductDependencies = ( - C69FBA2E2A940AD80062BFD7 /* RxDataSources */, ); productName = "macOS Example"; productReference = C69FBA142A9408740062BFD7 /* macOS Example.app */; @@ -443,7 +440,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 0.0.2; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitMacOSExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -473,7 +470,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 0.0.2; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitMacOSExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -514,14 +511,6 @@ }; }; /* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - C69FBA2E2A940AD80062BFD7 /* RxDataSources */ = { - isa = XCSwiftPackageProductDependency; - package = C69FBA2D2A940AD80062BFD7 /* XCRemoteSwiftPackageReference "RxDataSources" */; - productName = RxDataSources; - }; -/* End XCSwiftPackageProductDependency section */ }; rootObject = C69FBA0C2A9408740062BFD7 /* Project object */; } diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index 38e97e1..5c67b7c 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -628,7 +628,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 0.0.2; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKit; @@ -666,7 +666,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 0.0.2; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKit; diff --git a/RxNetworkKitX.podspec b/RxNetworkKitX.podspec index 31a0c90..1104206 100644 --- a/RxNetworkKitX.podspec +++ b/RxNetworkKitX.podspec @@ -4,7 +4,7 @@ Pod::Spec.new do |s| s.macos.deployment_target = '11.0' s.name = 'RxNetworkKitX' s.module_name = 'RxNetworkKit' - s.version = '0.0.1' + s.version = '0.0.2' s.summary = 'a lightweight FRP networking framework.' s.description = 'a FRP networking framework built on top of URLSession and uses RxSwift and RxCocoa.' s.homepage = 'https://github.com/loay-ashraf/RxNetworkKit' @@ -17,4 +17,4 @@ Pod::Spec.new do |s| s.dependency 'RxSwiftExt', '~> 6.0.1' s.source_files = 'Source/**/*.{swift,m,h}' s.swift_version = '5.0' - end \ No newline at end of file + end From 1fb7ed0f77cafd39b8bfbedb4109f592926977e4 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 24 Aug 2023 19:14:55 +0300 Subject: [PATCH 05/95] fix: remove RxDataSources import statement (#31) Resolves: none. --- Examples/macOS/macOS Example/App/AppDelegate.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Examples/macOS/macOS Example/App/AppDelegate.swift b/Examples/macOS/macOS Example/App/AppDelegate.swift index db58985..054d1ed 100644 --- a/Examples/macOS/macOS Example/App/AppDelegate.swift +++ b/Examples/macOS/macOS Example/App/AppDelegate.swift @@ -6,7 +6,6 @@ // import Cocoa -import RxDataSources import RxNetworkKit @main From b686c2688327e08813a9b171d22f45a86f644694 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Fri, 25 Aug 2023 15:38:07 +0300 Subject: [PATCH 06/95] Add CI Workflows For Repository (#33) * feat: add iOS workflow Resolves: none. * fix: update build-ios.yml Resolves: none. * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Update build-ios.yml * Create build-macos.yml * Update build-ios.yml + build-macos.yml * fix error 65 * Update build-ios.yml * update build-ios.yml + build-macos.yml * Update build-macos.yml * Update project.pbxproj * add publish-podspec.yml * add workflow files outside of folders * Update publish-podspec.yml * Update publish-podspec.yml * Update publish-podspec.yml * Create pod-lib-lint.yml * Update pod-lib-lint.yml * Update pod-lib-lint.yml * Update pod-lib-lint.yml * Update pod-lib-lint.yml * Update pod-lib-lint.yml * test container workflow * Update build.yml * Update build.yml * add some changes * Update build.yml * add some changes * Update build.yml * refactor: update names + remove comments Resolves: none. * add trigger for trunk push workflow * add dummy project to test SPM integration * add spm-lint.yml * Update spm-lint.yml * Update spm-lint.yml * Update spm-lint.yml * Update spm-lint.yml * update dummy project * update workspace dependencies versions --- .../SPMDummy.xcodeproj/project.pbxproj | 399 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 25 ++ .../SPMDummy/SPMDummy/AppDelegate.swift | 63 +++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../SPMDummy/Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 ++ .../SPMDummy/Base.lproj/Main.storyboard | 24 ++ .../workflows/SPMDummy/SPMDummy/Info.plist | 25 ++ .../SPMDummy/SPMDummy/SceneDelegate.swift | 53 +++ .../SPMDummy/SPMDummy/ViewController.swift | 30 ++ .github/workflows/build-ios.yml | 83 ++++ .github/workflows/build-macos.yml | 45 ++ .github/workflows/build.yml | 18 + .github/workflows/pod-lib-lint.yml | 30 ++ .github/workflows/pod-trunk-push.yml | 32 ++ .github/workflows/spm-lint.yml | 40 ++ Package.resolved | 4 +- Package.swift | 4 +- RxNetworkKit.xcodeproj/project.pbxproj | 10 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- 23 files changed, 948 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/SPMDummy/SPMDummy.xcodeproj/project.pbxproj create mode 100644 .github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 .github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 .github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 .github/workflows/SPMDummy/SPMDummy/AppDelegate.swift create mode 100644 .github/workflows/SPMDummy/SPMDummy/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 .github/workflows/SPMDummy/SPMDummy/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 .github/workflows/SPMDummy/SPMDummy/Assets.xcassets/Contents.json create mode 100644 .github/workflows/SPMDummy/SPMDummy/Base.lproj/LaunchScreen.storyboard create mode 100644 .github/workflows/SPMDummy/SPMDummy/Base.lproj/Main.storyboard create mode 100644 .github/workflows/SPMDummy/SPMDummy/Info.plist create mode 100644 .github/workflows/SPMDummy/SPMDummy/SceneDelegate.swift create mode 100644 .github/workflows/SPMDummy/SPMDummy/ViewController.swift create mode 100644 .github/workflows/build-ios.yml create mode 100644 .github/workflows/build-macos.yml create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/pod-lib-lint.yml create mode 100644 .github/workflows/pod-trunk-push.yml create mode 100644 .github/workflows/spm-lint.yml diff --git a/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.pbxproj b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.pbxproj new file mode 100644 index 0000000..aadf94c --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.pbxproj @@ -0,0 +1,399 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + C60771B52A98525600268543 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60771B42A98525600268543 /* AppDelegate.swift */; }; + C60771B72A98525600268543 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60771B62A98525600268543 /* SceneDelegate.swift */; }; + C60771B92A98525600268543 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60771B82A98525600268543 /* ViewController.swift */; }; + C60771BC2A98525600268543 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C60771BA2A98525600268543 /* Main.storyboard */; platformFilter = ios; }; + C60771BE2A98525A00268543 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C60771BD2A98525A00268543 /* Assets.xcassets */; }; + C60771C12A98525A00268543 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C60771BF2A98525A00268543 /* LaunchScreen.storyboard */; platformFilter = ios; }; + C6C5417E2A98CDCD00BDA70B /* RxNetworkKit in Frameworks */ = {isa = PBXBuildFile; productRef = C6C5417D2A98CDCD00BDA70B /* RxNetworkKit */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + C60771B12A98525600268543 /* SPMDummy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SPMDummy.app; sourceTree = BUILT_PRODUCTS_DIR; }; + C60771B42A98525600268543 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + C60771B62A98525600268543 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + C60771B82A98525600268543 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + C60771BB2A98525600268543 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + C60771BD2A98525A00268543 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + C60771C02A98525A00268543 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + C60771C22A98525A00268543 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C60771CA2A98531500268543 /* RxNetworkKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = RxNetworkKit; path = ../../..; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C60771AE2A98525600268543 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C6C5417E2A98CDCD00BDA70B /* RxNetworkKit in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C60771A82A98525600268543 = { + isa = PBXGroup; + children = ( + C60771C82A9852BF00268543 /* Packages */, + C60771B32A98525600268543 /* SPMDummy */, + C60771B22A98525600268543 /* Products */, + C60771CB2A98532100268543 /* Frameworks */, + ); + sourceTree = ""; + }; + C60771B22A98525600268543 /* Products */ = { + isa = PBXGroup; + children = ( + C60771B12A98525600268543 /* SPMDummy.app */, + ); + name = Products; + sourceTree = ""; + }; + C60771B32A98525600268543 /* SPMDummy */ = { + isa = PBXGroup; + children = ( + C60771B42A98525600268543 /* AppDelegate.swift */, + C60771B62A98525600268543 /* SceneDelegate.swift */, + C60771B82A98525600268543 /* ViewController.swift */, + C60771BA2A98525600268543 /* Main.storyboard */, + C60771BD2A98525A00268543 /* Assets.xcassets */, + C60771BF2A98525A00268543 /* LaunchScreen.storyboard */, + C60771C22A98525A00268543 /* Info.plist */, + ); + path = SPMDummy; + sourceTree = ""; + }; + C60771C82A9852BF00268543 /* Packages */ = { + isa = PBXGroup; + children = ( + C60771CA2A98531500268543 /* RxNetworkKit */, + ); + name = Packages; + sourceTree = ""; + }; + C60771CB2A98532100268543 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C60771B02A98525600268543 /* SPMDummy */ = { + isa = PBXNativeTarget; + buildConfigurationList = C60771C52A98525A00268543 /* Build configuration list for PBXNativeTarget "SPMDummy" */; + buildPhases = ( + C60771AD2A98525600268543 /* Sources */, + C60771AE2A98525600268543 /* Frameworks */, + C60771AF2A98525600268543 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SPMDummy; + packageProductDependencies = ( + C6C5417D2A98CDCD00BDA70B /* RxNetworkKit */, + ); + productName = SPMDummy; + productReference = C60771B12A98525600268543 /* SPMDummy.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C60771A92A98525600268543 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + C60771B02A98525600268543 = { + CreatedOnToolsVersion = 14.3.1; + }; + }; + }; + buildConfigurationList = C60771AC2A98525600268543 /* Build configuration list for PBXProject "SPMDummy" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C60771A82A98525600268543; + productRefGroup = C60771B22A98525600268543 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C60771B02A98525600268543 /* SPMDummy */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C60771AF2A98525600268543 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C60771C12A98525A00268543 /* LaunchScreen.storyboard in Resources */, + C60771BE2A98525A00268543 /* Assets.xcassets in Resources */, + C60771BC2A98525600268543 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C60771AD2A98525600268543 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C60771B92A98525600268543 /* ViewController.swift in Sources */, + C60771B52A98525600268543 /* AppDelegate.swift in Sources */, + C60771B72A98525600268543 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + C60771BA2A98525600268543 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C60771BB2A98525600268543 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + C60771BF2A98525A00268543 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C60771C02A98525A00268543 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C60771C32A98525A00268543 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + C60771C42A98525A00268543 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C60771C62A98525A00268543 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = Z3SPAA69G7; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = SPMDummy/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.las.SPMDummy; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + C60771C72A98525A00268543 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = Z3SPAA69G7; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = SPMDummy/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.las.SPMDummy; + PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C60771AC2A98525600268543 /* Build configuration list for PBXProject "SPMDummy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C60771C32A98525A00268543 /* Debug */, + C60771C42A98525A00268543 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C60771C52A98525A00268543 /* Build configuration list for PBXNativeTarget "SPMDummy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C60771C62A98525A00268543 /* Debug */, + C60771C72A98525A00268543 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + C6C5417D2A98CDCD00BDA70B /* RxNetworkKit */ = { + isa = XCSwiftPackageProductDependency; + productName = RxNetworkKit; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = C60771A92A98525600268543 /* Project object */; +} diff --git a/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..3d62ac3 --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,25 @@ +{ + "object": { + "pins": [ + { + "package": "RxSwift", + "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", + "state": { + "branch": null, + "revision": "9dcaa4b333db437b0fbfaf453fad29069044a8b4", + "version": "6.6.0" + } + }, + { + "package": "RxSwiftExt", + "repositoryURL": "https://github.com/RxSwiftCommunity/RxSwiftExt", + "state": { + "branch": null, + "revision": "a8065d19acbdee55c6e2b2d9a17a420e34ab8d83", + "version": "6.2.0" + } + } + ] + }, + "version": 1 +} diff --git a/.github/workflows/SPMDummy/SPMDummy/AppDelegate.swift b/.github/workflows/SPMDummy/SPMDummy/AppDelegate.swift new file mode 100644 index 0000000..e6f3cb3 --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy/AppDelegate.swift @@ -0,0 +1,63 @@ +// +// AppDelegate.swift +// SPMDummy +// +// Created by Loay Ashraf on 25/08/2023. +// + +#if os(iOS) +import UIKit +import RxNetworkKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} +#elseif os(macOS) +import Cocoa +import RxNetworkKit + +@main +class AppDelegate: NSObject, NSApplicationDelegate { + + + + + func applicationDidFinishLaunching(_ aNotification: Notification) { + // Insert code here to initialize your application + NetworkReachability.shared.start() + } + + func applicationWillTerminate(_ aNotification: Notification) { + // Insert code here to tear down your application + } + + func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } + + +} +#endif diff --git a/.github/workflows/SPMDummy/SPMDummy/Assets.xcassets/AccentColor.colorset/Contents.json b/.github/workflows/SPMDummy/SPMDummy/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/.github/workflows/SPMDummy/SPMDummy/Assets.xcassets/AppIcon.appiconset/Contents.json b/.github/workflows/SPMDummy/SPMDummy/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/.github/workflows/SPMDummy/SPMDummy/Assets.xcassets/Contents.json b/.github/workflows/SPMDummy/SPMDummy/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/.github/workflows/SPMDummy/SPMDummy/Base.lproj/LaunchScreen.storyboard b/.github/workflows/SPMDummy/SPMDummy/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/workflows/SPMDummy/SPMDummy/Base.lproj/Main.storyboard b/.github/workflows/SPMDummy/SPMDummy/Base.lproj/Main.storyboard new file mode 100644 index 0000000..25a7638 --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/workflows/SPMDummy/SPMDummy/Info.plist b/.github/workflows/SPMDummy/SPMDummy/Info.plist new file mode 100644 index 0000000..dd3c9af --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy/Info.plist @@ -0,0 +1,25 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + + diff --git a/.github/workflows/SPMDummy/SPMDummy/SceneDelegate.swift b/.github/workflows/SPMDummy/SPMDummy/SceneDelegate.swift new file mode 100644 index 0000000..9d59122 --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy/SceneDelegate.swift @@ -0,0 +1,53 @@ +// +// SceneDelegate.swift +// SPMDummy +// +// Created by Loay Ashraf on 25/08/2023. +// + +#if os(iOS) +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let _ = (scene as? UIWindowScene) else { return } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} +#endif diff --git a/.github/workflows/SPMDummy/SPMDummy/ViewController.swift b/.github/workflows/SPMDummy/SPMDummy/ViewController.swift new file mode 100644 index 0000000..5dda1ee --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy/ViewController.swift @@ -0,0 +1,30 @@ +// +// ViewController.swift +// SPMDummy +// +// Created by Loay Ashraf on 25/08/2023. +// + +#if os(iOS) +import UIKit + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + + +} +#elseif os(macOS) +import AppKit +class ViewController: NSViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + +} +#endif diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml new file mode 100644 index 0000000..29acff7 --- /dev/null +++ b/.github/workflows/build-ios.yml @@ -0,0 +1,83 @@ +name: Build iOS + +on: + workflow_call: + +jobs: + framework-ios-simulator: + name: Build Framework For iOS Simulator + runs-on: macos-13 + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + - name: Build Framework + env: + workspace: RxNetworkKit.xcworkspace + scheme: RxNetworkKit + destination: generic/platform=iOS Simulator + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} + + framework-ios: + name: Build Framework For iOS Device + runs-on: macos-13 + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + - name: Build Framework + env: + workspace: RxNetworkKit.xcworkspace + scheme: RxNetworkKit + destination: generic/platform=iOS + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} + + example-ios-simulator: + name: Build Example For iOS Simulator + runs-on: macos-13 + needs: framework-ios-simulator + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + - name: Build Example + env: + workspace: RxNetworkKit.xcworkspace + scheme: iOS Example + destination: generic/platform=iOS Simulator + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} + + example-ios: + name: Build Example For iOS Device + runs-on: macos-13 + needs: framework-ios + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + - name: Build Example + env: + workspace: RxNetworkKit.xcworkspace + scheme: iOS Example + destination: generic/platform=iOS + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml new file mode 100644 index 0000000..a9d7faa --- /dev/null +++ b/.github/workflows/build-macos.yml @@ -0,0 +1,45 @@ +name: Build macOS + +on: + workflow_call: + +jobs: + framework-macos: + name: Build Framework For macOS + runs-on: macos-13 + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + - name: Build Framework + env: + workspace: RxNetworkKit.xcworkspace + scheme: RxNetworkKit + destination: generic/platform=macOS + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} + + example-macos: + name: Build Example For macOS + runs-on: macos-13 + needs: framework-macos + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + - name: Build Example + env: + workspace: RxNetworkKit.xcworkspace + scheme: macOS Example + destination: generic/platform=macOS + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..893856b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,18 @@ +name: Build + +on: + push: + branches: + - '**' + pull_request: + branches: + - '**' + +jobs: + build-ios: + name: Build For iOS + uses: ./.github/workflows/build-ios.yml + + build-macos: + name: Build For macOS + uses: ./.github/workflows/build-macos.yml diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml new file mode 100644 index 0000000..b9858eb --- /dev/null +++ b/.github/workflows/pod-lib-lint.yml @@ -0,0 +1,30 @@ +name: Cocoapods Library Lint + +on: + workflow_run: + workflows: + - 'Build' + types: + - completed + branches-ignore: + - 'main' + +jobs: + pod-lib-lint: + name: Lint Library For Cocoapods + runs-on: macos-13 + if: ${{ github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '14.2' + - name: Install Cocoapods + run: | + sudo gem install cocoapods + - name: Library Lint + run: | + pod lib lint --allow-warnings --verbose diff --git a/.github/workflows/pod-trunk-push.yml b/.github/workflows/pod-trunk-push.yml new file mode 100644 index 0000000..2ea9c7e --- /dev/null +++ b/.github/workflows/pod-trunk-push.yml @@ -0,0 +1,32 @@ +name: Cocoapods Trunk Push + +on: + workflow_run: + workflows: + - 'Build' + types: + - completed + branches: + - 'main' + +jobs: + publish-podspec: + name: Push Podspec File To Trunk + runs-on: macos-13 + if: ${{ github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '14.2' + - name: Install Cocoapods + run: | + sudo gem install cocoapods + - name: Trunk Push + env: + COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} + run: | + pod trunk push --allow-warnings --verbose diff --git a/.github/workflows/spm-lint.yml b/.github/workflows/spm-lint.yml new file mode 100644 index 0000000..d7b503d --- /dev/null +++ b/.github/workflows/spm-lint.yml @@ -0,0 +1,40 @@ +name: Swift Package Manager Lint + +on: + workflow_run: + workflows: + - 'Build' + types: + - completed + branches: + - '**' + +jobs: + spm-lint: + name: Lint Package For Swift Package Manager + runs-on: macos-13 + strategy: + matrix: + destination: ['generic/platform=iOS Simulator', + 'generic/platform=iOS', + 'generic/platform=macOS'] + if: ${{ github.event.workflow_run.conclusion == 'success' }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + - name: Update Packages + run: | + swift package update + - name: Build Project + env: + project: ./.github/workflows/SPMDummy/SPMDummy.xcodeproj + scheme: SPMDummy + destination: ${{ matrix.destination }} + run: | + xcodebuild clean build -project "${project}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} + diff --git a/Package.resolved b/Package.resolved index 68394bf..3e0fda4 100644 --- a/Package.resolved +++ b/Package.resolved @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/RxSwiftCommunity/RxSwiftExt", "state": { "branch": null, - "revision": "a42af9caeab63f2dbde318f1f3dc424f05871a37", - "version": "6.1.0" + "revision": "827bd11853983383b708feaf7da95c560982b2b8", + "version": "6.0.1" } } ] diff --git a/Package.swift b/Package.swift index dcb97ff..5b5b387 100644 --- a/Package.swift +++ b/Package.swift @@ -17,8 +17,8 @@ let package = Package( .library(name: "RxNetworkKit", targets: ["RxNetworkKit"]), ], dependencies: [ - .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.5.0")), - .package(url: "https://github.com/RxSwiftCommunity/RxSwiftExt", .upToNextMajor(from: "6.1.0")), + .package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("6.5.0")), + .package(url: "https://github.com/RxSwiftCommunity/RxSwiftExt", .exact("6.0.1")), ], targets: [ .target(name: "RxNetworkKit", dependencies: ["RxSwift", "RxSwiftExt", .product(name: "RxCocoa", package: "RxSwift")], path: "Source"), diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index 5c67b7c..d09005c 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -646,7 +646,7 @@ 0B77DFDF29D964D40077FBC0 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -709,16 +709,16 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ReactiveX/RxSwift"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.0.0; + kind = exactVersion; + version = 6.5.0; }; }; 0B77E0BE29D969370077FBC0 /* XCRemoteSwiftPackageReference "RxSwiftExt" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/RxSwiftCommunity/RxSwiftExt"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.0.0; + kind = exactVersion; + version = 6.0.1; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved index 77b3f33..6ffbce0 100644 --- a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/RxSwiftCommunity/RxSwiftExt", "state" : { - "revision" : "a42af9caeab63f2dbde318f1f3dc424f05871a37", - "version" : "6.1.0" + "revision" : "827bd11853983383b708feaf7da95c560982b2b8", + "version" : "6.0.1" } } ], From 33ebb62efe7bcb42ea1dcdf29067bdf3bd17e8f1 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Sat, 26 Aug 2023 03:40:05 +0300 Subject: [PATCH 07/95] Update CI Workflows --- .github/workflows/pod-lib-lint.yml | 4 ++++ .github/workflows/spm-lint.yml | 4 ++++ Examples/iOS/iOS Example.xcodeproj/project.pbxproj | 8 ++++---- .../macOS/macOS Example.xcodeproj/project.pbxproj | 12 ------------ 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index b9858eb..2e8b754 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -18,6 +18,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + with: + ref: $ + - run: git branch + - run: env - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: diff --git a/.github/workflows/spm-lint.yml b/.github/workflows/spm-lint.yml index d7b503d..7ee18cd 100644 --- a/.github/workflows/spm-lint.yml +++ b/.github/workflows/spm-lint.yml @@ -23,6 +23,10 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + with: + ref: $ + - run: git branch + - run: env - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: diff --git a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj index 26242a9..fafc9ed 100644 --- a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj +++ b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj @@ -22,8 +22,8 @@ 9083E2B5C56C9978FA9A88AF /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4C68630CA1328C530E000D /* ViewModel.swift */; }; A592421CEFFDBE0D3F6A504D /* ViewController+NetworkRequestInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636BE87DD1664C2D7FAECEDF /* ViewController+NetworkRequestInterceptor.swift */; }; BBC4FB455DB9EFC760A182E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23795F79829D049A83D55440 /* Assets.xcassets */; }; - C68C3F692A9402DC0036FF9D /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C68C3F682A9401960036FF9D /* RxNetworkKit.framework */; }; - C68C3F6A2A9402DC0036FF9D /* RxNetworkKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C68C3F682A9401960036FF9D /* RxNetworkKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C68C3F692A9402DC0036FF9D /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C68C3F682A9401960036FF9D /* RxNetworkKit.framework */; platformFilter = ios; }; + C68C3F6A2A9402DC0036FF9D /* RxNetworkKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C68C3F682A9401960036FF9D /* RxNetworkKit.framework */; platformFilter = ios; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DD59F1300712C00933636CC2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D82BA0503FE7CC526E5586A /* SceneDelegate.swift */; }; /* End PBXBuildFile section */ @@ -421,7 +421,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -460,7 +460,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; diff --git a/Examples/macOS/macOS Example.xcodeproj/project.pbxproj b/Examples/macOS/macOS Example.xcodeproj/project.pbxproj index f1b5bf1..a11a105 100644 --- a/Examples/macOS/macOS Example.xcodeproj/project.pbxproj +++ b/Examples/macOS/macOS Example.xcodeproj/project.pbxproj @@ -231,7 +231,6 @@ ); mainGroup = C69FBA0B2A9408740062BFD7; packageReferences = ( - C69FBA2D2A940AD80062BFD7 /* XCRemoteSwiftPackageReference "RxDataSources" */, ); productRefGroup = C69FBA152A9408740062BFD7 /* Products */; projectDirPath = ""; @@ -500,17 +499,6 @@ defaultConfigurationName = Debug; }; /* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - C69FBA2D2A940AD80062BFD7 /* XCRemoteSwiftPackageReference "RxDataSources" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxDataSources"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.0.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ }; rootObject = C69FBA0C2A9408740062BFD7 /* Project object */; } From 088b62c5ce1b3b875b279c46c77498b588131caa Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Sat, 26 Aug 2023 04:04:12 +0300 Subject: [PATCH 08/95] update CI workflows --- .github/workflows/pod-lib-lint.yml | 2 +- .github/workflows/spm-lint.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index 2e8b754..ff528e7 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -19,7 +19,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - ref: $ + ref: ${{ github.event.workflow_run.head_branch }} - run: git branch - run: env - name: Set Xcode Version diff --git a/.github/workflows/spm-lint.yml b/.github/workflows/spm-lint.yml index 7ee18cd..4d88b05 100644 --- a/.github/workflows/spm-lint.yml +++ b/.github/workflows/spm-lint.yml @@ -24,7 +24,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - ref: $ + ref: ${{ github.event.workflow_run.head_branch }} - run: git branch - run: env - name: Set Xcode Version From b91663bbdace0ab405500ccdb0457e977eb2da21 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Sat, 26 Aug 2023 04:30:04 +0300 Subject: [PATCH 09/95] Update CI Workflow --- .github/workflows/pod-lib-lint.yml | 2 -- .github/workflows/spm-lint.yml | 3 --- 2 files changed, 5 deletions(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index ff528e7..f92ecb3 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -20,8 +20,6 @@ jobs: uses: actions/checkout@v3 with: ref: ${{ github.event.workflow_run.head_branch }} - - run: git branch - - run: env - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: diff --git a/.github/workflows/spm-lint.yml b/.github/workflows/spm-lint.yml index 4d88b05..85c150b 100644 --- a/.github/workflows/spm-lint.yml +++ b/.github/workflows/spm-lint.yml @@ -25,8 +25,6 @@ jobs: uses: actions/checkout@v3 with: ref: ${{ github.event.workflow_run.head_branch }} - - run: git branch - - run: env - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: @@ -41,4 +39,3 @@ jobs: destination: ${{ matrix.destination }} run: | xcodebuild clean build -project "${project}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} - From c8846466caff8bab0a34fc9bb0755410ad0bc2b9 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Sat, 26 Aug 2023 19:17:22 +0300 Subject: [PATCH 10/95] Update Dependency Version Rules (#45) * fix: update version rules for SPM to upToNextMajor Resolves: none. * fix: update version rule for SPM in iOS Example project Resolves: none. * fix: update version rules for cocoapods in podspec file Resolves: none. --- Examples/iOS/iOS Example.xcodeproj/project.pbxproj | 2 +- Package.swift | 4 ++-- RxNetworkKit.xcodeproj/project.pbxproj | 8 ++++---- .../xcshareddata/swiftpm/Package.resolved | 8 ++++---- RxNetworkKitX.podspec | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj index fafc9ed..90971f5 100644 --- a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj +++ b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj @@ -549,7 +549,7 @@ repositoryURL = "https://github.com/RxSwiftCommunity/RxDataSources"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 5.0.2; + minimumVersion = 5.0.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/Package.swift b/Package.swift index 5b5b387..7de8193 100644 --- a/Package.swift +++ b/Package.swift @@ -17,8 +17,8 @@ let package = Package( .library(name: "RxNetworkKit", targets: ["RxNetworkKit"]), ], dependencies: [ - .package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("6.5.0")), - .package(url: "https://github.com/RxSwiftCommunity/RxSwiftExt", .exact("6.0.1")), + .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.5.0")), + .package(url: "https://github.com/RxSwiftCommunity/RxSwiftExt", .upToNextMajor(from: "6.0.0")), ], targets: [ .target(name: "RxNetworkKit", dependencies: ["RxSwift", "RxSwiftExt", .product(name: "RxCocoa", package: "RxSwift")], path: "Source"), diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index d09005c..82a98d0 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -709,16 +709,16 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ReactiveX/RxSwift"; requirement = { - kind = exactVersion; - version = 6.5.0; + kind = upToNextMajorVersion; + minimumVersion = 6.5.0; }; }; 0B77E0BE29D969370077FBC0 /* XCRemoteSwiftPackageReference "RxSwiftExt" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/RxSwiftCommunity/RxSwiftExt"; requirement = { - kind = exactVersion; - version = 6.0.1; + kind = upToNextMajorVersion; + minimumVersion = 6.0.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6ffbce0..17e1219 100644 --- a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/ReactiveX/RxSwift", "state" : { - "revision" : "b4307ba0b6425c0ba4178e138799946c3da594f8", - "version" : "6.5.0" + "revision" : "9dcaa4b333db437b0fbfaf453fad29069044a8b4", + "version" : "6.6.0" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/RxSwiftCommunity/RxSwiftExt", "state" : { - "revision" : "827bd11853983383b708feaf7da95c560982b2b8", - "version" : "6.0.1" + "revision" : "a8065d19acbdee55c6e2b2d9a17a420e34ab8d83", + "version" : "6.2.0" } } ], diff --git a/RxNetworkKitX.podspec b/RxNetworkKitX.podspec index 1104206..0b497c5 100644 --- a/RxNetworkKitX.podspec +++ b/RxNetworkKitX.podspec @@ -12,9 +12,9 @@ Pod::Spec.new do |s| s.author = { 'loay-ashraf' => 'loay.ashraf.96@gmail.com' } s.source = { :git => 'https://github.com/loay-ashraf/RxNetworkKit.git', :tag => s.version.to_s } s.framework = "Foundation" - s.dependency 'RxSwift', '~> 6.5.0' - s.dependency 'RxCocoa', '~> 6.5.0' - s.dependency 'RxSwiftExt', '~> 6.0.1' + s.dependency 'RxSwift', '~> 6.5' + s.dependency 'RxCocoa', '~> 6.5' + s.dependency 'RxSwiftExt', '~> 6.0' s.source_files = 'Source/**/*.{swift,m,h}' s.swift_version = '5.0' end From 4795fd78fe9940b1451957d6b07615c68846bddc Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 22:51:39 +0300 Subject: [PATCH 11/95] feat: add WebSocket capability to NetworkManager (#46) Resolves: none. --- RxNetworkKit.xcodeproj/project.pbxproj | 48 +++++++++- .../xcshareddata/swiftpm/Package.resolved | 8 +- Source/Manager/NetworkManager.swift | 14 +++ .../Web Socket/Extensions/Reactive+ping.swift | 27 ++++++ .../Extensions/Reactive+receive.swift | 39 ++++++++ .../Web Socket/Extensions/Reactive+send.swift | 29 ++++++ Source/Web Socket/WebSocket.swift | 90 +++++++++++++++++++ Source/Web Socket/WebSocketCloseCode.swift | 10 +++ Source/Web Socket/WebSocketCloseHandler.swift | 38 ++++++++ Source/Web Socket/WebSocketMessage.swift | 10 +++ 10 files changed, 307 insertions(+), 6 deletions(-) create mode 100644 Source/Web Socket/Extensions/Reactive+ping.swift create mode 100644 Source/Web Socket/Extensions/Reactive+receive.swift create mode 100644 Source/Web Socket/Extensions/Reactive+send.swift create mode 100644 Source/Web Socket/WebSocket.swift create mode 100644 Source/Web Socket/WebSocketCloseCode.swift create mode 100644 Source/Web Socket/WebSocketCloseHandler.swift create mode 100644 Source/Web Socket/WebSocketMessage.swift diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index 82a98d0..b538b6f 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -62,6 +62,13 @@ C6A9BEFA2A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */; }; C6A9BEFD2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */; }; C6A9BEFF2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */; }; + C6BDFFE82ACDF3830022F675 /* Reactive+receive.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFE72ACDF3830022F675 /* Reactive+receive.swift */; }; + C6BDFFEA2ACDF3D90022F675 /* Reactive+send.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFE92ACDF3D90022F675 /* Reactive+send.swift */; }; + C6BDFFEC2ACDF4100022F675 /* Reactive+ping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFEB2ACDF4100022F675 /* Reactive+ping.swift */; }; + C6BDFFEE2ACDF46A0022F675 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFED2ACDF46A0022F675 /* WebSocket.swift */; }; + C6BDFFF02ACDF4AB0022F675 /* WebSocketCloseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFEF2ACDF4AB0022F675 /* WebSocketCloseHandler.swift */; }; + C6BDFFF32ACDF5100022F675 /* WebSocketMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFF22ACDF50F0022F675 /* WebSocketMessage.swift */; }; + C6BDFFF52ACDF5250022F675 /* WebSocketCloseCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFF42ACDF5250022F675 /* WebSocketCloseCode.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -118,6 +125,13 @@ C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+setAdditionalHTTPHeader.swift"; sourceTree = ""; }; C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+setUserAgentHTTPHeader.swift"; sourceTree = ""; }; C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+operatingSystemName.swift"; sourceTree = ""; }; + C6BDFFE72ACDF3830022F675 /* Reactive+receive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+receive.swift"; sourceTree = ""; }; + C6BDFFE92ACDF3D90022F675 /* Reactive+send.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+send.swift"; sourceTree = ""; }; + C6BDFFEB2ACDF4100022F675 /* Reactive+ping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+ping.swift"; sourceTree = ""; }; + C6BDFFED2ACDF46A0022F675 /* WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = ""; }; + C6BDFFEF2ACDF4AB0022F675 /* WebSocketCloseHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocketCloseHandler.swift; sourceTree = ""; }; + C6BDFFF22ACDF50F0022F675 /* WebSocketMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocketMessage.swift; sourceTree = ""; }; + C6BDFFF42ACDF5250022F675 /* WebSocketCloseCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocketCloseCode.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -155,6 +169,7 @@ 0B77E04A29D965D30077FBC0 /* Source */ = { isa = PBXGroup; children = ( + C6BDFFE62ACDF3260022F675 /* Web Socket */, 0B77E04B29D965D30077FBC0 /* Custom Requests */, 0B77E05929D965D30077FBC0 /* Request Interceptor */, 0B77E05F29D965D30077FBC0 /* Manager */, @@ -181,9 +196,9 @@ 0B77E04C29D965D30077FBC0 /* Download */ = { isa = PBXGroup; children = ( - 0B77E04D29D965D30077FBC0 /* Reactive+URLSessionDownloadResponse.swift */, 0B77E04E29D965D30077FBC0 /* DownloadEvent.swift */, 0B77E04F29D965D30077FBC0 /* URLSession+DownloadTask.swift */, + 0B77E04D29D965D30077FBC0 /* Reactive+URLSessionDownloadResponse.swift */, 0B77E05029D965D30077FBC0 /* Reactive+URLSessionAdaptedDownloadResponse.swift */, ); path = Download; @@ -196,9 +211,9 @@ 0B77E05329D965D30077FBC0 /* UploadFile.swift */, 0B77E05429D965D30077FBC0 /* URLSession+UploadTask.swift */, 0B77E05529D965D30077FBC0 /* Data+AppendString.swift */, - 0B77E05629D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift */, 0B77E05729D965D30077FBC0 /* UploadEvent.swift */, 0B77E05829D965D30077FBC0 /* Reactive+URLSessionUploadResponse.swift */, + 0B77E05629D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift */, ); path = Upload; sourceTree = ""; @@ -338,6 +353,28 @@ path = Router; sourceTree = ""; }; + C6BDFFE62ACDF3260022F675 /* Web Socket */ = { + isa = PBXGroup; + children = ( + C6BDFFF12ACDF4CB0022F675 /* Extensions */, + C6BDFFED2ACDF46A0022F675 /* WebSocket.swift */, + C6BDFFEF2ACDF4AB0022F675 /* WebSocketCloseHandler.swift */, + C6BDFFF22ACDF50F0022F675 /* WebSocketMessage.swift */, + C6BDFFF42ACDF5250022F675 /* WebSocketCloseCode.swift */, + ); + path = "Web Socket"; + sourceTree = ""; + }; + C6BDFFF12ACDF4CB0022F675 /* Extensions */ = { + isa = PBXGroup; + children = ( + C6BDFFE72ACDF3830022F675 /* Reactive+receive.swift */, + C6BDFFE92ACDF3D90022F675 /* Reactive+send.swift */, + C6BDFFEB2ACDF4100022F675 /* Reactive+ping.swift */, + ); + path = Extensions; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -427,12 +464,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C6BDFFF02ACDF4AB0022F675 /* WebSocketCloseHandler.swift in Sources */, 0B77E0AD29D965D30077FBC0 /* Single+CatchErrors.swift in Sources */, 0B77E0B529D965D30077FBC0 /* NetworkRouter.swift in Sources */, 0B77E09529D965D30077FBC0 /* RequestRetryPolicy.swift in Sources */, 0B77E0B329D965D30077FBC0 /* Reactive+Curl.swift in Sources */, C6A9BEF82A93E2D600459E32 /* HTTPErrorBody.swift in Sources */, 0B77E0A929D965D30077FBC0 /* NetworkError.swift in Sources */, + C6BDFFEA2ACDF3D90022F675 /* Reactive+send.swift in Sources */, 0B77E09F29D965D30077FBC0 /* NWPath+InterfaceType.swift in Sources */, 0B77E09C29D965D30077FBC0 /* HTTPScheme.swift in Sources */, 0B77E0A029D965D30077FBC0 /* NetworkReachabilityStatus.swift in Sources */, @@ -454,11 +493,13 @@ C6A9BEFD2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift in Sources */, 0B77E09129D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift in Sources */, C6A9BEFA2A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift in Sources */, + C6BDFFF32ACDF5100022F675 /* WebSocketMessage.swift in Sources */, 0B77E0A629D965D30077FBC0 /* DefaultNetworkAPIError.swift in Sources */, 0B77E0A429D965D30077FBC0 /* NetworkReachability.swift in Sources */, 0B77E0B229D965D30077FBC0 /* Completable+Retry.swift in Sources */, 0B77E0B029D965D30077FBC0 /* Observable+Retry.swift in Sources */, 0B77E08B29D965D30077FBC0 /* URLSession+DownloadTask.swift in Sources */, + C6BDFFF52ACDF5250022F675 /* WebSocketCloseCode.swift in Sources */, 0B77E0A729D965D30077FBC0 /* NetworkServerError.swift in Sources */, 0B77E0B629D965D30077FBC0 /* NetworkUploadRouter.swift in Sources */, C6A9BEF62A93E2AE00459E32 /* DefaultHTTPErrorBody.swift in Sources */, @@ -469,13 +510,16 @@ 0B77E09629D965D30077FBC0 /* RequestAdapter.swift in Sources */, 0B77E0A829D965D30077FBC0 /* NetworkClientError.swift in Sources */, 0B77E0A229D965D30077FBC0 /* NWInterfaceType+CaseIterable.swift in Sources */, + C6BDFFEE2ACDF46A0022F675 /* WebSocket.swift in Sources */, 0B77E09329D965D30077FBC0 /* Reactive+URLSessionUploadResponse.swift in Sources */, 0B77E09B29D965D30077FBC0 /* HTTPMethod.swift in Sources */, 0B77E0AC29D965D30077FBC0 /* Single+Decode.swift in Sources */, 0B77E0A329D965D30077FBC0 /* NetworkInterfaceType.swift in Sources */, 0B77E09229D965D30077FBC0 /* UploadEvent.swift in Sources */, 0B77E09029D965D30077FBC0 /* Data+AppendString.swift in Sources */, + C6BDFFE82ACDF3830022F675 /* Reactive+receive.swift in Sources */, 0B77E08929D965D30077FBC0 /* Reactive+URLSessionDownloadResponse.swift in Sources */, + C6BDFFEC2ACDF4100022F675 /* Reactive+ping.swift in Sources */, 0B77E0AA29D965D30077FBC0 /* NetworkAPIError.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved index 17e1219..6ffbce0 100644 --- a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/ReactiveX/RxSwift", "state" : { - "revision" : "9dcaa4b333db437b0fbfaf453fad29069044a8b4", - "version" : "6.6.0" + "revision" : "b4307ba0b6425c0ba4178e138799946c3da594f8", + "version" : "6.5.0" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/RxSwiftCommunity/RxSwiftExt", "state" : { - "revision" : "a8065d19acbdee55c6e2b2d9a17a420e34ab8d83", - "version" : "6.2.0" + "revision" : "827bd11853983383b708feaf7da95c560982b2b8", + "version" : "6.0.1" } } ], diff --git a/Source/Manager/NetworkManager.swift b/Source/Manager/NetworkManager.swift index db90b27..1a69f5f 100644 --- a/Source/Manager/NetworkManager.swift +++ b/Source/Manager/NetworkManager.swift @@ -24,6 +24,7 @@ public class NetworkManager { // Apply User-Agent header as a part of HTTP aditional headers. configuration.setUserAgentHTTPHeader() // Initialize manager's properties. + URLSession.rx.shouldLogRequest = { _ in false } self.session = .init(configuration: configuration, delegate: eventMonitor, delegateQueue: nil) self.requestInterceptor = requestInterceptor self.eventMonitor = eventMonitor @@ -172,4 +173,17 @@ public class NetworkManager { .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } + /// Creats websocket object and establishes connection using provided url and protocols. + /// + /// - Parameters: + /// - url: `URL` of websocket server. + /// - protocols: `[String]` websocket connection protocols. + /// - closeHandler: `WebSocketCloseHandler` used to provide close coda and reason upon connection closure. + /// + /// - Returns: `WebSocket` object that represents the connection. + public func webSocket(_ url: URL, _ protocols: [String], _ closeHandler: WebSocketCloseHandler) -> WebSocket { + let task = session.webSocketTask(with: url, protocols: protocols) + let webSocket = WebSocket(task: task, closeHandler: closeHandler) + return webSocket + } } diff --git a/Source/Web Socket/Extensions/Reactive+ping.swift b/Source/Web Socket/Extensions/Reactive+ping.swift new file mode 100644 index 0000000..10ae88b --- /dev/null +++ b/Source/Web Socket/Extensions/Reactive+ping.swift @@ -0,0 +1,27 @@ +// +// Reactive+ping.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 04/10/2023. +// + +import Foundation +import RxSwift + +extension Reactive where Base: URLSessionWebSocketTask { + /// Sends a ping to the websocket server. + /// + /// - Returns: `Completable` observable encapsulating send ping operation. + func ping() -> Completable { + Completable.create { subscription in + base.sendPing { error in + guard let error = error else { + subscription(.completed) + return + } + subscription(.error(error)) + } + return Disposables.create() + } + } +} diff --git a/Source/Web Socket/Extensions/Reactive+receive.swift b/Source/Web Socket/Extensions/Reactive+receive.swift new file mode 100644 index 0000000..701fb75 --- /dev/null +++ b/Source/Web Socket/Extensions/Reactive+receive.swift @@ -0,0 +1,39 @@ +// +// Reactive+receive.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 04/10/2023. +// + +import Foundation +import RxSwift + +extension Reactive where Base: URLSessionWebSocketTask { + /// Starts receiving messages sent from websocket server. + /// + /// - Parameter closeHandler: `WebSocketCloseHandler` used to provide close coda and reason upon connection closure. + /// + /// - Returns: `Observable` object encapsulating received messages. + func receive(closeHandler: WebSocketCloseHandler) -> Observable { + func receive(subscription: AnyObserver) { + base.receive { result in + guard base.closeCode == .invalid else { return } + switch result { + case .success(let message): + subscription.onNext(message) + receive(subscription: subscription) + case .failure(let error): + subscription.onError(error) + } + } + } + return Observable.create { subscription in + receive(subscription: subscription) + return Disposables.create { + let request = base.currentRequest + base.cancel(with: closeHandler.code(for: request), reason: closeHandler.reason(for: request)) + } + } + .share() + } +} diff --git a/Source/Web Socket/Extensions/Reactive+send.swift b/Source/Web Socket/Extensions/Reactive+send.swift new file mode 100644 index 0000000..383f7d1 --- /dev/null +++ b/Source/Web Socket/Extensions/Reactive+send.swift @@ -0,0 +1,29 @@ +// +// Reactive+send.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 04/10/2023. +// + +import Foundation +import RxSwift + +extension Reactive where Base: URLSessionWebSocketTask { + /// Sends message to the websocket server. + /// + /// - Parameter message: `WebSocketMessage` to be sent to websocket server. + /// + /// - Returns: `Completable` observable encapsulating send message operation. + func send(message: WebSocketMessage) -> Completable { + Completable.create { subscription in + base.send(message) { error in + guard let error = error else { + subscription(.completed) + return + } + subscription(.error(error)) + } + return Disposables.create() + } + } +} diff --git a/Source/Web Socket/WebSocket.swift b/Source/Web Socket/WebSocket.swift new file mode 100644 index 0000000..2c86f2a --- /dev/null +++ b/Source/Web Socket/WebSocket.swift @@ -0,0 +1,90 @@ +// +// WebSocket.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 04/10/2023. +// + +import Foundation +import RxSwift +import RxRelay + +public class WebSocket { + public let text: PublishRelay = .init() + public let data: PublishRelay = .init() + public let error: PublishRelay = .init() + private let task: URLSessionWebSocketTask + private let closeHandler: WebSocketCloseHandler + private let receiveObservable: Observable + private let disposeBag: DisposeBag = .init() + + /// Creates `WebSocket` instance with generic tyoe `T`. + /// + /// - Parameters: + /// - task: `URLSessionWebSocketTask` that represents the connection to websocket server. + /// - closeHandler: `WebSocketCloseHandler` used to provide close coda and reason upon connection closure. + init(task: URLSessionWebSocketTask, closeHandler: WebSocketCloseHandler) { + self.task = task + self.closeHandler = closeHandler + self.receiveObservable = task.rx.receive(closeHandler: closeHandler) + setupBindings() + } + /// Resumes current task to establish the connection. + public func connect() { + task.resume() + } + /// Cancels the request associated with current task to close the connection. + public func disconnect() { + let request = task.currentRequest + task.cancel(with: closeHandler.code(for: request), reason: closeHandler.reason(for: request)) + } + /// Sends message to the websocket server. + /// + /// - Parameter message: `WebSocketMessage` to be sent to websocket server. + /// + /// - Returns: `Completable` observable encapsulating send message operation. + public func send(_ message: WebSocketMessage) -> Completable { + task.rx.send(message: message) + } + /// Sends a ping to the websocket server. + /// + /// - Returns: `Completable` observable encapsulating send ping operation. + public func ping() -> Completable { + task.rx.ping() + } + /// Sets up internal observable bindings. + private func setupBindings() { + receiveObservable + .materialize() + .compactMap({ + guard case .next(let element) = $0, case .string(let text) = element else { return nil } + return text + }) + .bind(to: text) + .disposed(by: disposeBag) + receiveObservable + .materialize() + .compactMap({ + guard case .next(let element) = $0, case .data(let data) = element else { return nil } + return data + }) + .flatMap({ (data: Data) in + if T.self == Data.self { + return Observable.just(data as! T) + } else { + return Observable.just(data) + .decode(type: T.self, decoder: JSONDecoder()) + } + }) + .bind(to: data) + .disposed(by: disposeBag) + receiveObservable + .materialize() + .compactMap({ + guard case .error(let error) = $0 else { return nil } + return error + }) + .bind(to: error) + .disposed(by: disposeBag) + } +} diff --git a/Source/Web Socket/WebSocketCloseCode.swift b/Source/Web Socket/WebSocketCloseCode.swift new file mode 100644 index 0000000..88e3b02 --- /dev/null +++ b/Source/Web Socket/WebSocketCloseCode.swift @@ -0,0 +1,10 @@ +// +// WebSocketCloseCode.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 04/10/2023. +// + +import Foundation + +public typealias WebSocketCloseCode = URLSessionWebSocketTask.CloseCode diff --git a/Source/Web Socket/WebSocketCloseHandler.swift b/Source/Web Socket/WebSocketCloseHandler.swift new file mode 100644 index 0000000..c5e0693 --- /dev/null +++ b/Source/Web Socket/WebSocketCloseHandler.swift @@ -0,0 +1,38 @@ +// +// WebSocketCloseHandler.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 04/10/2023. +// + +import Foundation + +public class WebSocketCloseHandler { + private let _code: (URLRequest?) -> WebSocketCloseCode + private let _reason: (URLRequest?) -> Data? + /// Creates `WebSocketCloseHandler` instance. + /// + /// - Parameters: + /// - code: a closure used to provide close code for a given `URLRequest` object. + /// - reason: a closure used to provide close reason for a given `URLRequest` object. + public init(code: @escaping (URLRequest?) -> WebSocketCloseCode, reason: @escaping (URLRequest?) -> Data?) { + self._code = code + self._reason = reason + } + /// Provides close code for a given request. + /// + /// - Parameter request: `URLRequest?` object used to provide close code. + /// + /// - Returns: `WebSocketCloseCode` close code provided using given request. + func code(for request: URLRequest?) -> WebSocketCloseCode { + _code(request) + } + /// Provides close reason for a given request. + /// + /// - Parameter request: `URLRequest?` object used to provide close reason. + /// + /// - Returns: `Data?` close reason provided using given request. + func reason(for request: URLRequest?) -> Data? { + _reason(request) + } +} diff --git a/Source/Web Socket/WebSocketMessage.swift b/Source/Web Socket/WebSocketMessage.swift new file mode 100644 index 0000000..c79c8b3 --- /dev/null +++ b/Source/Web Socket/WebSocketMessage.swift @@ -0,0 +1,10 @@ +// +// WebSocketMessage.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 04/10/2023. +// + +import Foundation + +public typealias WebSocketMessage = URLSessionWebSocketTask.Message From b7935cdf7b8df0b40f6423982eb377ba549a9634 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 22:56:36 +0300 Subject: [PATCH 12/95] fix a typo --- Source/Manager/NetworkManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Manager/NetworkManager.swift b/Source/Manager/NetworkManager.swift index 1a69f5f..86cca18 100644 --- a/Source/Manager/NetworkManager.swift +++ b/Source/Manager/NetworkManager.swift @@ -173,7 +173,7 @@ public class NetworkManager { .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } - /// Creats websocket object and establishes connection using provided url and protocols. + /// Creates websocket object and establishes connection using provided url and protocols. /// /// - Parameters: /// - url: `URL` of websocket server. From 30d6c91224cfd3780fff4ed12f35383a2b16a690 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:09:26 +0300 Subject: [PATCH 13/95] update dependencies versions Resolves: none. --- RxNetworkKit.xcodeproj/project.pbxproj | 4 ++-- .../xcshareddata/swiftpm/Package.resolved | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index b538b6f..8488115 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -754,7 +754,7 @@ repositoryURL = "https://github.com/ReactiveX/RxSwift"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 6.5.0; + minimumVersion = 6.6.0; }; }; 0B77E0BE29D969370077FBC0 /* XCRemoteSwiftPackageReference "RxSwiftExt" */ = { @@ -762,7 +762,7 @@ repositoryURL = "https://github.com/RxSwiftCommunity/RxSwiftExt"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 6.0.0; + minimumVersion = 6.2.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6ffbce0..e70bc7c 100644 --- a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/ReactiveX/RxSwift", "state" : { - "revision" : "b4307ba0b6425c0ba4178e138799946c3da594f8", - "version" : "6.5.0" + "revision" : "9dcaa4b333db437b0fbfaf453fad29069044a8b4", + "version" : "6.6.0" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/RxSwiftCommunity/RxSwiftExt", "state" : { - "revision" : "827bd11853983383b708feaf7da95c560982b2b8", - "version" : "6.0.1" + "revision" : "eb4adf9f00a21b3efc3869a5218a6d7517e95222", + "version" : "6.2.1" } } ], From 48bf7eaad7bb139b12a139aeacc42f0baa233172 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:10:07 +0300 Subject: [PATCH 14/95] update Package.swift + podspec file --- Package.swift | 4 ++-- RxNetworkKitX.podspec | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Package.swift b/Package.swift index 7de8193..2f3913c 100644 --- a/Package.swift +++ b/Package.swift @@ -17,8 +17,8 @@ let package = Package( .library(name: "RxNetworkKit", targets: ["RxNetworkKit"]), ], dependencies: [ - .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.5.0")), - .package(url: "https://github.com/RxSwiftCommunity/RxSwiftExt", .upToNextMajor(from: "6.0.0")), + .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.6.0")), + .package(url: "https://github.com/RxSwiftCommunity/RxSwiftExt", .upToNextMajor(from: "6.2.0")), ], targets: [ .target(name: "RxNetworkKit", dependencies: ["RxSwift", "RxSwiftExt", .product(name: "RxCocoa", package: "RxSwift")], path: "Source"), diff --git a/RxNetworkKitX.podspec b/RxNetworkKitX.podspec index 0b497c5..86868bd 100644 --- a/RxNetworkKitX.podspec +++ b/RxNetworkKitX.podspec @@ -4,7 +4,7 @@ Pod::Spec.new do |s| s.macos.deployment_target = '11.0' s.name = 'RxNetworkKitX' s.module_name = 'RxNetworkKit' - s.version = '0.0.2' + s.version = '0.1.0' s.summary = 'a lightweight FRP networking framework.' s.description = 'a FRP networking framework built on top of URLSession and uses RxSwift and RxCocoa.' s.homepage = 'https://github.com/loay-ashraf/RxNetworkKit' @@ -12,9 +12,9 @@ Pod::Spec.new do |s| s.author = { 'loay-ashraf' => 'loay.ashraf.96@gmail.com' } s.source = { :git => 'https://github.com/loay-ashraf/RxNetworkKit.git', :tag => s.version.to_s } s.framework = "Foundation" - s.dependency 'RxSwift', '~> 6.5' - s.dependency 'RxCocoa', '~> 6.5' - s.dependency 'RxSwiftExt', '~> 6.0' + s.dependency 'RxSwift', '~> 6.6' + s.dependency 'RxCocoa', '~> 6.6' + s.dependency 'RxSwiftExt', '~> 6.2' s.source_files = 'Source/**/*.{swift,m,h}' s.swift_version = '5.0' end From 106245c0be77c85c26f325324a37ddfa9f9a2e43 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:14:56 +0300 Subject: [PATCH 15/95] change xcode version used in CI/CD to 14.3.1 Resolves: none. --- .github/workflows/build-ios.yml | 8 ++++---- .github/workflows/build-macos.yml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index 29acff7..7066d7e 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -14,7 +14,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '14.3.1' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace @@ -33,7 +33,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '14.3.1' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace @@ -53,7 +53,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '14.3.1' - name: Build Example env: workspace: RxNetworkKit.xcworkspace @@ -73,7 +73,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '14.3.1' - name: Build Example env: workspace: RxNetworkKit.xcworkspace diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index a9d7faa..bc351c2 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -14,7 +14,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '14.3.1' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace @@ -34,7 +34,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '14.3.1' - name: Build Example env: workspace: RxNetworkKit.xcworkspace From d7e4ec589ceac6cccb163db28c02832127c4aaf1 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:40:23 +0300 Subject: [PATCH 16/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index f92ecb3..73e8d5a 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -1,13 +1,16 @@ name: Cocoapods Library Lint on: - workflow_run: - workflows: - - 'Build' - types: - - completed - branches-ignore: - - 'main' + push: + branches: + - 'develop' +# workflow_run: +# workflows: +# - 'Build' +# types: +# - completed +# branches-ignore: +# - 'main' jobs: pod-lib-lint: @@ -23,7 +26,11 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.2' + xcode-version: '14.3.1' + - name: List Apps + run: | + cd ./Applications + ls - name: Install Cocoapods run: | sudo gem install cocoapods From 81ae4848432c7ff562d4c5e6c530b63ce769881c Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:42:03 +0300 Subject: [PATCH 17/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index 73e8d5a..cfb9278 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -3,7 +3,7 @@ name: Cocoapods Library Lint on: push: branches: - - 'develop' + - ** # workflow_run: # workflows: # - 'Build' From cb2672e62ac3352cc9416714612d966253f06539 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:42:48 +0300 Subject: [PATCH 18/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index cfb9278..6df13f2 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -3,7 +3,7 @@ name: Cocoapods Library Lint on: push: branches: - - ** + - '**' # workflow_run: # workflows: # - 'Build' From 101872986d9ed676041c7a606b35e79348491c60 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:44:56 +0300 Subject: [PATCH 19/95] Update build.yml --- .github/workflows/build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 893856b..8b74e68 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,12 +1,12 @@ name: Build -on: - push: - branches: - - '**' - pull_request: - branches: - - '**' +#on: +# push: +# branches: +# - '**' +# pull_request: +# branches: +# - '**' jobs: build-ios: From 65100f0091adb00b58d72d87b22a166187328318 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:45:37 +0300 Subject: [PATCH 20/95] Update build.yml --- .github/workflows/build.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8b74e68..55f39cd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,5 @@ -name: Build - +#name: Build +# #on: # push: # branches: @@ -7,12 +7,12 @@ name: Build # pull_request: # branches: # - '**' - -jobs: - build-ios: - name: Build For iOS - uses: ./.github/workflows/build-ios.yml - - build-macos: - name: Build For macOS - uses: ./.github/workflows/build-macos.yml +# +#jobs: +# build-ios: +# name: Build For iOS +# uses: ./.github/workflows/build-ios.yml +# +# build-macos: +# name: Build For macOS +# uses: ./.github/workflows/build-macos.yml From be2f64a5f211df8021c9199ec2b257a6fc140193 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:46:49 +0300 Subject: [PATCH 21/95] update workflow files --- .github/workflows/build.yml | 36 +++++++++++++++--------------- .github/workflows/pod-lib-lint.yml | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 55f39cd..893856b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,18 +1,18 @@ -#name: Build -# -#on: -# push: -# branches: -# - '**' -# pull_request: -# branches: -# - '**' -# -#jobs: -# build-ios: -# name: Build For iOS -# uses: ./.github/workflows/build-ios.yml -# -# build-macos: -# name: Build For macOS -# uses: ./.github/workflows/build-macos.yml +name: Build + +on: + push: + branches: + - '**' + pull_request: + branches: + - '**' + +jobs: + build-ios: + name: Build For iOS + uses: ./.github/workflows/build-ios.yml + + build-macos: + name: Build For macOS + uses: ./.github/workflows/build-macos.yml diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index 6df13f2..3fec2e3 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -16,7 +16,7 @@ jobs: pod-lib-lint: name: Lint Library For Cocoapods runs-on: macos-13 - if: ${{ github.event.workflow_run.conclusion == 'success' }} +# if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout From 42eca2a2b56f150063636c7fcd3c1035e0bdf596 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:47:46 +0300 Subject: [PATCH 22/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index 3fec2e3..be979e4 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -29,7 +29,7 @@ jobs: xcode-version: '14.3.1' - name: List Apps run: | - cd ./Applications + cd ~/Applications ls - name: Install Cocoapods run: | From 4e434e47a330513d1ce1b815da53edf3c870e80d Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:52:21 +0300 Subject: [PATCH 23/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index be979e4..0a861d1 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -29,8 +29,7 @@ jobs: xcode-version: '14.3.1' - name: List Apps run: | - cd ~/Applications - ls + which Xcode - name: Install Cocoapods run: | sudo gem install cocoapods From 7ad7842864b15b784ddaa63c5606094146fe4053 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:53:25 +0300 Subject: [PATCH 24/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index 0a861d1..7070a0b 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -29,7 +29,8 @@ jobs: xcode-version: '14.3.1' - name: List Apps run: | - which Xcode + cd /Applications + ls - name: Install Cocoapods run: | sudo gem install cocoapods From 797752aafc4aea7f80225d4c530d04732ca1d555 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 4 Oct 2023 23:58:51 +0300 Subject: [PATCH 25/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index 7070a0b..55f4e66 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -29,8 +29,10 @@ jobs: xcode-version: '14.3.1' - name: List Apps run: | - cd /Applications - ls + cd /Applications/Xcode_14.3.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/ + sudo mkdir arc + cd arc + sudo git clone https://github.com/kamyarelyasi/Libarclite-Files.git - name: Install Cocoapods run: | sudo gem install cocoapods From 3276f6dde66a4f03f65b8a19beaba41403db5742 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 00:21:01 +0300 Subject: [PATCH 26/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index 55f4e66..2d60053 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -27,11 +27,11 @@ jobs: uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '14.3.1' - - name: List Apps + - name: Add Missing Lib Files (for iOS 9.0 and macOS 10.9) run: | - cd /Applications/Xcode_14.3.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/ + cd /Applications/Xcode_14.3.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib sudo mkdir arc - cd arc + cd arc sudo git clone https://github.com/kamyarelyasi/Libarclite-Files.git - name: Install Cocoapods run: | From 101e691f462f73e0dfe3b75b8236f07f7b235c78 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 00:28:48 +0300 Subject: [PATCH 27/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index 2d60053..95e2ca9 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -33,6 +33,7 @@ jobs: sudo mkdir arc cd arc sudo git clone https://github.com/kamyarelyasi/Libarclite-Files.git + ls - name: Install Cocoapods run: | sudo gem install cocoapods From 1d6ee4b6120033f9bd541ec72993a9c35e2d027d Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 00:30:25 +0300 Subject: [PATCH 28/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index 95e2ca9..e1d2c19 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -32,8 +32,7 @@ jobs: cd /Applications/Xcode_14.3.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib sudo mkdir arc cd arc - sudo git clone https://github.com/kamyarelyasi/Libarclite-Files.git - ls + sudo git clone https://github.com/kamyarelyasi/Libarclite-Files.git . - name: Install Cocoapods run: | sudo gem install cocoapods From eba946256158ad11762fb5fbb8e68f2f7c2202b3 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 00:39:55 +0300 Subject: [PATCH 29/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index e1d2c19..dedf7c6 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -1,22 +1,19 @@ name: Cocoapods Library Lint on: - push: - branches: - - '**' -# workflow_run: -# workflows: -# - 'Build' -# types: -# - completed -# branches-ignore: -# - 'main' + workflow_run: + workflows: + - 'Build' + types: + - completed + branches-ignore: + - 'main' jobs: pod-lib-lint: name: Lint Library For Cocoapods runs-on: macos-13 -# if: ${{ github.event.workflow_run.conclusion == 'success' }} + if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout From 585fd9264a560c4b69c2d2388c476815127eaecc Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 00:41:36 +0300 Subject: [PATCH 30/95] Update pod-trunk-push.yml --- .github/workflows/pod-trunk-push.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pod-trunk-push.yml b/.github/workflows/pod-trunk-push.yml index 2ea9c7e..7f0effb 100644 --- a/.github/workflows/pod-trunk-push.yml +++ b/.github/workflows/pod-trunk-push.yml @@ -21,7 +21,13 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.2' + xcode-version: '14.3.1' + - name: Add Missing Lib Files (for iOS 9.0 and macOS 10.9) + run: | + cd /Applications/Xcode_14.3.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib + sudo mkdir arc + cd arc + sudo git clone https://github.com/kamyarelyasi/Libarclite-Files.git . - name: Install Cocoapods run: | sudo gem install cocoapods From 006acea12162ade64c717d2f4f5cd870ba2d2d3f Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 00:46:28 +0300 Subject: [PATCH 31/95] Update spm-lint.yml --- .github/workflows/spm-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spm-lint.yml b/.github/workflows/spm-lint.yml index 85c150b..2b1b6f4 100644 --- a/.github/workflows/spm-lint.yml +++ b/.github/workflows/spm-lint.yml @@ -28,7 +28,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: latest-stable + xcode-version: '14.3.1' - name: Update Packages run: | swift package update From 96a6c1650c8292fbdc19bc242249681cff570165 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 01:44:06 +0300 Subject: [PATCH 32/95] Update build-macos.yml --- .github/workflows/build-macos.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index bc351c2..efd1eb5 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -14,7 +14,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.3.1' + xcode-version: '15.0' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace @@ -34,7 +34,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.3.1' + xcode-version: '15.0' - name: Build Example env: workspace: RxNetworkKit.xcworkspace From 318f32a438033ba10d5754fa553cb01d8ae79ad5 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 01:59:14 +0300 Subject: [PATCH 33/95] update Xcode version to 15.0.0 for CI workflows --- .github/workflows/build-ios.yml | 8 ++++---- .github/workflows/build-macos.yml | 4 ++-- .github/workflows/pod-lib-lint.yml | 4 ++-- .github/workflows/pod-trunk-push.yml | 4 ++-- .github/workflows/spm-lint.yml | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index 7066d7e..c411fd3 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -14,7 +14,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.3.1' + xcode-version: '15.0.0' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace @@ -33,7 +33,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.3.1' + xcode-version: '15.0.0' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace @@ -53,7 +53,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.3.1' + xcode-version: '15.0.0' - name: Build Example env: workspace: RxNetworkKit.xcworkspace @@ -73,7 +73,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.3.1' + xcode-version: '15.0.0' - name: Build Example env: workspace: RxNetworkKit.xcworkspace diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index efd1eb5..2e086b6 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -14,7 +14,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.0' + xcode-version: '15.0.0' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace @@ -34,7 +34,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.0' + xcode-version: '15.0.0' - name: Build Example env: workspace: RxNetworkKit.xcworkspace diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index dedf7c6..1e4a715 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -23,10 +23,10 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.3.1' + xcode-version: '15.0.0' - name: Add Missing Lib Files (for iOS 9.0 and macOS 10.9) run: | - cd /Applications/Xcode_14.3.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib + cd /Applications/Xcode_15.0.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib sudo mkdir arc cd arc sudo git clone https://github.com/kamyarelyasi/Libarclite-Files.git . diff --git a/.github/workflows/pod-trunk-push.yml b/.github/workflows/pod-trunk-push.yml index 7f0effb..9350f27 100644 --- a/.github/workflows/pod-trunk-push.yml +++ b/.github/workflows/pod-trunk-push.yml @@ -21,10 +21,10 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.3.1' + xcode-version: '15.0.0' - name: Add Missing Lib Files (for iOS 9.0 and macOS 10.9) run: | - cd /Applications/Xcode_14.3.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib + cd /Applications/Xcode_15.0.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib sudo mkdir arc cd arc sudo git clone https://github.com/kamyarelyasi/Libarclite-Files.git . diff --git a/.github/workflows/spm-lint.yml b/.github/workflows/spm-lint.yml index 2b1b6f4..0b3fb98 100644 --- a/.github/workflows/spm-lint.yml +++ b/.github/workflows/spm-lint.yml @@ -28,7 +28,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '14.3.1' + xcode-version: '15.0.0' - name: Update Packages run: | swift package update From 907121b1a485a5bfcad634d1d54658e9f3782d37 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 02:40:54 +0300 Subject: [PATCH 34/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index 1e4a715..7010e7b 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -36,3 +36,4 @@ jobs: - name: Library Lint run: | pod lib lint --allow-warnings --verbose + From bb48adf769e12724e4852282cf52baf2caa17967 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 02:42:01 +0300 Subject: [PATCH 35/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index 7010e7b..b886bc5 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -1,19 +1,22 @@ name: Cocoapods Library Lint on: - workflow_run: - workflows: - - 'Build' - types: - - completed - branches-ignore: - - 'main' +push: + branches: + - '**' +# workflow_run: +# workflows: +# - 'Build' +# types: +# - completed +# branches-ignore: +# - 'main' jobs: pod-lib-lint: name: Lint Library For Cocoapods runs-on: macos-13 - if: ${{ github.event.workflow_run.conclusion == 'success' }} +# if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout From d7443146e90369d991cdee2c33df423f32bac3b2 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 02:43:06 +0300 Subject: [PATCH 36/95] Update pod-lib-lint.yml --- .github/workflows/pod-lib-lint.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml index b886bc5..8245f50 100644 --- a/.github/workflows/pod-lib-lint.yml +++ b/.github/workflows/pod-lib-lint.yml @@ -1,9 +1,9 @@ name: Cocoapods Library Lint on: -push: - branches: - - '**' + push: + branches: + - '**' # workflow_run: # workflows: # - 'Build' From e4e5f38859d2a51c0d82cfa5fca42279f9ec95e4 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 04:03:56 +0300 Subject: [PATCH 37/95] break: drop support for Cocoapods (cannot work with Xcode 15) Resolves: none. --- .github/workflows/pod-lib-lint.yml | 42 ---------------------------- .github/workflows/pod-trunk-push.yml | 38 ------------------------- RxNetworkKitX.podspec | 20 ------------- 3 files changed, 100 deletions(-) delete mode 100644 .github/workflows/pod-lib-lint.yml delete mode 100644 .github/workflows/pod-trunk-push.yml delete mode 100644 RxNetworkKitX.podspec diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml deleted file mode 100644 index 8245f50..0000000 --- a/.github/workflows/pod-lib-lint.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Cocoapods Library Lint - -on: - push: - branches: - - '**' -# workflow_run: -# workflows: -# - 'Build' -# types: -# - completed -# branches-ignore: -# - 'main' - -jobs: - pod-lib-lint: - name: Lint Library For Cocoapods - runs-on: macos-13 -# if: ${{ github.event.workflow_run.conclusion == 'success' }} - - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: ${{ github.event.workflow_run.head_branch }} - - name: Set Xcode Version - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: '15.0.0' - - name: Add Missing Lib Files (for iOS 9.0 and macOS 10.9) - run: | - cd /Applications/Xcode_15.0.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib - sudo mkdir arc - cd arc - sudo git clone https://github.com/kamyarelyasi/Libarclite-Files.git . - - name: Install Cocoapods - run: | - sudo gem install cocoapods - - name: Library Lint - run: | - pod lib lint --allow-warnings --verbose - diff --git a/.github/workflows/pod-trunk-push.yml b/.github/workflows/pod-trunk-push.yml deleted file mode 100644 index 9350f27..0000000 --- a/.github/workflows/pod-trunk-push.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Cocoapods Trunk Push - -on: - workflow_run: - workflows: - - 'Build' - types: - - completed - branches: - - 'main' - -jobs: - publish-podspec: - name: Push Podspec File To Trunk - runs-on: macos-13 - if: ${{ github.event.workflow_run.conclusion == 'success' }} - - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set Xcode Version - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: '15.0.0' - - name: Add Missing Lib Files (for iOS 9.0 and macOS 10.9) - run: | - cd /Applications/Xcode_15.0.0.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib - sudo mkdir arc - cd arc - sudo git clone https://github.com/kamyarelyasi/Libarclite-Files.git . - - name: Install Cocoapods - run: | - sudo gem install cocoapods - - name: Trunk Push - env: - COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} - run: | - pod trunk push --allow-warnings --verbose diff --git a/RxNetworkKitX.podspec b/RxNetworkKitX.podspec deleted file mode 100644 index 86868bd..0000000 --- a/RxNetworkKitX.podspec +++ /dev/null @@ -1,20 +0,0 @@ -Pod::Spec.new do |s| - s.platform = :ios, :macos - s.ios.deployment_target = '14.0' - s.macos.deployment_target = '11.0' - s.name = 'RxNetworkKitX' - s.module_name = 'RxNetworkKit' - s.version = '0.1.0' - s.summary = 'a lightweight FRP networking framework.' - s.description = 'a FRP networking framework built on top of URLSession and uses RxSwift and RxCocoa.' - s.homepage = 'https://github.com/loay-ashraf/RxNetworkKit' - s.license = { :type => 'MIT', :file => 'LICENSE' } - s.author = { 'loay-ashraf' => 'loay.ashraf.96@gmail.com' } - s.source = { :git => 'https://github.com/loay-ashraf/RxNetworkKit.git', :tag => s.version.to_s } - s.framework = "Foundation" - s.dependency 'RxSwift', '~> 6.6' - s.dependency 'RxCocoa', '~> 6.6' - s.dependency 'RxSwiftExt', '~> 6.2' - s.source_files = 'Source/**/*.{swift,m,h}' - s.swift_version = '5.0' - end From 52f72be44fffbb7cb4578ed534f4642c23354db3 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 18:37:56 +0300 Subject: [PATCH 38/95] [49][DocC][Update Documentation] (#50) * feat: update overview for classes, structs, enums, typaliases and protocols Resolves: none. * feat: add docs catalog Resolves: none. --- Docs.docc/Pages/NetworkManager.md | 37 +++++++++++++ Docs.docc/Pages/RxNetworkKit.md | 54 +++++++++++++++++++ RxNetworkKit.xcodeproj/project.pbxproj | 4 ++ .../Download/DownloadEvent.swift | 1 + .../Custom Requests/Upload/UploadEvent.swift | 1 + .../Custom Requests/Upload/UploadFile.swift | 10 ++++ .../Upload/UploadFormData.swift | 3 ++ Source/Error/DefaultNetworkAPIError.swift | 4 ++ Source/Error/NetworkAPIError.swift | 2 +- Source/Error/NetworkClientError.swift | 2 +- Source/Error/NetworkError.swift | 3 ++ Source/Error/NetworkServerError.swift | 2 +- .../Event Monitor/NetworkEventMonitor.swift | 5 +- Source/HTTP/DefaultHTTPErrorBody.swift | 6 +++ Source/HTTP/HTTPErrorBody.swift | 1 + Source/HTTP/HTTPMethod.swift | 1 + Source/HTTP/HTTPScheme.swift | 1 + Source/Manager/NetworkManager.swift | 17 +++++- .../Reachability/NetworkInterfaceType.swift | 1 + Source/Reachability/NetworkReachability.swift | 13 ++++- .../NetworkReachabilityStatus.swift | 1 + .../Request Interceptor/RequestAdapter.swift | 10 ++++ .../RequestInterceptor.swift | 1 + .../Request Interceptor/RequestRetrier.swift | 27 ++++++++++ .../RequestRetryPolicy.swift | 3 +- Source/Router/NetworkDownloadRouter.swift | 3 ++ Source/Router/NetworkRouter.swift | 17 +++++- Source/Router/NetworkUploadRouter.swift | 1 + Source/Web Socket/WebSocket.swift | 13 ++++- Source/Web Socket/WebSocketCloseCode.swift | 1 + Source/Web Socket/WebSocketCloseHandler.swift | 6 +++ Source/Web Socket/WebSocketMessage.swift | 1 + 32 files changed, 241 insertions(+), 11 deletions(-) create mode 100644 Docs.docc/Pages/NetworkManager.md create mode 100644 Docs.docc/Pages/RxNetworkKit.md diff --git a/Docs.docc/Pages/NetworkManager.md b/Docs.docc/Pages/NetworkManager.md new file mode 100644 index 0000000..21e055b --- /dev/null +++ b/Docs.docc/Pages/NetworkManager.md @@ -0,0 +1,37 @@ +# ``NetworkManager`` + +## Topics + +### Creating a Network Manager + +- ``NetworkManager/init(configuration:requestInterceptor:eventMonitor:)`` + +### Making a Request + +- ``NetworkRouter`` +- ``NetworkManager/request(_:_:_:)-2m73c`` +- ``NetworkManager/request(_:_:_:)-2icwe`` + +### Downloading a File + +- ``NetworkDownloadRouter`` +- ``DownloadEvent`` +- ``NetworkManager/download(_:_:_:)`` +- ``NetworkManager/download(_:_:_:_:)`` + +### Uploading a File + +- ``NetworkUploadRouter`` +- ``UploadEvent`` +- ``UploadFile`` +- ``UploadFormData`` +- ``NetworkManager/upload(_:_:_:_:)-5aqfe`` +- ``NetworkManager/upload(_:_:_:_:)-4xcrt`` + +### Connecting to a WebSocket + +- ``WebSocket`` +- ``WebSocketMessage`` +- ``WebSocketCloseCode`` +- ``WebSocketCloseHandler`` +- ``NetworkManager/webSocket(_:_:_:)`` diff --git a/Docs.docc/Pages/RxNetworkKit.md b/Docs.docc/Pages/RxNetworkKit.md new file mode 100644 index 0000000..0cb0921 --- /dev/null +++ b/Docs.docc/Pages/RxNetworkKit.md @@ -0,0 +1,54 @@ +# ``RxNetworkKit`` + +a lightweight networking framework based on URLSession and RxSwift. + +## Overview + +RxNetworkKit fits nicely in your project if you use RxSwift and RxCocoa mainly in your project. + +It makes use of RxSwift's traits at request level to acheive a high level of specialization for observed request sequence and expected output from it. + +### Full of Goodies: +- includes download and upload capabillity with progress tracking all within the same observable sequence. +- includes websocket capabillity for observing remote data updates. +- includes a request interceptor protocol that can be implemented for request adaptation and retry on failure. +- comes with a reachability class that you can observe from anywhere for reachability status. + +## Topics + +### Foundation + +- ``NetworkManager`` + +### HTTP + +- ``HTTPScheme`` +- ``HTTPMethod`` +- ``HTTPStatusCode`` +- ``HTTPErrorBody`` +- ``DefaultHTTPErrorBody`` + +### Error + +- ``NetworkError`` +- ``NetworkAPIError`` +- ``DefaultNetworkAPIError`` +- ``NetworkClientError`` +- ``NetworkServerError`` + +### Request Interceptor + +- ``NetworkRequestInterceptor`` +- ``NetworkRequestAdapter`` +- ``NetworkRequestRetrier`` +- ``NetworkRequestRetryPolicy`` + +### Event Monitor + +- ``NetworkEventMonitor`` + +### Network Reachability + +- ``NetworkReachability`` +- ``NetworkReachabilityStatus`` +- ``NetworkInterfaceType`` diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index 8488115..c6a5a47 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -57,6 +57,7 @@ 0B77E0BD29D968DE0077FBC0 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BC29D968DE0077FBC0 /* RxSwift */; }; 0B77E0C029D969370077FBC0 /* RxSwiftExt in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BF29D969370077FBC0 /* RxSwiftExt */; }; C6049B162A95307800E5727E /* RxNetworkKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C6049B152A95307800E5727E /* RxNetworkKit.h */; }; + C69A78562ACF001400ECF092 /* Docs.docc in Sources */ = {isa = PBXBuildFile; fileRef = C69A78552ACEFF3200ECF092 /* Docs.docc */; }; C6A9BEF62A93E2AE00459E32 /* DefaultHTTPErrorBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */; }; C6A9BEF82A93E2D600459E32 /* HTTPErrorBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */; }; C6A9BEFA2A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */; }; @@ -120,6 +121,7 @@ 0B77E08729D965D30077FBC0 /* NetworkRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkRouter.swift; sourceTree = ""; }; 0B77E08829D965D30077FBC0 /* NetworkUploadRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkUploadRouter.swift; sourceTree = ""; }; C6049B152A95307800E5727E /* RxNetworkKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxNetworkKit.h; sourceTree = ""; }; + C69A78552ACEFF3200ECF092 /* Docs.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Docs.docc; sourceTree = ""; }; C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultHTTPErrorBody.swift; sourceTree = ""; }; C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPErrorBody.swift; sourceTree = ""; }; C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+setAdditionalHTTPHeader.swift"; sourceTree = ""; }; @@ -152,6 +154,7 @@ 0B77DFCC29D964D40077FBC0 = { isa = PBXGroup; children = ( + C69A78552ACEFF3200ECF092 /* Docs.docc */, 0B6BD87329DC0A9400502F47 /* Package.swift */, 0B77E04A29D965D30077FBC0 /* Source */, 0B77DFD729D964D40077FBC0 /* Products */, @@ -485,6 +488,7 @@ 0B77E09E29D965D30077FBC0 /* HTTPStatusCode.swift in Sources */, 0B77E08D29D965D30077FBC0 /* UploadFormData.swift in Sources */, 0B77E0AF29D965D30077FBC0 /* Single+Decodable.swift in Sources */, + C69A78562ACF001400ECF092 /* Docs.docc in Sources */, 0B77E0A129D965D30077FBC0 /* NWInterfaceType+RawRepresentable.swift in Sources */, 0B77E0A529D965D30077FBC0 /* NetworkEventMonitor.swift in Sources */, 0B77E0B129D965D30077FBC0 /* Observable+Decodable.swift in Sources */, diff --git a/Source/Custom Requests/Download/DownloadEvent.swift b/Source/Custom Requests/Download/DownloadEvent.swift index ff2afc6..d02a00b 100644 --- a/Source/Custom Requests/Download/DownloadEvent.swift +++ b/Source/Custom Requests/Download/DownloadEvent.swift @@ -7,6 +7,7 @@ import Foundation +/// An enumeration of the types of events received during a download operation. public enum DownloadEvent { case completed case completedWithData(data: Data?) diff --git a/Source/Custom Requests/Upload/UploadEvent.swift b/Source/Custom Requests/Upload/UploadEvent.swift index aabc6fb..4f2e711 100644 --- a/Source/Custom Requests/Upload/UploadEvent.swift +++ b/Source/Custom Requests/Upload/UploadEvent.swift @@ -7,6 +7,7 @@ import Foundation +/// An enumeration of the types of events received during an upload operation. public enum UploadEvent { case completed(model: T) case progress(progress: Progress) diff --git a/Source/Custom Requests/Upload/UploadFile.swift b/Source/Custom Requests/Upload/UploadFile.swift index bd0aab0..c7b4e03 100644 --- a/Source/Custom Requests/Upload/UploadFile.swift +++ b/Source/Custom Requests/Upload/UploadFile.swift @@ -7,12 +7,20 @@ import Foundation +/// Holds file details for multipart form upload, public struct UploadFile { + + /// key used for file record. let key: String + /// name of file in the file record. let name: String + /// local url of the file. let url: URL? + /// data of the file. let data: Data? + /// MIME type of the file. let mimeType: HTTPMIMEType + /// Creates `File` instance, use this initializer for relativley small files. /// /// - Parameters: @@ -27,6 +35,7 @@ public struct UploadFile { guard let mime = HTTPMIMEType(fileName: name) else { return nil } self.mimeType = mime } + /// Creates `File` instance, use this initializer for relativley large files. /// /// - Parameters: @@ -41,4 +50,5 @@ public struct UploadFile { guard let mime = HTTPMIMEType(fileName: name) else { return nil } self.mimeType = mime } + } diff --git a/Source/Custom Requests/Upload/UploadFormData.swift b/Source/Custom Requests/Upload/UploadFormData.swift index ce1d5eb..866a569 100644 --- a/Source/Custom Requests/Upload/UploadFormData.swift +++ b/Source/Custom Requests/Upload/UploadFormData.swift @@ -7,9 +7,12 @@ import Foundation +/// Holds details for a mulipart form upload. public struct UploadFormData { + /// parameters (text data fields) to be included in the form HTTP body. let parameters: [String: String] /// files to be included in the form HTTP body. let files: [UploadFile] + } diff --git a/Source/Error/DefaultNetworkAPIError.swift b/Source/Error/DefaultNetworkAPIError.swift index e9af7dd..de4431c 100644 --- a/Source/Error/DefaultNetworkAPIError.swift +++ b/Source/Error/DefaultNetworkAPIError.swift @@ -5,6 +5,10 @@ // Created by Loay Ashraf on 19/02/2023. // +/// Default type used for decoding internal api error bodies. public struct DefaultNetworkAPIError: NetworkAPIError { + + /// error message. let message: String + } diff --git a/Source/Error/NetworkAPIError.swift b/Source/Error/NetworkAPIError.swift index 1264403..77d6575 100644 --- a/Source/Error/NetworkAPIError.swift +++ b/Source/Error/NetworkAPIError.swift @@ -5,5 +5,5 @@ // Created by Loay Ashraf on 20/03/2023. // -/// API-side internal error +/// Internal api error body type. public protocol NetworkAPIError: Error, Decodable { } diff --git a/Source/Error/NetworkClientError.swift b/Source/Error/NetworkClientError.swift index b0e8ee4..2724b5f 100644 --- a/Source/Error/NetworkClientError.swift +++ b/Source/Error/NetworkClientError.swift @@ -5,7 +5,7 @@ // Created by Loay Ashraf on 19/02/2023. // -/// Client-side (transport) error +/// Client-side (transport) network error. public enum NetworkClientError: Error { case http(HTTPStatusCode, HTTPErrorBody?) case serialization(Error) diff --git a/Source/Error/NetworkError.swift b/Source/Error/NetworkError.swift index bcb136f..493151d 100644 --- a/Source/Error/NetworkError.swift +++ b/Source/Error/NetworkError.swift @@ -7,10 +7,12 @@ import Foundation +/// Generic network error (client, server or api). public enum NetworkError: Error { case client(NetworkClientError) case server(NetworkServerError) case api(NetworkAPIError) + /// Creates `NetworkError` instance. /// /// - Parameter response: `HTTPURLResponse` used to get response status code. @@ -35,4 +37,5 @@ public enum NetworkError: Error { return nil } } + } diff --git a/Source/Error/NetworkServerError.swift b/Source/Error/NetworkServerError.swift index 6c059c7..8a9713e 100644 --- a/Source/Error/NetworkServerError.swift +++ b/Source/Error/NetworkServerError.swift @@ -5,7 +5,7 @@ // Created by Loay Ashraf on 19/02/2023. // -/// Server-side error +/// Server-side network error. public enum NetworkServerError: Error { case http(HTTPStatusCode, HTTPErrorBody?) case generic(Error) diff --git a/Source/Event Monitor/NetworkEventMonitor.swift b/Source/Event Monitor/NetworkEventMonitor.swift index f484f9b..8fe8991 100644 --- a/Source/Event Monitor/NetworkEventMonitor.swift +++ b/Source/Event Monitor/NetworkEventMonitor.swift @@ -7,6 +7,7 @@ import Foundation -/// To monitor network events in a given session, all you have to do -/// is implement its delegate methods, easy and simple isn't it? 🤔. +// To monitor network events in a given session, all you have to do +// is implement its delegate methods, easy and simple isn't it? 🤔. +/// Session and Tasks delegate. public typealias NetworkEventMonitor = URLSessionDelegate & URLSessionTaskDelegate & URLSessionDataDelegate & URLSessionStreamDelegate & URLSessionDownloadDelegate & URLSessionWebSocketDelegate diff --git a/Source/HTTP/DefaultHTTPErrorBody.swift b/Source/HTTP/DefaultHTTPErrorBody.swift index 89238f8..7b2055f 100644 --- a/Source/HTTP/DefaultHTTPErrorBody.swift +++ b/Source/HTTP/DefaultHTTPErrorBody.swift @@ -7,8 +7,14 @@ import Foundation +/// Default type used for decoding http error bodies. public struct DefaultHTTPErrorBody: HTTPErrorBody { + + /// response status code. let statusCode: Int? + /// error message. let message: String? + /// support identifier. let supportId: String? + } diff --git a/Source/HTTP/HTTPErrorBody.swift b/Source/HTTP/HTTPErrorBody.swift index 0366e4b..7b3ce1f 100644 --- a/Source/HTTP/HTTPErrorBody.swift +++ b/Source/HTTP/HTTPErrorBody.swift @@ -7,4 +7,5 @@ import Foundation +/// HTTP error body type. public protocol HTTPErrorBody: Decodable { } diff --git a/Source/HTTP/HTTPMethod.swift b/Source/HTTP/HTTPMethod.swift index c272ae9..cb23363 100644 --- a/Source/HTTP/HTTPMethod.swift +++ b/Source/HTTP/HTTPMethod.swift @@ -5,6 +5,7 @@ // Created by Loay Ashraf on 20/03/2023. // +/// An enumeration of the types of http methods. public enum HTTPMethod: String { case get = "GET" case put = "PUT" diff --git a/Source/HTTP/HTTPScheme.swift b/Source/HTTP/HTTPScheme.swift index 34da387..d807277 100644 --- a/Source/HTTP/HTTPScheme.swift +++ b/Source/HTTP/HTTPScheme.swift @@ -5,6 +5,7 @@ // Created by Loay Ashraf on 20/03/2023. // +/// An enumeration of the types of http schemes. public enum HTTPScheme: String { case http = "http://" case https = "https://" diff --git a/Source/Manager/NetworkManager.swift b/Source/Manager/NetworkManager.swift index 86cca18..9f294b8 100644 --- a/Source/Manager/NetworkManager.swift +++ b/Source/Manager/NetworkManager.swift @@ -8,13 +8,18 @@ import Foundation import RxSwift import RxSwiftExt -import RxCocoa +/// Entry point for creating and managing network requests. public class NetworkManager { + + /// Principal `URLSession` used to create request tasks. private let session: URLSession + /// Principal `NetworkRequestInterceptor` used to intercept requests. private let requestInterceptor: NetworkRequestInterceptor + /// Principal `NetworkEventMonitor` used to monitor request tasks. private let eventMonitor: NetworkEventMonitor - /// Creates `NetworkManager` instance. + + /// Creates a `NetworkManager` instance. /// /// - Parameters: /// - configuration: `URLSessionConfiguration` object used to create `URLSession` instance. @@ -29,6 +34,7 @@ public class NetworkManager { self.requestInterceptor = requestInterceptor self.eventMonitor = eventMonitor } + /// Creates a `Completable` observable encapsulating data request using given `Router`. /// Use this method if you are expecting empty response body. /// @@ -54,6 +60,7 @@ public class NetworkManager { .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } + /// Creates a `Single` observable encapsulating data request using given `Router`. /// Use this method if you are expecting data in response body. /// @@ -78,6 +85,7 @@ public class NetworkManager { .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } + /// Creates a `Observable` object encapsulating download request using given `Router`. /// Use this method if you are downloading a relatively small file (keeps data in memory). /// @@ -101,6 +109,7 @@ public class NetworkManager { .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } + /// Creates a `Observable` object encapsulating download request using given `Router`. /// Use this method if you are downloading a relatively small file (saves file to disk). /// @@ -125,6 +134,7 @@ public class NetworkManager { .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } + /// Creates a `Observable` object encapsulating upload request using given `Router`. /// Use this method if you are uploading single file at a time. /// @@ -149,6 +159,7 @@ public class NetworkManager { .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } + /// Creates a `Observable` object encapsulating upload request using given `Router`. /// Use this method if you are uploading multiple files at a time. /// @@ -173,6 +184,7 @@ public class NetworkManager { .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable } + /// Creates websocket object and establishes connection using provided url and protocols. /// /// - Parameters: @@ -186,4 +198,5 @@ public class NetworkManager { let webSocket = WebSocket(task: task, closeHandler: closeHandler) return webSocket } + } diff --git a/Source/Reachability/NetworkInterfaceType.swift b/Source/Reachability/NetworkInterfaceType.swift index b0ed77e..2032238 100644 --- a/Source/Reachability/NetworkInterfaceType.swift +++ b/Source/Reachability/NetworkInterfaceType.swift @@ -8,4 +8,5 @@ import Foundation import Network +/// Types of network interfaces, based on their link layer media types. public typealias NetworkInterfaceType = NWInterface.InterfaceType diff --git a/Source/Reachability/NetworkReachability.swift b/Source/Reachability/NetworkReachability.swift index 2465b18..db8aee3 100644 --- a/Source/Reachability/NetworkReachability.swift +++ b/Source/Reachability/NetworkReachability.swift @@ -10,29 +10,38 @@ import Network import RxSwift import RxCocoa +/// Monitors network reachbility status. public class NetworkReachability { + + /// Shared `NetworkReachability` instance. public static let shared: NetworkReachability = .init() + /// a `BehaviorRelay` object for current reachability status. public private(set) var status: BehaviorRelay = .init(value: .unReachable) + /// a `PublishRelay` object that notifies subscribers when network is reachable. public private(set) var didBecomeReachable: PublishRelay = .init() private var _status: NetworkReachabilityStatus = .unReachable private var monitor: NWPathMonitor private let monitorQueue: DispatchQueue - /// Creates `NetworkReachability` instance. + + /// Creates a `NetworkReachability` instance. private init() { self.monitor = .init() let bundleID = Bundle.main.bundleIdentifier! let monitorQueueLabel = bundleID + ".reachability" self.monitorQueue = .init(label: monitorQueueLabel, qos: .utility) } + /// Starts network monitor on monitor dispatch queue. public func start() { monitor.pathUpdateHandler = handlePathUpdate(_:) monitor.start(queue: monitorQueue) } + /// Stops network monitor. public func stop() { monitor.cancel() } + /// Sets interface types to monitor by creating new `NWPathMonitor` instance, /// Call `start` method after calling this method. /// @@ -45,6 +54,7 @@ public class NetworkReachability { let newMonitor = NWPathMonitor(prohibitedInterfaceTypes: prohibtedTypes) monitor = newMonitor } + /// Handles netwok path updates and sends events to relays. /// /// - Parameter path: updated `NWPath` object. @@ -61,4 +71,5 @@ public class NetworkReachability { didBecomeReachable.accept(()) } } + } diff --git a/Source/Reachability/NetworkReachabilityStatus.swift b/Source/Reachability/NetworkReachabilityStatus.swift index bb609c0..1dc98a1 100644 --- a/Source/Reachability/NetworkReachabilityStatus.swift +++ b/Source/Reachability/NetworkReachabilityStatus.swift @@ -8,6 +8,7 @@ import Foundation import Network +/// Status for network reachability. public enum NetworkReachabilityStatus: Equatable { case reachable(interfaceType: NetworkInterfaceType) case unReachable diff --git a/Source/Request Interceptor/RequestAdapter.swift b/Source/Request Interceptor/RequestAdapter.swift index 9174342..534d32b 100644 --- a/Source/Request Interceptor/RequestAdapter.swift +++ b/Source/Request Interceptor/RequestAdapter.swift @@ -7,6 +7,16 @@ import Foundation +/// Adapts outgoing requests. public protocol NetworkRequestAdapter { + + /// Adapts a given request for a given session. + /// + /// - Parameters: + /// - request: current `URLRequest` + /// - session: current `URLSession` + /// + /// - Returns: `URLRequest` adapted request for a given session. func adapt(_ request: URLRequest, for session: URLSession) -> URLRequest + } diff --git a/Source/Request Interceptor/RequestInterceptor.swift b/Source/Request Interceptor/RequestInterceptor.swift index f4fe8fd..1d3a83c 100644 --- a/Source/Request Interceptor/RequestInterceptor.swift +++ b/Source/Request Interceptor/RequestInterceptor.swift @@ -5,4 +5,5 @@ // Created by Loay Ashraf on 27/03/2023. // +/// Request adapter and retrier. public typealias NetworkRequestInterceptor = NetworkRequestAdapter & NetworkRequestRetrier diff --git a/Source/Request Interceptor/RequestRetrier.swift b/Source/Request Interceptor/RequestRetrier.swift index 6c6a929..5dc3e48 100644 --- a/Source/Request Interceptor/RequestRetrier.swift +++ b/Source/Request Interceptor/RequestRetrier.swift @@ -7,8 +7,35 @@ import Foundation +/// Retries requests upon failure. public protocol NetworkRequestRetrier { + + /// Provides maximum retry attempts for a given request and session. + /// + /// - Parameters: + /// - request: current `URLRequest` + /// - session: current `URLSession` + /// + /// - Returns: `Int` maximum retry attempts for given request and session. func retryMaxAttempts(_ request: URLRequest, for session: URLSession) -> Int + + /// Provides retry policy for a given request and session. + /// + /// - Parameters: + /// - request: current `URLRequest` + /// - session: current `URLSession` + /// + /// - Returns: `NetworkRequestRetryPolicy` retry policy for given request and session. func retryPolicy(_ request: URLRequest, for session: URLSession) -> NetworkRequestRetryPolicy + + /// Decides if a given request should be reried for a given session and error. + /// + /// - Parameters: + /// - request: current `URLRequest` + /// - session: current `URLSession` + /// - error: encountered `NetworkError` + /// + /// - Returns: `NetworkRequestRetryPolicy` retry policy for given request and session. func shouldRetry(_ request: URLRequest, for session: URLSession, dueTo error: NetworkError) -> Bool + } diff --git a/Source/Request Interceptor/RequestRetryPolicy.swift b/Source/Request Interceptor/RequestRetryPolicy.swift index 4a163ea..61256b2 100644 --- a/Source/Request Interceptor/RequestRetryPolicy.swift +++ b/Source/Request Interceptor/RequestRetryPolicy.swift @@ -7,9 +7,10 @@ import Foundation -// This struct is inspired by Alex Grebenyuk excellent blog https://kean.blog/post/smart-retry +// This enum is inspired by Alex Grebenyuk excellent blog https://kean.blog/post/smart-retry // Here's Alex's twitter: https://twitter.com/a_grebenyuk +/// Policy for retrying failed requests. public enum NetworkRequestRetryPolicy { case immediate case constant(time: Double) diff --git a/Source/Router/NetworkDownloadRouter.swift b/Source/Router/NetworkDownloadRouter.swift index 66652f9..d8353bb 100644 --- a/Source/Router/NetworkDownloadRouter.swift +++ b/Source/Router/NetworkDownloadRouter.swift @@ -7,9 +7,11 @@ import Foundation +/// Holds download request details. public protocol NetworkDownloadRouter: NetworkRouter { } public extension NetworkDownloadRouter { + /// By Default: HTTP method is GET for download requests. var method: HTTPMethod { .get @@ -18,4 +20,5 @@ public extension NetworkDownloadRouter { var body: [String : Any]? { nil } + } diff --git a/Source/Router/NetworkRouter.swift b/Source/Router/NetworkRouter.swift index c10b019..455d90b 100644 --- a/Source/Router/NetworkRouter.swift +++ b/Source/Router/NetworkRouter.swift @@ -7,21 +7,35 @@ import Foundation +/// Holds request details. public protocol NetworkRouter { + + /// http scheme of the request. var scheme: HTTPScheme { get } + /// http method of the request. var method: HTTPMethod { get } + /// url domain of the request, eg.: `github.com`. var domain: String { get } + /// url path of the request, eg.: `api/users`. var path: String { get } + /// http headers of the request. var headers: [String: String] { get } + /// url query parameters of the request. var parameters: [String: String]? { get } + /// http body of the request. var body: [String: Any]? { get } + /// full url of the request (including: domain, path and query parameters). var url: URL? { get } - /// Creates `URLRequest` object using router properties. + + /// Creates a `URLRequest` object using router properties. /// /// - Returns: `URLRequest` created using router properties. func asURLRequest() -> URLRequest + } + public extension NetworkRouter { + /// Creates `URLRequest` object using router properties. /// /// - Returns: `URLRequest` created using router properties. @@ -45,4 +59,5 @@ public extension NetworkRouter { request.allHTTPHeaderFields = headers return request } + } diff --git a/Source/Router/NetworkUploadRouter.swift b/Source/Router/NetworkUploadRouter.swift index 0703889..e60d9d0 100644 --- a/Source/Router/NetworkUploadRouter.swift +++ b/Source/Router/NetworkUploadRouter.swift @@ -7,6 +7,7 @@ import Foundation +/// Holds upload request details. public protocol NetworkUploadRouter: NetworkRouter { } public extension NetworkUploadRouter { diff --git a/Source/Web Socket/WebSocket.swift b/Source/Web Socket/WebSocket.swift index 2c86f2a..7edb256 100644 --- a/Source/Web Socket/WebSocket.swift +++ b/Source/Web Socket/WebSocket.swift @@ -9,16 +9,21 @@ import Foundation import RxSwift import RxRelay +/// Encapsulates a connection to websocket server. public class WebSocket { + + /// a `PublishRelay` object for text messages received from websocket server. public let text: PublishRelay = .init() + /// a generic `PublishRelay` object for data messages received from websocket server. public let data: PublishRelay = .init() + /// a `PublishRelay` object for errors encountered while receiving/sending from/to websocket server. public let error: PublishRelay = .init() private let task: URLSessionWebSocketTask private let closeHandler: WebSocketCloseHandler private let receiveObservable: Observable private let disposeBag: DisposeBag = .init() - /// Creates `WebSocket` instance with generic tyoe `T`. + /// Creates a `WebSocket` instance with generic tyoe `T`. /// /// - Parameters: /// - task: `URLSessionWebSocketTask` that represents the connection to websocket server. @@ -29,15 +34,18 @@ public class WebSocket { self.receiveObservable = task.rx.receive(closeHandler: closeHandler) setupBindings() } + /// Resumes current task to establish the connection. public func connect() { task.resume() } + /// Cancels the request associated with current task to close the connection. public func disconnect() { let request = task.currentRequest task.cancel(with: closeHandler.code(for: request), reason: closeHandler.reason(for: request)) } + /// Sends message to the websocket server. /// /// - Parameter message: `WebSocketMessage` to be sent to websocket server. @@ -46,12 +54,14 @@ public class WebSocket { public func send(_ message: WebSocketMessage) -> Completable { task.rx.send(message: message) } + /// Sends a ping to the websocket server. /// /// - Returns: `Completable` observable encapsulating send ping operation. public func ping() -> Completable { task.rx.ping() } + /// Sets up internal observable bindings. private func setupBindings() { receiveObservable @@ -87,4 +97,5 @@ public class WebSocket { .bind(to: error) .disposed(by: disposeBag) } + } diff --git a/Source/Web Socket/WebSocketCloseCode.swift b/Source/Web Socket/WebSocketCloseCode.swift index 88e3b02..2c99ee0 100644 --- a/Source/Web Socket/WebSocketCloseCode.swift +++ b/Source/Web Socket/WebSocketCloseCode.swift @@ -7,4 +7,5 @@ import Foundation +/// A code that indicates why a WebSocket connection closed. public typealias WebSocketCloseCode = URLSessionWebSocketTask.CloseCode diff --git a/Source/Web Socket/WebSocketCloseHandler.swift b/Source/Web Socket/WebSocketCloseHandler.swift index c5e0693..3ce4888 100644 --- a/Source/Web Socket/WebSocketCloseHandler.swift +++ b/Source/Web Socket/WebSocketCloseHandler.swift @@ -7,9 +7,12 @@ import Foundation +/// Provides close code and reason for websocket connection. public class WebSocketCloseHandler { + private let _code: (URLRequest?) -> WebSocketCloseCode private let _reason: (URLRequest?) -> Data? + /// Creates `WebSocketCloseHandler` instance. /// /// - Parameters: @@ -19,6 +22,7 @@ public class WebSocketCloseHandler { self._code = code self._reason = reason } + /// Provides close code for a given request. /// /// - Parameter request: `URLRequest?` object used to provide close code. @@ -27,6 +31,7 @@ public class WebSocketCloseHandler { func code(for request: URLRequest?) -> WebSocketCloseCode { _code(request) } + /// Provides close reason for a given request. /// /// - Parameter request: `URLRequest?` object used to provide close reason. @@ -35,4 +40,5 @@ public class WebSocketCloseHandler { func reason(for request: URLRequest?) -> Data? { _reason(request) } + } diff --git a/Source/Web Socket/WebSocketMessage.swift b/Source/Web Socket/WebSocketMessage.swift index c79c8b3..b4823b1 100644 --- a/Source/Web Socket/WebSocketMessage.swift +++ b/Source/Web Socket/WebSocketMessage.swift @@ -7,4 +7,5 @@ import Foundation +/// An enumeration of the types of messages sent and received. public typealias WebSocketMessage = URLSessionWebSocketTask.Message From d2a10cec6bb76ab0ec8b30d6262bd1f9f1115e4a Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 18:42:12 +0300 Subject: [PATCH 39/95] update CI workflows to build docs Resolves: none. --- .github/workflows/build-ios.yml | 2 ++ .github/workflows/build-macos.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index c411fd3..c3ccbec 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -22,6 +22,7 @@ jobs: destination: generic/platform=iOS Simulator run: | xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} + xcodebuild docbuild framework-ios: name: Build Framework For iOS Device @@ -41,6 +42,7 @@ jobs: destination: generic/platform=iOS run: | xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} + xcodebuild docbuild example-ios-simulator: name: Build Example For iOS Simulator diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 2e086b6..1ec8cf7 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -22,6 +22,7 @@ jobs: destination: generic/platform=macOS run: | xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} + xcodebuild docbuild example-macos: name: Build Example For macOS From f62e74c2074ff0282d59c9658a537a67fdd32905 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 19:16:40 +0300 Subject: [PATCH 40/95] update CI workflows Resolves: none. --- .github/workflows/build-ios.yml | 6 ++---- .github/workflows/build-macos.yml | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index c3ccbec..90bb7dc 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -21,8 +21,7 @@ jobs: scheme: RxNetworkKit destination: generic/platform=iOS Simulator run: | - xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} - xcodebuild docbuild + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" docbuild | xcpretty && exit ${PIPESTATUS[0]} framework-ios: name: Build Framework For iOS Device @@ -41,8 +40,7 @@ jobs: scheme: RxNetworkKit destination: generic/platform=iOS run: | - xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} - xcodebuild docbuild + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" docbuild | xcpretty && exit ${PIPESTATUS[0]} example-ios-simulator: name: Build Example For iOS Simulator diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 1ec8cf7..b9ccbb3 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -21,8 +21,7 @@ jobs: scheme: RxNetworkKit destination: generic/platform=macOS run: | - xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} - xcodebuild docbuild + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" docbuild | xcpretty && exit ${PIPESTATUS[0]} example-macos: name: Build Example For macOS From 5494beb2679a89856ea8dc0ab434f69fe623b0bf Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 19:59:44 +0300 Subject: [PATCH 41/95] Update build-ios.yml --- .github/workflows/build-ios.yml | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index 90bb7dc..b4e2d7e 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -4,6 +4,30 @@ on: workflow_call: jobs: + framework-docs: + name: Build Framework Docs + runs-on: macos-13 + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.0.0' + - name: Build Docs + env: + workspace: RxNetworkKit.xcworkspace + scheme: RxNetworkKit + destination: generic/platform=iOS Simulator + run: | + xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" -derivedDataPath derivedData | xcpretty && exit ${PIPESTATUS[0]} + mkdir docArchives + cp -R `find derivedData -type d -name "*.doccarchive"` docArchives + - name: Deploy Docs + run: | + (xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --output-path docs + framework-ios-simulator: name: Build Framework For iOS Simulator runs-on: macos-13 @@ -21,7 +45,7 @@ jobs: scheme: RxNetworkKit destination: generic/platform=iOS Simulator run: | - xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" docbuild | xcpretty && exit ${PIPESTATUS[0]} + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} framework-ios: name: Build Framework For iOS Device @@ -40,7 +64,7 @@ jobs: scheme: RxNetworkKit destination: generic/platform=iOS run: | - xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" docbuild | xcpretty && exit ${PIPESTATUS[0]} + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} example-ios-simulator: name: Build Example For iOS Simulator From 4d8073e641626038cc7d5c362316055a7cbf4452 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 20:07:28 +0300 Subject: [PATCH 42/95] Update build-ios.yml --- .github/workflows/build-ios.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index b4e2d7e..9846d11 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -26,7 +26,9 @@ jobs: cp -R `find derivedData -type d -name "*.doccarchive"` docArchives - name: Deploy Docs run: | - (xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --output-path docs + (xcrun --find docc) process-archive \ + transform-for-static-hosting docArchives/RxNetworkKit.doccarchive \ + --output-path docs framework-ios-simulator: name: Build Framework For iOS Simulator From e157d9bd28bf4831960d2579c708808b02faa154 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 20:18:02 +0300 Subject: [PATCH 43/95] Update build-ios.yml --- .github/workflows/build-ios.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index 9846d11..627dfa4 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -26,9 +26,7 @@ jobs: cp -R `find derivedData -type d -name "*.doccarchive"` docArchives - name: Deploy Docs run: | - (xcrun --find docc) process-archive \ - transform-for-static-hosting docArchives/RxNetworkKit.doccarchive \ - --output-path docs + (xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs framework-ios-simulator: name: Build Framework For iOS Simulator From 295b46e02f3f93f0ccfb3af467bfc044a2e04144 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 20:30:16 +0300 Subject: [PATCH 44/95] Update build-ios.yml --- .github/workflows/build-ios.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index 627dfa4..d489081 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -26,7 +26,7 @@ jobs: cp -R `find derivedData -type d -name "*.doccarchive"` docArchives - name: Deploy Docs run: | - (xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs + $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit/gh-pages --output-path docs framework-ios-simulator: name: Build Framework For iOS Simulator From d5a70373a286d6cdf136602770ce027fb603e258 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 20:37:13 +0300 Subject: [PATCH 45/95] Update build-ios.yml --- .github/workflows/build-ios.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index d489081..ac46fa0 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -26,7 +26,7 @@ jobs: cp -R `find derivedData -type d -name "*.doccarchive"` docArchives - name: Deploy Docs run: | - $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit/gh-pages --output-path docs + $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs framework-ios-simulator: name: Build Framework For iOS Simulator From d666135898df27e7d8c93bc03f72e123e701c6a5 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 20:42:17 +0300 Subject: [PATCH 46/95] Update build-ios.yml --- .github/workflows/build-ios.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index ac46fa0..af192f8 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -26,6 +26,8 @@ jobs: cp -R `find derivedData -type d -name "*.doccarchive"` docArchives - name: Deploy Docs run: | + cd docArchives + ls $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs framework-ios-simulator: From 46ac717724e417b62b3c617f4ec5d84b3a73649d Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 21:02:39 +0300 Subject: [PATCH 47/95] fix: update corrupt ci workflow file Resolves: none. --- .github/workflows/{build-deploy-docs => build-deploy-docs.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{build-deploy-docs => build-deploy-docs.yml} (100%) diff --git a/.github/workflows/build-deploy-docs b/.github/workflows/build-deploy-docs.yml similarity index 100% rename from .github/workflows/build-deploy-docs rename to .github/workflows/build-deploy-docs.yml From f0b1dbdc5d171461894a49fab8899ccd49f988eb Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 21:07:18 +0300 Subject: [PATCH 48/95] Delete build-deploy-docs1.yml --- .github/workflows/build-deploy-docs1.yml | 46 ------------------------ 1 file changed, 46 deletions(-) delete mode 100644 .github/workflows/build-deploy-docs1.yml diff --git a/.github/workflows/build-deploy-docs1.yml b/.github/workflows/build-deploy-docs1.yml deleted file mode 100644 index cb7a370..0000000 --- a/.github/workflows/build-deploy-docs1.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Build and Deploy Docs - -on: - push: - branches: - - 'develop' - - workflow_dispatch: - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: "pages" - cancel-in-progress: true - -jobs: - build-deploy-docs: - name: Build and Deploy Docs - runs-on: macos-13 - - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set Xcode Version - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: '15.0.0' - - name: Build Docs - env: - workspace: RxNetworkKit.xcworkspace - scheme: RxNetworkKit - destination: generic/platform=iOS Simulator - run: | - xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" -derivedDataPath derivedData | xcpretty && exit ${PIPESTATUS[0]} - $(xcrun --find docc) process-archive transform-for-static-hosting derivedData/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs - echo "" > docs/index.html - - name: Upload artifact - uses: actions/upload-pages-artifact@v1 - with: - path: 'docs' - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v1 From 90539ebac720dbac3fdd9959e79f4e28c70273ee Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 21:09:49 +0300 Subject: [PATCH 49/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 84e0c68..cb7a370 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -22,7 +22,7 @@ jobs: runs-on: macos-13 steps: - - name: Checkout + - name: Checkout uses: actions/checkout@v3 - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 From 27aa3abb95e5f810fbd043a743e6e559333fb0de Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 21:20:55 +0300 Subject: [PATCH 50/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index cb7a370..e1b5535 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -34,9 +34,9 @@ jobs: scheme: RxNetworkKit destination: generic/platform=iOS Simulator run: | - xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" -derivedDataPath derivedData | xcpretty && exit ${PIPESTATUS[0]} - $(xcrun --find docc) process-archive transform-for-static-hosting derivedData/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs - echo "" > docs/index.html + xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" -derivedDataPath derivedData | xcpretty && exit ${PIPESTATUS[0]}; + $(xcrun --find docc) process-archive transform-for-static-hosting derivedData/Build/Products/Debug-iphoneos/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs; + echo "" > docs/index.html; - name: Upload artifact uses: actions/upload-pages-artifact@v1 with: From 057683e5c16feb4aec355a32092cbea61d718974 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 21:27:00 +0300 Subject: [PATCH 51/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index e1b5535..41cc97d 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -34,13 +34,14 @@ jobs: scheme: RxNetworkKit destination: generic/platform=iOS Simulator run: | - xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" -derivedDataPath derivedData | xcpretty && exit ${PIPESTATUS[0]}; + xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" -derivedDataPath derivedData | xcpretty; $(xcrun --find docc) process-archive transform-for-static-hosting derivedData/Build/Products/Debug-iphoneos/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs; echo "" > docs/index.html; - name: Upload artifact uses: actions/upload-pages-artifact@v1 with: - path: 'docs' + run: | + ls - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v1 From 609ab045b094c88101ee5c36ed52464c994bfe7a Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 21:35:01 +0300 Subject: [PATCH 52/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 41cc97d..2a61d1a 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -35,6 +35,8 @@ jobs: destination: generic/platform=iOS Simulator run: | xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" -derivedDataPath derivedData | xcpretty; + cd derivedData + ls $(xcrun --find docc) process-archive transform-for-static-hosting derivedData/Build/Products/Debug-iphoneos/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs; echo "" > docs/index.html; - name: Upload artifact From 55ae407e915bb78714512d6b81523778bc065818 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 21:43:10 +0300 Subject: [PATCH 53/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 2a61d1a..a229974 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -35,9 +35,10 @@ jobs: destination: generic/platform=iOS Simulator run: | xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" -derivedDataPath derivedData | xcpretty; - cd derivedData - ls - $(xcrun --find docc) process-archive transform-for-static-hosting derivedData/Build/Products/Debug-iphoneos/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs; + echo "Copying DocC archives to docArchives..."; + mkdir docArchives; + cp -R `find derivedData -type d -name "*.doccarchive"` docArchives; + $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs; echo "" > docs/index.html; - name: Upload artifact uses: actions/upload-pages-artifact@v1 From ccaa5e5eb49f8878b6ea36f00e732189c2827f7d Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 21:45:57 +0300 Subject: [PATCH 54/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index a229974..8bcabcf 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -43,8 +43,7 @@ jobs: - name: Upload artifact uses: actions/upload-pages-artifact@v1 with: - run: | - ls + path: 'docs' - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v1 From f7a059f0190e5648e290739f32f3ca3c757c70d6 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 21:57:59 +0300 Subject: [PATCH 55/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 8bcabcf..3314cbf 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -32,14 +32,14 @@ jobs: env: workspace: RxNetworkKit.xcworkspace scheme: RxNetworkKit - destination: generic/platform=iOS Simulator run: | - xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" -derivedDataPath derivedData | xcpretty; - echo "Copying DocC archives to docArchives..."; - mkdir docArchives; - cp -R `find derivedData -type d -name "*.doccarchive"` docArchives; - $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs; - echo "" > docs/index.html; + xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -derivedDataPath derivedData | xcpretty + - name: Perpare Docs for deployment + run: | + mkdir docArchives + cp -R `find derivedData -type d -name "*.doccarchive"` docArchives + $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs + echo "" > docs/index.html - name: Upload artifact uses: actions/upload-pages-artifact@v1 with: From 80409807903a63d0972eaa9b42574e3e15e52393 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 22:05:35 +0300 Subject: [PATCH 56/95] update CI workflows --- .github/workflows/build-deploy-docs.yml | 11 ++++------- .github/workflows/build.yml | 8 ++++++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 3314cbf..2081f46 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -1,10 +1,7 @@ name: Build and Deploy Docs on: - push: - branches: - - 'develop' - + workflow_call: workflow_dispatch: permissions: @@ -12,9 +9,9 @@ permissions: pages: write id-token: write -concurrency: - group: "pages" - cancel-in-progress: true +#concurrency: +# group: "pages" +# cancel-in-progress: true jobs: build-deploy-docs: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 893856b..268d7c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,10 @@ on: pull_request: branches: - '**' + +concurrency: + group: "build" + cancel-in-progress: true jobs: build-ios: @@ -16,3 +20,7 @@ jobs: build-macos: name: Build For macOS uses: ./.github/workflows/build-macos.yml + + build-deploy-docs: + name: Build and Deploy Docs + uses: ./.github/workflows/build-deploy-docs.yml From f3b8ee75d9092145fe4362550bd48b5e153ee5e0 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 22:08:24 +0300 Subject: [PATCH 57/95] Update build.yml --- .github/workflows/build.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 268d7c6..9d65597 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,12 @@ on: pull_request: branches: - '**' - + +permissions: + contents: read + pages: write + id-token: write + concurrency: group: "build" cancel-in-progress: true From 3d4c9b444059b34d28059ab43cdbc82897ccf7f8 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 22:09:07 +0300 Subject: [PATCH 58/95] Update build-ios.yml --- .github/workflows/build-ios.yml | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index af192f8..635c4ad 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -4,31 +4,6 @@ on: workflow_call: jobs: - framework-docs: - name: Build Framework Docs - runs-on: macos-13 - - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set Xcode Version - uses: maxim-lobanov/setup-xcode@v1 - with: - xcode-version: '15.0.0' - - name: Build Docs - env: - workspace: RxNetworkKit.xcworkspace - scheme: RxNetworkKit - destination: generic/platform=iOS Simulator - run: | - xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" -derivedDataPath derivedData | xcpretty && exit ${PIPESTATUS[0]} - mkdir docArchives - cp -R `find derivedData -type d -name "*.doccarchive"` docArchives - - name: Deploy Docs - run: | - cd docArchives - ls - $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs framework-ios-simulator: name: Build Framework For iOS Simulator From 9619cfe917e16884684fcbdae27e0076ff1b9331 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 22:13:29 +0300 Subject: [PATCH 59/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 2081f46..64be3b1 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -9,10 +9,6 @@ permissions: pages: write id-token: write -#concurrency: -# group: "pages" -# cancel-in-progress: true - jobs: build-deploy-docs: name: Build and Deploy Docs @@ -31,16 +27,15 @@ jobs: scheme: RxNetworkKit run: | xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -derivedDataPath derivedData | xcpretty - - name: Perpare Docs for deployment + - name: Perpare Docs for Deployment run: | mkdir docArchives cp -R `find derivedData -type d -name "*.doccarchive"` docArchives $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs - echo "" > docs/index.html - - name: Upload artifact + - name: Upload Docs artifact uses: actions/upload-pages-artifact@v1 with: path: 'docs' - - name: Deploy to GitHub Pages + - name: Deploy Docs artifact id: deployment uses: actions/deploy-pages@v1 From 84a438916cbebeacd5b9cd2775f1b3fe66451108 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 22:45:12 +0300 Subject: [PATCH 60/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 64be3b1..d40839f 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -12,6 +12,9 @@ permissions: jobs: build-deploy-docs: name: Build and Deploy Docs + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} runs-on: macos-13 steps: From e34c8ce5fcdccc4ce8d8914eb912d796a066d1c4 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 22:57:20 +0300 Subject: [PATCH 61/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index d40839f..2173d2f 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -12,9 +12,9 @@ permissions: jobs: build-deploy-docs: name: Build and Deploy Docs - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} +# environment: +# name: github-pages +# url: ${{ steps.deployment.outputs.page_url }} runs-on: macos-13 steps: @@ -35,10 +35,25 @@ jobs: mkdir docArchives cp -R `find derivedData -type d -name "*.doccarchive"` docArchives $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs + - name: Setup Pages + id: pages + uses: actions/configure-pages@v3 - name: Upload Docs artifact uses: actions/upload-pages-artifact@v1 with: path: 'docs' - - name: Deploy Docs artifact +# - name: Deploy Docs artifact +# id: deployment +# uses: actions/deploy-pages@v1 + + build-deploy-docs: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 + uses: actions/deploy-pages@v2 + From 5cdbb67aba51bcd0ba3321853cba4c343a246ae8 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 22:58:16 +0300 Subject: [PATCH 62/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 2173d2f..363ed7c 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -46,8 +46,8 @@ jobs: # id: deployment # uses: actions/deploy-pages@v1 - build-deploy-docs: - needs: build + deploy: + needs: build-deploy-docs runs-on: ubuntu-latest environment: name: github-pages From bfb235fb4d4c3781e8181aece8e36838bd8930f1 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 23:08:22 +0300 Subject: [PATCH 63/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 363ed7c..aceda8d 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -34,7 +34,7 @@ jobs: run: | mkdir docArchives cp -R `find derivedData -type d -name "*.doccarchive"` docArchives - $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs + $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path loay-ashraf/RxNetworkKit --output-path docs - name: Setup Pages id: pages uses: actions/configure-pages@v3 From 34320d850d81be2798ab87bea0ce567e182cac89 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 23:16:55 +0300 Subject: [PATCH 64/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index aceda8d..279f7fb 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -35,25 +35,10 @@ jobs: mkdir docArchives cp -R `find derivedData -type d -name "*.doccarchive"` docArchives $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path loay-ashraf/RxNetworkKit --output-path docs - - name: Setup Pages - id: pages - uses: actions/configure-pages@v3 - name: Upload Docs artifact uses: actions/upload-pages-artifact@v1 with: path: 'docs' -# - name: Deploy Docs artifact -# id: deployment -# uses: actions/deploy-pages@v1 - - deploy: - needs: build-deploy-docs - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - name: Deploy to GitHub Pages + - name: Deploy Docs artifact id: deployment - uses: actions/deploy-pages@v2 - + uses: actions/deploy-pages@v1 From fff4d441bf99b311c57d2f1ab4a01e316d7892b0 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 23:29:47 +0300 Subject: [PATCH 65/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 279f7fb..4c8fb9c 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -12,9 +12,6 @@ permissions: jobs: build-deploy-docs: name: Build and Deploy Docs -# environment: -# name: github-pages -# url: ${{ steps.deployment.outputs.page_url }} runs-on: macos-13 steps: From 5b5b665eee5b4fd5dd65d500f92258c94f2f6e57 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 23:29:58 +0300 Subject: [PATCH 66/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 4c8fb9c..64be3b1 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -31,7 +31,7 @@ jobs: run: | mkdir docArchives cp -R `find derivedData -type d -name "*.doccarchive"` docArchives - $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path loay-ashraf/RxNetworkKit --output-path docs + $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs - name: Upload Docs artifact uses: actions/upload-pages-artifact@v1 with: From 9cdd3eb6f79467e9ce5a24cf9e7832a64cc7812f Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 23:50:36 +0300 Subject: [PATCH 67/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 64be3b1..73fbde4 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -32,6 +32,7 @@ jobs: mkdir docArchives cp -R `find derivedData -type d -name "*.doccarchive"` docArchives $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs + echo "" > docs/index.html; - name: Upload Docs artifact uses: actions/upload-pages-artifact@v1 with: From 9fdbd6fe6cceb89ee7a841c70de918d46f6bf551 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 23:58:44 +0300 Subject: [PATCH 68/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 73fbde4..40131b4 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -33,6 +33,15 @@ jobs: cp -R `find derivedData -type d -name "*.doccarchive"` docArchives $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs echo "" > docs/index.html; + + + deploy: + runs-on: macos-13 + needs: build-deploy-docs + + steps: + - name: Checkout + uses: actions/checkout@v3 - name: Upload Docs artifact uses: actions/upload-pages-artifact@v1 with: From 4ae026d38c6ad7632ac462f54cc71c1d22675f55 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Fri, 6 Oct 2023 00:07:45 +0300 Subject: [PATCH 69/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 40131b4..3cd0f0a 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -27,13 +27,11 @@ jobs: scheme: RxNetworkKit run: | xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -derivedDataPath derivedData | xcpretty - - name: Perpare Docs for Deployment - run: | - mkdir docArchives - cp -R `find derivedData -type d -name "*.doccarchive"` docArchives - $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs - echo "" > docs/index.html; - + - name: Upload Derived Data + uses: actions/upload-artifact@v3 + with: + name: derived-data + path: derivedData deploy: runs-on: macos-13 @@ -42,6 +40,16 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + - name: Download Derived Data + uses: actions/download-artifact@v3 + with: + name: derived-data + - name: Perpare Docs for Deployment + run: | + mkdir docArchives + cp -R `find derivedData -type d -name "*.doccarchive"` docArchives + $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs + echo "" > docs/index.html; - name: Upload Docs artifact uses: actions/upload-pages-artifact@v1 with: From 7fae70d633d450d769f3534190f367df4452fb87 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Fri, 6 Oct 2023 00:14:18 +0300 Subject: [PATCH 70/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 3cd0f0a..60c5e3d 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -27,11 +27,13 @@ jobs: scheme: RxNetworkKit run: | xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -derivedDataPath derivedData | xcpretty - - name: Upload Derived Data + mkdir docArchives + cp -R `find derivedData -type d -name "*.doccarchive"` docArchives + - name: Upload Doc Archive uses: actions/upload-artifact@v3 with: - name: derived-data - path: derivedData + name: doc-archive + path: docArchives/RxNetworkKit.doccarchive deploy: runs-on: macos-13 @@ -43,12 +45,10 @@ jobs: - name: Download Derived Data uses: actions/download-artifact@v3 with: - name: derived-data + name: doc-archive - name: Perpare Docs for Deployment run: | - mkdir docArchives - cp -R `find derivedData -type d -name "*.doccarchive"` docArchives - $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs + $(xcrun --find docc) process-archive transform-for-static-hosting RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs echo "" > docs/index.html; - name: Upload Docs artifact uses: actions/upload-pages-artifact@v1 From e0bd8d6d56e096679c2003a766add39cd7a45b20 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Fri, 6 Oct 2023 00:23:50 +0300 Subject: [PATCH 71/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 60c5e3d..7458cda 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -29,11 +29,12 @@ jobs: xcodebuild docbuild -workspace "${workspace}" -scheme "${scheme}" -derivedDataPath derivedData | xcpretty mkdir docArchives cp -R `find derivedData -type d -name "*.doccarchive"` docArchives + zip -r RxNetworkKitDocCArchive.zip docArchives/RxNetworkKit.doccarchive - name: Upload Doc Archive uses: actions/upload-artifact@v3 with: - name: doc-archive - path: docArchives/RxNetworkKit.doccarchive + name: doc-archive-zip + path: RxNetworkKitDocCArchive.zip deploy: runs-on: macos-13 @@ -45,9 +46,10 @@ jobs: - name: Download Derived Data uses: actions/download-artifact@v3 with: - name: doc-archive + name: doc-archive-zip - name: Perpare Docs for Deployment run: | + unzip RxNetworkKitDocCArchive.zip $(xcrun --find docc) process-archive transform-for-static-hosting RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs echo "" > docs/index.html; - name: Upload Docs artifact From 6433d3ab2b387af98cf5ec1c483deb2d9ddf54ff Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Fri, 6 Oct 2023 00:30:48 +0300 Subject: [PATCH 72/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 7458cda..273774c 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -50,7 +50,7 @@ jobs: - name: Perpare Docs for Deployment run: | unzip RxNetworkKitDocCArchive.zip - $(xcrun --find docc) process-archive transform-for-static-hosting RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs + $(xcrun --find docc) process-archive transform-for-static-hosting docArchives/RxNetworkKit.doccarchive --hosting-base-path RxNetworkKit --output-path docs echo "" > docs/index.html; - name: Upload Docs artifact uses: actions/upload-pages-artifact@v1 From 621cb734034e0af3491a5860b2f7247ea599e4f2 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Fri, 6 Oct 2023 00:45:15 +0300 Subject: [PATCH 73/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 273774c..2dc8c7b 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -10,8 +10,8 @@ permissions: id-token: write jobs: - build-deploy-docs: - name: Build and Deploy Docs + build-docs: + name: Build Docs runs-on: macos-13 steps: @@ -37,8 +37,9 @@ jobs: path: RxNetworkKitDocCArchive.zip deploy: + name: Deploy Docs runs-on: macos-13 - needs: build-deploy-docs + needs: build-docs steps: - name: Checkout From 63511b55ec793c47c267c086b95ab1d27980ba6e Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Fri, 6 Oct 2023 00:46:02 +0300 Subject: [PATCH 74/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 2dc8c7b..d238713 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -36,7 +36,7 @@ jobs: name: doc-archive-zip path: RxNetworkKitDocCArchive.zip - deploy: + deploy-docs: name: Deploy Docs runs-on: macos-13 needs: build-docs From c02167f287cc2fbc12491303c2c8e34455d87e5a Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Fri, 6 Oct 2023 01:26:11 +0300 Subject: [PATCH 75/95] Update build-deploy-docs.yml --- .github/workflows/build-deploy-docs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index d238713..7419c84 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -30,7 +30,7 @@ jobs: mkdir docArchives cp -R `find derivedData -type d -name "*.doccarchive"` docArchives zip -r RxNetworkKitDocCArchive.zip docArchives/RxNetworkKit.doccarchive - - name: Upload Doc Archive + - name: Upload Docs Archive uses: actions/upload-artifact@v3 with: name: doc-archive-zip @@ -44,7 +44,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Download Derived Data + - name: Download Docs Archive uses: actions/download-artifact@v3 with: name: doc-archive-zip From ee129c72d573cdce0afe2fc3085a9d11e6b95f4a Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Mon, 9 Oct 2023 22:39:25 +0300 Subject: [PATCH 76/95] Update websocket error handling (#58) * feature: bind send and ping errors to the error relay in WebSocket object Resolves: none. * fix: capture weak reference to self to avoid retain cycle in example viewModel Resolves: none. * feat: add WebSocketError Resolves: none. * Update NetworkManager.md --- Docs.docc/Pages/NetworkManager.md | 1 + .../iOS Example/View Model/ViewModel.swift | 3 ++- .../macOS Example/View Model/ViewModel.swift | 3 ++- RxNetworkKit.xcodeproj/project.pbxproj | 4 +++ .../Web Socket/Extensions/Reactive+ping.swift | 4 ++- .../Extensions/Reactive+receive.swift | 7 ++--- .../Web Socket/Extensions/Reactive+send.swift | 4 ++- Source/Web Socket/WebSocket.swift | 26 ++++++++++++++----- Source/Web Socket/WebSocketError.swift | 15 +++++++++++ 9 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 Source/Web Socket/WebSocketError.swift diff --git a/Docs.docc/Pages/NetworkManager.md b/Docs.docc/Pages/NetworkManager.md index 21e055b..3061998 100644 --- a/Docs.docc/Pages/NetworkManager.md +++ b/Docs.docc/Pages/NetworkManager.md @@ -34,4 +34,5 @@ - ``WebSocketMessage`` - ``WebSocketCloseCode`` - ``WebSocketCloseHandler`` +- ``WebSocketError`` - ``NetworkManager/webSocket(_:_:_:)`` diff --git a/Examples/iOS/iOS Example/View Model/ViewModel.swift b/Examples/iOS/iOS Example/View Model/ViewModel.swift index dfe174f..aa5517e 100644 --- a/Examples/iOS/iOS Example/View Model/ViewModel.swift +++ b/Examples/iOS/iOS Example/View Model/ViewModel.swift @@ -40,7 +40,8 @@ class ViewModel { // Create default sequence with default API call request. let loadObservable = viewState .filter( { ![.idle, .loading(loadType: .paginate), .error].contains($0) }) - .flatMapLatest{ _ in + .flatMapLatest{ [weak self] _ in + guard let self = self else { return Observable<[Model]>.empty().materialize() } let single: Single<[Model]> = self.networkManager.request(Router.default) return single .asObservable() diff --git a/Examples/macOS/macOS Example/View Model/ViewModel.swift b/Examples/macOS/macOS Example/View Model/ViewModel.swift index dfe174f..aa5517e 100644 --- a/Examples/macOS/macOS Example/View Model/ViewModel.swift +++ b/Examples/macOS/macOS Example/View Model/ViewModel.swift @@ -40,7 +40,8 @@ class ViewModel { // Create default sequence with default API call request. let loadObservable = viewState .filter( { ![.idle, .loading(loadType: .paginate), .error].contains($0) }) - .flatMapLatest{ _ in + .flatMapLatest{ [weak self] _ in + guard let self = self else { return Observable<[Model]>.empty().materialize() } let single: Single<[Model]> = self.networkManager.request(Router.default) return single .asObservable() diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index c6a5a47..936b8d2 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -63,6 +63,7 @@ C6A9BEFA2A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */; }; C6A9BEFD2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */; }; C6A9BEFF2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */; }; + C6B4B4C42AD47A2F009073ED /* WebSocketError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B4B4C32AD47A2F009073ED /* WebSocketError.swift */; }; C6BDFFE82ACDF3830022F675 /* Reactive+receive.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFE72ACDF3830022F675 /* Reactive+receive.swift */; }; C6BDFFEA2ACDF3D90022F675 /* Reactive+send.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFE92ACDF3D90022F675 /* Reactive+send.swift */; }; C6BDFFEC2ACDF4100022F675 /* Reactive+ping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFEB2ACDF4100022F675 /* Reactive+ping.swift */; }; @@ -127,6 +128,7 @@ C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+setAdditionalHTTPHeader.swift"; sourceTree = ""; }; C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+setUserAgentHTTPHeader.swift"; sourceTree = ""; }; C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+operatingSystemName.swift"; sourceTree = ""; }; + C6B4B4C32AD47A2F009073ED /* WebSocketError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocketError.swift; sourceTree = ""; }; C6BDFFE72ACDF3830022F675 /* Reactive+receive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+receive.swift"; sourceTree = ""; }; C6BDFFE92ACDF3D90022F675 /* Reactive+send.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+send.swift"; sourceTree = ""; }; C6BDFFEB2ACDF4100022F675 /* Reactive+ping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+ping.swift"; sourceTree = ""; }; @@ -364,6 +366,7 @@ C6BDFFEF2ACDF4AB0022F675 /* WebSocketCloseHandler.swift */, C6BDFFF22ACDF50F0022F675 /* WebSocketMessage.swift */, C6BDFFF42ACDF5250022F675 /* WebSocketCloseCode.swift */, + C6B4B4C32AD47A2F009073ED /* WebSocketError.swift */, ); path = "Web Socket"; sourceTree = ""; @@ -498,6 +501,7 @@ 0B77E09129D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift in Sources */, C6A9BEFA2A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift in Sources */, C6BDFFF32ACDF5100022F675 /* WebSocketMessage.swift in Sources */, + C6B4B4C42AD47A2F009073ED /* WebSocketError.swift in Sources */, 0B77E0A629D965D30077FBC0 /* DefaultNetworkAPIError.swift in Sources */, 0B77E0A429D965D30077FBC0 /* NetworkReachability.swift in Sources */, 0B77E0B229D965D30077FBC0 /* Completable+Retry.swift in Sources */, diff --git a/Source/Web Socket/Extensions/Reactive+ping.swift b/Source/Web Socket/Extensions/Reactive+ping.swift index 10ae88b..deea8d2 100644 --- a/Source/Web Socket/Extensions/Reactive+ping.swift +++ b/Source/Web Socket/Extensions/Reactive+ping.swift @@ -9,6 +9,7 @@ import Foundation import RxSwift extension Reactive where Base: URLSessionWebSocketTask { + /// Sends a ping to the websocket server. /// /// - Returns: `Completable` observable encapsulating send ping operation. @@ -19,9 +20,10 @@ extension Reactive where Base: URLSessionWebSocketTask { subscription(.completed) return } - subscription(.error(error)) + subscription(.error(WebSocketError.ping(error: error))) } return Disposables.create() } } + } diff --git a/Source/Web Socket/Extensions/Reactive+receive.swift b/Source/Web Socket/Extensions/Reactive+receive.swift index 701fb75..988d0b9 100644 --- a/Source/Web Socket/Extensions/Reactive+receive.swift +++ b/Source/Web Socket/Extensions/Reactive+receive.swift @@ -23,16 +23,13 @@ extension Reactive where Base: URLSessionWebSocketTask { subscription.onNext(message) receive(subscription: subscription) case .failure(let error): - subscription.onError(error) + subscription.onError(WebSocketError.receive(error: error)) } } } return Observable.create { subscription in receive(subscription: subscription) - return Disposables.create { - let request = base.currentRequest - base.cancel(with: closeHandler.code(for: request), reason: closeHandler.reason(for: request)) - } + return Disposables.create() } .share() } diff --git a/Source/Web Socket/Extensions/Reactive+send.swift b/Source/Web Socket/Extensions/Reactive+send.swift index 383f7d1..693ae8b 100644 --- a/Source/Web Socket/Extensions/Reactive+send.swift +++ b/Source/Web Socket/Extensions/Reactive+send.swift @@ -9,6 +9,7 @@ import Foundation import RxSwift extension Reactive where Base: URLSessionWebSocketTask { + /// Sends message to the websocket server. /// /// - Parameter message: `WebSocketMessage` to be sent to websocket server. @@ -21,9 +22,10 @@ extension Reactive where Base: URLSessionWebSocketTask { subscription(.completed) return } - subscription(.error(error)) + subscription(.error(WebSocketError.send(error: error))) } return Disposables.create() } } + } diff --git a/Source/Web Socket/WebSocket.swift b/Source/Web Socket/WebSocket.swift index 7edb256..7c7a644 100644 --- a/Source/Web Socket/WebSocket.swift +++ b/Source/Web Socket/WebSocket.swift @@ -17,7 +17,7 @@ public class WebSocket { /// a generic `PublishRelay` object for data messages received from websocket server. public let data: PublishRelay = .init() /// a `PublishRelay` object for errors encountered while receiving/sending from/to websocket server. - public let error: PublishRelay = .init() + public let error: PublishRelay = .init() private let task: URLSessionWebSocketTask private let closeHandler: WebSocketCloseHandler private let receiveObservable: Observable @@ -35,6 +35,11 @@ public class WebSocket { setupBindings() } + /// Destroys current `WebSocket` instance and cancels current task. + deinit { + disconnect() + } + /// Resumes current task to establish the connection. public func connect() { task.resume() @@ -49,17 +54,23 @@ public class WebSocket { /// Sends message to the websocket server. /// /// - Parameter message: `WebSocketMessage` to be sent to websocket server. - /// - /// - Returns: `Completable` observable encapsulating send message operation. - public func send(_ message: WebSocketMessage) -> Completable { + public func send(_ message: WebSocketMessage) { task.rx.send(message: message) + .subscribe(onError: { error in + guard let error = error as? WebSocketError else { return } + self.error.accept(error) + }) + .disposed(by: disposeBag) } /// Sends a ping to the websocket server. - /// - /// - Returns: `Completable` observable encapsulating send ping operation. - public func ping() -> Completable { + public func ping() { task.rx.ping() + .subscribe(onError: { error in + guard let error = error as? WebSocketError else { return } + self.error.accept(error) + }) + .disposed(by: disposeBag) } /// Sets up internal observable bindings. @@ -92,6 +103,7 @@ public class WebSocket { .materialize() .compactMap({ guard case .error(let error) = $0 else { return nil } + guard let error = error as? WebSocketError else { return nil } return error }) .bind(to: error) diff --git a/Source/Web Socket/WebSocketError.swift b/Source/Web Socket/WebSocketError.swift new file mode 100644 index 0000000..5b8f2a9 --- /dev/null +++ b/Source/Web Socket/WebSocketError.swift @@ -0,0 +1,15 @@ +// +// WebSocketError.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 09/10/2023. +// + +import Foundation + +/// An enumeration that contains cases where error is received from websocket. +public enum WebSocketError: Error { + case receive(error: Error) + case send(error: Error) + case ping(error: Error) +} From 66caa61ea59524aab1ac9009c8cf2e5ba0d850a9 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Tue, 10 Oct 2023 18:56:56 +0300 Subject: [PATCH 77/95] Update build.yml --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9d65597..f92d79f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,3 +29,4 @@ jobs: build-deploy-docs: name: Build and Deploy Docs uses: ./.github/workflows/build-deploy-docs.yml + if: github.ref == 'refs/heads/main' From b6bc7b3eb5a3344ab84cfabd9480546da9c32ab4 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Tue, 9 Jan 2024 18:03:45 +0200 Subject: [PATCH 78/95] Rename Types and Organize Project files (#61) * feat: sort files by name inside Source folder Resolves: none. * feat: restructure framework project Resolves: none. * feat: update docs Resolves: none. * feat: update docs Resolves: none. * feat: update examples Resolves: none. * feat: update docs Resolves: none. * feat: move common http types to separate framework Resolves: none. * update ci * update ci * feat: add CoreHTTP as swift package Resolves: none. * Update Package.swift * feat: add CoreHTTP as a remote Swift Package Resolves: none. * Update Package.swift * Update Package.swift * feat: update docs Resolves: none. * Update build-macos.yml * Update build-macos.yml * feat: add CoreExample framework Resolves: none. * feat: add CoreExample as a dependency Resolves: none. * feat: add CoreExample to macOS Example Resolves: none. * fix: update code signing for framework Resolves: none. * Update build-macos.yml * Update build-macos.yml * update frameworks flow * Update build-ios.yml * Update Package.resolved * Update project.pbxproj * Update project.pbxproj * Update build-ios.yml * update dependencies * feat: add Session object that can be shared between RESTClient and HTTPClient Resolves: none. * refactor: remove reference to event monitor in RESTClient and HTTPClient Resolves: none. * feat: update ci workflows Resolves: none. * feat: update documentation Resolves: none. --- .github/workflows/build-ios.yml | 12 +- .github/workflows/build-macos.yml | 6 +- Docs.docc/Pages/HTTPClient.md | 32 ++ Docs.docc/Pages/NetworkManager.md | 38 -- Docs.docc/Pages/RESTClient.md | 13 + Docs.docc/Pages/RxNetworkKit.md | 27 +- .../iOS/iOS Example.xcodeproj/project.pbxproj | 171 ++----- .../ViewController+NetworkEventMonitor.swift | 26 - ...Controller+NetworkRequestInterceptor.swift | 24 - .../Controller/ViewController.swift | 13 +- Examples/iOS/iOS Example/Model/Model.swift | 21 - .../macOS Example.xcodeproj/project.pbxproj | 185 +++---- .../xcschemes/macOS Example.xcscheme | 77 +++ .../ViewController+NetworkEventMonitor.swift | 26 - ...Controller+NetworkRequestInterceptor.swift | 24 - .../Controller/ViewController.swift | 13 +- .../Network/DownloadRouter.swift | 34 -- .../macOS/macOS Example/Network/Router.swift | 38 -- .../macOS Example/View Model/ViewModel.swift | 70 --- .../macOS Example/View/ViewLoadType.swift | 12 - .../macOS/macOS Example/View/ViewState.swift | 12 - Package.resolved | 17 +- Package.swift | 3 +- RxNetworkKit.xcodeproj/project.pbxproj | 341 +++++-------- .../contents.xcworkspacedata | 5 +- .../xcshareddata/swiftpm/Package.resolved | 9 + .../CoreExample.xcodeproj/project.pbxproj | 481 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 14 + .../xcschemes/CoreExample.xcscheme | 66 +++ Shared/CoreExample/Source/CoreExample.h | 18 + .../CoreExample/Source}/Model/Model.swift | 10 +- .../Network/DownloadRequestRouter.swift | 16 +- .../Source/Network/RequestEventMonitor.swift | 26 + .../Source/Network/RequestInterceptor.swift | 24 + .../Source/Network/RequestRouter.swift | 20 +- .../Source}/View Model/ViewModel.swift | 29 +- .../Source}/View/ViewLoadType.swift | 2 +- .../CoreExample/Source}/View/ViewState.swift | 2 +- Source/Error/DefaultNetworkAPIError.swift | 14 - Source/Error/NetworkAPIError.swift | 9 - Source/Error/NetworkClientError.swift | 13 - Source/Error/NetworkError.swift | 41 -- Source/Error/NetworkServerError.swift | 12 - .../Event Monitor/NetworkEventMonitor.swift | 13 - Source/HTTP/DefaultHTTPErrorBody.swift | 20 - .../Extensions}/Data+AppendString.swift | 0 .../Extensions}/Observable+Decodable.swift | 8 +- .../Extensions}/Observable+Retry.swift | 6 +- .../ProcessInfo+operatingSystemName.swift | 0 .../Extensions}/Reactive+Curl.swift | 3 +- ...ve+URLSessionAdaptedDownloadResponse.swift | 21 +- ...tive+URLSessionAdaptedUploadResponse.swift | 25 +- .../Reactive+URLSessionDownloadResponse.swift | 2 +- .../Reactive+URLSessionUploadResponse.swift | 8 +- .../Extensions/Reactive+WebSocketPing.swift} | 2 +- .../Reactive+WebSocketReceive.swift} | 2 +- .../Extensions/Reactive+WebSocketSend.swift} | 2 +- .../Extensions}/URLSession+DownloadTask.swift | 0 .../Extensions}/URLSession+UploadTask.swift | 16 +- ...onfiguration+setAdditionalHTTPHeader.swift | 0 ...Configuration+setUserAgentHTTPHeader.swift | 2 +- Source/HTTP/HTTPErrorBody.swift | 11 - Source/HTTP/HTTPMethod.swift | 18 - Source/HTTP/HTTPScheme.swift | 12 - Source/HTTP/HTTPStatusCode.swift | 193 ------- Source/HTTP/HTTPURLResponse+StatusCode.swift | 14 - Source/HTTP/Types/Client/HTTPClient.swift | 147 ++++++ .../Event/HTTPDownloadRequestEvent.swift} | 4 +- .../Event/HTTPUploadRequestEvent.swift} | 4 +- .../Request/Parameters}/HTTPMIMEType.swift | 0 .../Parameters/HTTPUploadRequestFile.swift} | 4 +- .../HTTPUploadRequestFormData.swift} | 6 +- .../Router/HTTPDownloadRequestRouter.swift} | 7 +- .../Router/HTTPUploadRequestRouter.swift} | 6 +- .../Types}/Web Socket/WebSocket.swift | 0 .../Web Socket/WebSocketCloseCode.swift | 0 .../Web Socket/WebSocketCloseHandler.swift | 0 .../Types}/Web Socket/WebSocketError.swift | 0 .../Types}/Web Socket/WebSocketMessage.swift | 0 Source/Manager/NetworkManager.swift | 202 -------- .../Extensions}/Completable+Retry.swift | 7 +- .../Extensions}/Single+CatchErrors.swift | 10 +- .../Extensions}/Single+Decodable.swift | 10 +- .../Extensions}/Single+Decode.swift | 12 +- .../Extensions}/Single+Retry.swift | 6 +- .../Extensions}/Single+VerifyResponse.swift | 4 +- Source/REST/Types/Client/RESTClient.swift | 85 ++++ .../Request Interceptor/RequestAdapter.swift | 22 - .../RequestInterceptor.swift | 9 - .../Request Interceptor/RequestRetrier.swift | 41 -- .../RequestRetryPolicy.swift | 38 -- Source/Router/NetworkRouter.swift | 63 --- Source/RxNetworkKit.swift | 8 + Source/Session/Session.swift | 39 ++ 96 files changed, 1507 insertions(+), 1666 deletions(-) create mode 100644 Docs.docc/Pages/HTTPClient.md delete mode 100644 Docs.docc/Pages/NetworkManager.md create mode 100644 Docs.docc/Pages/RESTClient.md delete mode 100644 Examples/iOS/iOS Example/Controller/ViewController+NetworkEventMonitor.swift delete mode 100644 Examples/iOS/iOS Example/Controller/ViewController+NetworkRequestInterceptor.swift delete mode 100644 Examples/iOS/iOS Example/Model/Model.swift create mode 100644 Examples/macOS/macOS Example.xcodeproj/xcshareddata/xcschemes/macOS Example.xcscheme delete mode 100644 Examples/macOS/macOS Example/Controller/ViewController+NetworkEventMonitor.swift delete mode 100644 Examples/macOS/macOS Example/Controller/ViewController+NetworkRequestInterceptor.swift delete mode 100644 Examples/macOS/macOS Example/Network/DownloadRouter.swift delete mode 100644 Examples/macOS/macOS Example/Network/Router.swift delete mode 100644 Examples/macOS/macOS Example/View Model/ViewModel.swift delete mode 100644 Examples/macOS/macOS Example/View/ViewLoadType.swift delete mode 100644 Examples/macOS/macOS Example/View/ViewState.swift create mode 100644 Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj create mode 100644 Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Shared/CoreExample/CoreExample.xcodeproj/xcshareddata/xcschemes/CoreExample.xcscheme create mode 100644 Shared/CoreExample/Source/CoreExample.h rename {Examples/macOS/macOS Example => Shared/CoreExample/Source}/Model/Model.swift (64%) rename Examples/iOS/iOS Example/Network/DownloadRouter.swift => Shared/CoreExample/Source/Network/DownloadRequestRouter.swift (50%) create mode 100644 Shared/CoreExample/Source/Network/RequestEventMonitor.swift create mode 100644 Shared/CoreExample/Source/Network/RequestInterceptor.swift rename Examples/iOS/iOS Example/Network/Router.swift => Shared/CoreExample/Source/Network/RequestRouter.swift (53%) rename {Examples/iOS/iOS Example => Shared/CoreExample/Source}/View Model/ViewModel.swift (64%) rename {Examples/iOS/iOS Example => Shared/CoreExample/Source}/View/ViewLoadType.swift (84%) rename {Examples/iOS/iOS Example => Shared/CoreExample/Source}/View/ViewState.swift (82%) delete mode 100644 Source/Error/DefaultNetworkAPIError.swift delete mode 100644 Source/Error/NetworkAPIError.swift delete mode 100644 Source/Error/NetworkClientError.swift delete mode 100644 Source/Error/NetworkError.swift delete mode 100644 Source/Error/NetworkServerError.swift delete mode 100644 Source/Event Monitor/NetworkEventMonitor.swift delete mode 100644 Source/HTTP/DefaultHTTPErrorBody.swift rename Source/{Custom Requests/Upload => HTTP/Extensions}/Data+AppendString.swift (100%) rename Source/{Rx Extensions/Rx+NetworkOps/Observable => HTTP/Extensions}/Observable+Decodable.swift (79%) rename Source/{Rx Extensions/Rx+NetworkOps/Observable => HTTP/Extensions}/Observable+Retry.swift (84%) rename Source/{Manager => HTTP/Extensions}/ProcessInfo+operatingSystemName.swift (100%) rename Source/{Rx Extensions/Rx+CurlCommand => HTTP/Extensions}/Reactive+Curl.swift (97%) rename Source/{Custom Requests/Download => HTTP/Extensions}/Reactive+URLSessionAdaptedDownloadResponse.swift (62%) rename Source/{Custom Requests/Upload => HTTP/Extensions}/Reactive+URLSessionAdaptedUploadResponse.swift (60%) rename Source/{Custom Requests/Download => HTTP/Extensions}/Reactive+URLSessionDownloadResponse.swift (98%) rename Source/{Custom Requests/Upload => HTTP/Extensions}/Reactive+URLSessionUploadResponse.swift (90%) rename Source/{Web Socket/Extensions/Reactive+ping.swift => HTTP/Extensions/Reactive+WebSocketPing.swift} (95%) rename Source/{Web Socket/Extensions/Reactive+receive.swift => HTTP/Extensions/Reactive+WebSocketReceive.swift} (97%) rename Source/{Web Socket/Extensions/Reactive+send.swift => HTTP/Extensions/Reactive+WebSocketSend.swift} (95%) rename Source/{Custom Requests/Download => HTTP/Extensions}/URLSession+DownloadTask.swift (100%) rename Source/{Custom Requests/Upload => HTTP/Extensions}/URLSession+UploadTask.swift (81%) rename Source/{Manager => HTTP/Extensions}/URLSessionConfiguration+setAdditionalHTTPHeader.swift (100%) rename Source/{Manager => HTTP/Extensions}/URLSessionConfiguration+setUserAgentHTTPHeader.swift (92%) delete mode 100644 Source/HTTP/HTTPErrorBody.swift delete mode 100644 Source/HTTP/HTTPMethod.swift delete mode 100644 Source/HTTP/HTTPScheme.swift delete mode 100644 Source/HTTP/HTTPStatusCode.swift delete mode 100644 Source/HTTP/HTTPURLResponse+StatusCode.swift create mode 100644 Source/HTTP/Types/Client/HTTPClient.swift rename Source/{Custom Requests/Download/DownloadEvent.swift => HTTP/Types/Request/Event/HTTPDownloadRequestEvent.swift} (78%) rename Source/{Custom Requests/Upload/UploadEvent.swift => HTTP/Types/Request/Event/HTTPUploadRequestEvent.swift} (73%) rename Source/HTTP/{ => Types/Request/Parameters}/HTTPMIMEType.swift (100%) rename Source/{Custom Requests/Upload/UploadFile.swift => HTTP/Types/Request/Parameters/HTTPUploadRequestFile.swift} (95%) rename Source/{Custom Requests/Upload/UploadFormData.swift => HTTP/Types/Request/Parameters/HTTPUploadRequestFormData.swift} (72%) rename Source/{Router/NetworkDownloadRouter.swift => HTTP/Types/Request/Router/HTTPDownloadRequestRouter.swift} (70%) rename Source/{Router/NetworkUploadRouter.swift => HTTP/Types/Request/Router/HTTPUploadRequestRouter.swift} (73%) rename Source/{ => HTTP/Types}/Web Socket/WebSocket.swift (100%) rename Source/{ => HTTP/Types}/Web Socket/WebSocketCloseCode.swift (100%) rename Source/{ => HTTP/Types}/Web Socket/WebSocketCloseHandler.swift (100%) rename Source/{ => HTTP/Types}/Web Socket/WebSocketError.swift (100%) rename Source/{ => HTTP/Types}/Web Socket/WebSocketMessage.swift (100%) delete mode 100644 Source/Manager/NetworkManager.swift rename Source/{Rx Extensions/Rx+NetworkOps/Completable => REST/Extensions}/Completable+Retry.swift (85%) rename Source/{Rx Extensions/Rx+NetworkOps/Single => REST/Extensions}/Single+CatchErrors.swift (74%) rename Source/{Rx Extensions/Rx+NetworkOps/Single => REST/Extensions}/Single+Decodable.swift (80%) rename Source/{Rx Extensions/Rx+NetworkOps/Single => REST/Extensions}/Single+Decode.swift (80%) rename Source/{Rx Extensions/Rx+NetworkOps/Single => REST/Extensions}/Single+Retry.swift (85%) rename Source/{Rx Extensions/Rx+NetworkOps/Single => REST/Extensions}/Single+VerifyResponse.swift (78%) create mode 100644 Source/REST/Types/Client/RESTClient.swift delete mode 100644 Source/Request Interceptor/RequestAdapter.swift delete mode 100644 Source/Request Interceptor/RequestInterceptor.swift delete mode 100644 Source/Request Interceptor/RequestRetrier.swift delete mode 100644 Source/Request Interceptor/RequestRetryPolicy.swift delete mode 100644 Source/Router/NetworkRouter.swift create mode 100644 Source/RxNetworkKit.swift create mode 100644 Source/Session/Session.swift diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index 635c4ad..cef0351 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -15,14 +15,14 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.0.0' + xcode-version: 'latest-stable' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace scheme: RxNetworkKit destination: generic/platform=iOS Simulator run: | - xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} framework-ios: name: Build Framework For iOS Device @@ -34,14 +34,14 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.0.0' + xcode-version: 'latest-stable' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace scheme: RxNetworkKit destination: generic/platform=iOS run: | - xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} example-ios-simulator: name: Build Example For iOS Simulator @@ -54,7 +54,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.0.0' + xcode-version: 'latest-stable' - name: Build Example env: workspace: RxNetworkKit.xcworkspace @@ -74,7 +74,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.0.0' + xcode-version: 'latest-stable' - name: Build Example env: workspace: RxNetworkKit.xcworkspace diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index b9ccbb3..d8ef568 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -14,14 +14,14 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.0.0' + xcode-version: 'latest-stable' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace scheme: RxNetworkKit destination: generic/platform=macOS run: | - xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" docbuild | xcpretty && exit ${PIPESTATUS[0]} + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} example-macos: name: Build Example For macOS @@ -34,7 +34,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.0.0' + xcode-version: 'latest-stable' - name: Build Example env: workspace: RxNetworkKit.xcworkspace diff --git a/Docs.docc/Pages/HTTPClient.md b/Docs.docc/Pages/HTTPClient.md new file mode 100644 index 0000000..86691a2 --- /dev/null +++ b/Docs.docc/Pages/HTTPClient.md @@ -0,0 +1,32 @@ +# ``HTTPClient`` + +## Topics + +### Creating a HTTP Client + +- ``HTTPClient/init(session:requestInterceptor:)`` + +### Downloading a File + +- ``HTTPDownloadRequestRouter`` +- ``HTTPDownloadRequestEvent`` +- ``HTTPClient/download(_:_:_:)`` +- ``HTTPClient/download(_:_:_:_:)`` + +### Uploading a File + +- ``HTTPUploadRequestRouter`` +- ``HTTPUploadRequestEvent`` +- ``HTTPUploadRequestFile`` +- ``HTTPUploadRequestFormData`` +- ``HTTPClient/upload(_:_:_:_:)-cavg`` +- ``HTTPClient/upload(_:_:_:_:)-2m1kr`` + +### Connecting to a WebSocket + +- ``WebSocket`` +- ``WebSocketMessage`` +- ``WebSocketCloseCode`` +- ``WebSocketCloseHandler`` +- ``WebSocketError`` +- ``HTTPClient/webSocket(_:_:_:)`` diff --git a/Docs.docc/Pages/NetworkManager.md b/Docs.docc/Pages/NetworkManager.md deleted file mode 100644 index 3061998..0000000 --- a/Docs.docc/Pages/NetworkManager.md +++ /dev/null @@ -1,38 +0,0 @@ -# ``NetworkManager`` - -## Topics - -### Creating a Network Manager - -- ``NetworkManager/init(configuration:requestInterceptor:eventMonitor:)`` - -### Making a Request - -- ``NetworkRouter`` -- ``NetworkManager/request(_:_:_:)-2m73c`` -- ``NetworkManager/request(_:_:_:)-2icwe`` - -### Downloading a File - -- ``NetworkDownloadRouter`` -- ``DownloadEvent`` -- ``NetworkManager/download(_:_:_:)`` -- ``NetworkManager/download(_:_:_:_:)`` - -### Uploading a File - -- ``NetworkUploadRouter`` -- ``UploadEvent`` -- ``UploadFile`` -- ``UploadFormData`` -- ``NetworkManager/upload(_:_:_:_:)-5aqfe`` -- ``NetworkManager/upload(_:_:_:_:)-4xcrt`` - -### Connecting to a WebSocket - -- ``WebSocket`` -- ``WebSocketMessage`` -- ``WebSocketCloseCode`` -- ``WebSocketCloseHandler`` -- ``WebSocketError`` -- ``NetworkManager/webSocket(_:_:_:)`` diff --git a/Docs.docc/Pages/RESTClient.md b/Docs.docc/Pages/RESTClient.md new file mode 100644 index 0000000..ee1404c --- /dev/null +++ b/Docs.docc/Pages/RESTClient.md @@ -0,0 +1,13 @@ +# ``RESTClient`` + +## Topics + +### Creating a REST Client + +- ``RESTClient/init(session:requestInterceptor:)`` + +### Making an API Call + +- ``HTTPRequestRouter`` +- ``RESTClient/request(_:_:_:)-8yak0`` +- ``RESTClient/request(_:_:_:)-2spzm`` diff --git a/Docs.docc/Pages/RxNetworkKit.md b/Docs.docc/Pages/RxNetworkKit.md index 0cb0921..46495d9 100644 --- a/Docs.docc/Pages/RxNetworkKit.md +++ b/Docs.docc/Pages/RxNetworkKit.md @@ -18,34 +18,35 @@ It makes use of RxSwift's traits at request level to acheive a high level of spe ### Foundation -- ``NetworkManager`` +- ``RESTClient`` +- ``HTTPClient`` ### HTTP - ``HTTPScheme`` - ``HTTPMethod`` - ``HTTPStatusCode`` -- ``HTTPErrorBody`` -- ``DefaultHTTPErrorBody`` +- ``HTTPBodyError`` +- ``DefaultHTTPBodyError`` ### Error -- ``NetworkError`` -- ``NetworkAPIError`` -- ``DefaultNetworkAPIError`` -- ``NetworkClientError`` -- ``NetworkServerError`` +- ``HTTPError`` +- ``HTTPAPIError`` +- ``DefaultHTTPAPIError`` +- ``HTTPClientError`` +- ``HTTPServerError`` ### Request Interceptor -- ``NetworkRequestInterceptor`` -- ``NetworkRequestAdapter`` -- ``NetworkRequestRetrier`` -- ``NetworkRequestRetryPolicy`` +- ``HTTPRequestInterceptor`` +- ``HTTPRequestAdapter`` +- ``HTTPRequestRetrier`` +- ``HTTPRequestRetryPolicy`` ### Event Monitor -- ``NetworkEventMonitor`` +- ``HTTPRequestEventMonitor`` ### Network Reachability diff --git a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj index 90971f5..2bbf03d 100644 --- a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj +++ b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj @@ -9,42 +9,29 @@ /* Begin PBXBuildFile section */ 1A78320FA3392498E8F9EA82 /* RxDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = 819F858F2F093441234733F7 /* RxDataSources */; }; 39055093EB07D0FE09141EEC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF62C70BEC0D6E6B113DF07D /* AppDelegate.swift */; }; - 3AEB576B0CFFB902AAB9A011 /* ViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C3894E572FDCE188F82EDC /* ViewState.swift */; }; - 3B7E472276BAE43EC318CEE7 /* ViewController+NetworkEventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C980771BC25F36E3B043532 /* ViewController+NetworkEventMonitor.swift */; }; - 3E8F01214A2EDFF7FF36B4CF /* ViewLoadType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A907DFBE8FCCD6C0B43E63D6 /* ViewLoadType.swift */; }; 51A80F9B659EA32FC640A1BD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C88A69CA6F5F2FCCE6E09D6 /* ViewController.swift */; }; - 57A194118E15AE6C872A9752 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9452311597B38423B82E0C41 /* Router.swift */; }; 5AB3A2AD0715912AF77C0664 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DB3031CC8044F79028730604 /* Main.storyboard */; }; 68B891FE4C701A7ACED986C8 /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78DA3F5B1FB69E1E8AC1985D /* TableViewCell.swift */; }; - 72DA0B2700DBA4EFD95DC6FB /* DownloadRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA2AB00F9F48BF1E7763F5CE /* DownloadRouter.swift */; }; - 72F38840BBCBADB2424F0B21 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE9E3440BC86CB6E6DE5AFB8 /* Model.swift */; }; 8960867AA957F939239DF713 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 70ECA8262019A9E34A028A99 /* LaunchScreen.storyboard */; }; - 9083E2B5C56C9978FA9A88AF /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED4C68630CA1328C530E000D /* ViewModel.swift */; }; - A592421CEFFDBE0D3F6A504D /* ViewController+NetworkRequestInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636BE87DD1664C2D7FAECEDF /* ViewController+NetworkRequestInterceptor.swift */; }; BBC4FB455DB9EFC760A182E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 23795F79829D049A83D55440 /* Assets.xcassets */; }; - C68C3F692A9402DC0036FF9D /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C68C3F682A9401960036FF9D /* RxNetworkKit.framework */; platformFilter = ios; }; - C68C3F6A2A9402DC0036FF9D /* RxNetworkKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C68C3F682A9401960036FF9D /* RxNetworkKit.framework */; platformFilter = ios; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C63EEB382AD7BE61003A64CA /* CoreExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C63EEB372AD7BE61003A64CA /* CoreExample.framework */; }; + C63EEB392AD7BE61003A64CA /* CoreExample.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C63EEB372AD7BE61003A64CA /* CoreExample.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C63EEB3B2AD7C046003A64CA /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C63EEB3A2AD7C046003A64CA /* RxNetworkKit.framework */; }; + C63EEB3C2AD7C046003A64CA /* RxNetworkKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C63EEB3A2AD7C046003A64CA /* RxNetworkKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C68473852AD7D7930071B1F6 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = C68473842AD7D7930071B1F6 /* RxSwift */; }; + C68473872AD7D7990071B1F6 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = C68473862AD7D7990071B1F6 /* RxCocoa */; }; DD59F1300712C00933636CC2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D82BA0503FE7CC526E5586A /* SceneDelegate.swift */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - C68C3F672A9401960036FF9D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0B77E0FA29D977390077FBC0 /* RxNetworkKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 0B77DFD629D964D40077FBC0; - remoteInfo = RxNetworkKit; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ - 0B568E5A29DEDBEC00E51B21 /* Embed Frameworks */ = { + C658CF5F2AD5F09D009E561D /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - C68C3F6A2A9402DC0036FF9D /* RxNetworkKit.framework in Embed Frameworks */, + C63EEB3C2AD7C046003A64CA /* RxNetworkKit.framework in Embed Frameworks */, + C63EEB392AD7BE61003A64CA /* CoreExample.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -52,24 +39,16 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 0B568E4C29DED67300E51B21 /* RxNetworkKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RxNetworkKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 0B77E0FA29D977390077FBC0 /* RxNetworkKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RxNetworkKit.xcodeproj; path = ../../RxNetworkKit.xcodeproj; sourceTree = ""; }; - 0B77E10429D97A3E0077FBC0 /* iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 23795F79829D049A83D55440 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 417337DBE1D0E25D426FCAD6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 4C88A69CA6F5F2FCCE6E09D6 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 513860401B08F9CC5147DACE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 5C980771BC25F36E3B043532 /* ViewController+NetworkEventMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+NetworkEventMonitor.swift"; sourceTree = ""; }; - 636BE87DD1664C2D7FAECEDF /* ViewController+NetworkRequestInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+NetworkRequestInterceptor.swift"; sourceTree = ""; }; 78DA3F5B1FB69E1E8AC1985D /* TableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewCell.swift; sourceTree = ""; }; 7D82BA0503FE7CC526E5586A /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - 9452311597B38423B82E0C41 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; - A907DFBE8FCCD6C0B43E63D6 /* ViewLoadType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewLoadType.swift; sourceTree = ""; }; - A9C3894E572FDCE188F82EDC /* ViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewState.swift; sourceTree = ""; }; - DE9E3440BC86CB6E6DE5AFB8 /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; + C63EEB372AD7BE61003A64CA /* CoreExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoreExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C63EEB3A2AD7C046003A64CA /* RxNetworkKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RxNetworkKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C69B8C042AD60477006DAA47 /* iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; E147CA829E5E766A25250CC2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - EA2AB00F9F48BF1E7763F5CE /* DownloadRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadRouter.swift; sourceTree = ""; }; - ED4C68630CA1328C530E000D /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; EF62C70BEC0D6E6B113DF07D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -78,7 +57,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C68C3F692A9402DC0036FF9D /* RxNetworkKit.framework in Frameworks */, + C63EEB3B2AD7C046003A64CA /* RxNetworkKit.framework in Frameworks */, + C63EEB382AD7BE61003A64CA /* CoreExample.framework in Frameworks */, + C68473872AD7D7990071B1F6 /* RxCocoa in Frameworks */, + C68473852AD7D7930071B1F6 /* RxSwift in Frameworks */, 1A78320FA3392498E8F9EA82 /* RxDataSources in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -91,49 +73,19 @@ children = ( 36E7F1463C2C9E9F2036FAA5 /* App */, 1FDF345BB924980CA278023D /* Controller */, - 4B89E8498C8B58EE28C3C891 /* Model */, - 25BB99B2F9A2D2F5C444E953 /* Network */, 4A90A627BF6330AF8C9D1785 /* View */, - BA9109AFAC6C900555940948 /* View Model */, ); path = "iOS Example"; sourceTree = ""; }; - 0B568E4B29DED67300E51B21 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 0B568E4C29DED67300E51B21 /* RxNetworkKit.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 0B77E10529D97A3E0077FBC0 /* Products */ = { - isa = PBXGroup; - children = ( - 0B77E10429D97A3E0077FBC0 /* iOS Example.app */, - ); - name = Products; - sourceTree = ""; - }; 1FDF345BB924980CA278023D /* Controller */ = { isa = PBXGroup; children = ( 4C88A69CA6F5F2FCCE6E09D6 /* ViewController.swift */, - 5C980771BC25F36E3B043532 /* ViewController+NetworkEventMonitor.swift */, - 636BE87DD1664C2D7FAECEDF /* ViewController+NetworkRequestInterceptor.swift */, ); path = Controller; sourceTree = ""; }; - 25BB99B2F9A2D2F5C444E953 /* Network */ = { - isa = PBXGroup; - children = ( - EA2AB00F9F48BF1E7763F5CE /* DownloadRouter.swift */, - 9452311597B38423B82E0C41 /* Router.swift */, - ); - path = Network; - sourceTree = ""; - }; 36E7F1463C2C9E9F2036FAA5 /* App */ = { isa = PBXGroup; children = ( @@ -151,32 +103,23 @@ children = ( DB3031CC8044F79028730604 /* Main.storyboard */, 78DA3F5B1FB69E1E8AC1985D /* TableViewCell.swift */, - A907DFBE8FCCD6C0B43E63D6 /* ViewLoadType.swift */, - A9C3894E572FDCE188F82EDC /* ViewState.swift */, ); path = View; sourceTree = ""; }; - 4B89E8498C8B58EE28C3C891 /* Model */ = { + C63EEAE32AD7A03E003A64CA /* Frameworks */ = { isa = PBXGroup; children = ( - DE9E3440BC86CB6E6DE5AFB8 /* Model.swift */, + C63EEB3A2AD7C046003A64CA /* RxNetworkKit.framework */, + C63EEB372AD7BE61003A64CA /* CoreExample.framework */, ); - path = Model; - sourceTree = ""; - }; - BA9109AFAC6C900555940948 /* View Model */ = { - isa = PBXGroup; - children = ( - ED4C68630CA1328C530E000D /* ViewModel.swift */, - ); - path = "View Model"; + name = Frameworks; sourceTree = ""; }; - C68C3F642A9401920036FF9D /* Products */ = { + C683C9172AD6089D0061B4F3 /* Products */ = { isa = PBXGroup; children = ( - C68C3F682A9401960036FF9D /* RxNetworkKit.framework */, + C69B8C042AD60477006DAA47 /* iOS Example.app */, ); name = Products; sourceTree = ""; @@ -184,10 +127,9 @@ EDB69B1E108A08763BE6EEC7 = { isa = PBXGroup; children = ( - 0B77E0FA29D977390077FBC0 /* RxNetworkKit.xcodeproj */, 045E34205091BBFDC877D65F /* iOS Example */, - 0B77E10529D97A3E0077FBC0 /* Products */, - 0B568E4B29DED67300E51B21 /* Frameworks */, + C683C9172AD6089D0061B4F3 /* Products */, + C63EEAE32AD7A03E003A64CA /* Frameworks */, ); sourceTree = ""; }; @@ -201,7 +143,7 @@ 97EC71B25102A34723F5A8C0 /* Sources */, DB03F5F61AE8A3039BD18A18 /* Resources */, 0624E543E0F1109CF4C762FE /* Frameworks */, - 0B568E5A29DEDBEC00E51B21 /* Embed Frameworks */, + C658CF5F2AD5F09D009E561D /* Embed Frameworks */, ); buildRules = ( ); @@ -210,9 +152,11 @@ name = "iOS Example"; packageProductDependencies = ( 819F858F2F093441234733F7 /* RxDataSources */, + C68473842AD7D7930071B1F6 /* RxSwift */, + C68473862AD7D7990071B1F6 /* RxCocoa */, ); - productName = RxNetworkingExample; - productReference = 0B77E10429D97A3E0077FBC0 /* iOS Example.app */; + productName = RxNetworkKitExample; + productReference = C69B8C042AD60477006DAA47 /* iOS Example.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -242,15 +186,10 @@ mainGroup = EDB69B1E108A08763BE6EEC7; packageReferences = ( 1380ADB47BEE21E098AC60ED /* XCRemoteSwiftPackageReference "RxDataSources" */, + C63EEAE62AD7A2B9003A64CA /* XCRemoteSwiftPackageReference "RxSwift" */, ); - productRefGroup = 0B77E10529D97A3E0077FBC0 /* Products */; + productRefGroup = EDB69B1E108A08763BE6EEC7; projectDirPath = ""; - projectReferences = ( - { - ProductGroup = C68C3F642A9401920036FF9D /* Products */; - ProjectRef = 0B77E0FA29D977390077FBC0 /* RxNetworkKit.xcodeproj */; - }, - ); projectRoot = ""; targets = ( 26E9FD5EEA5FDD0B3C83D665 /* iOS Example */, @@ -258,16 +197,6 @@ }; /* End PBXProject section */ -/* Begin PBXReferenceProxy section */ - C68C3F682A9401960036FF9D /* RxNetworkKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = RxNetworkKit.framework; - remoteRef = C68C3F672A9401960036FF9D /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - /* Begin PBXResourcesBuildPhase section */ DB03F5F61AE8A3039BD18A18 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -287,17 +216,9 @@ buildActionMask = 2147483647; files = ( 39055093EB07D0FE09141EEC /* AppDelegate.swift in Sources */, - 72DA0B2700DBA4EFD95DC6FB /* DownloadRouter.swift in Sources */, - 72F38840BBCBADB2424F0B21 /* Model.swift in Sources */, - 57A194118E15AE6C872A9752 /* Router.swift in Sources */, DD59F1300712C00933636CC2 /* SceneDelegate.swift in Sources */, 68B891FE4C701A7ACED986C8 /* TableViewCell.swift in Sources */, - 3B7E472276BAE43EC318CEE7 /* ViewController+NetworkEventMonitor.swift in Sources */, - A592421CEFFDBE0D3F6A504D /* ViewController+NetworkRequestInterceptor.swift in Sources */, 51A80F9B659EA32FC640A1BD /* ViewController.swift in Sources */, - 3E8F01214A2EDFF7FF36B4CF /* ViewLoadType.swift in Sources */, - 9083E2B5C56C9978FA9A88AF /* ViewModel.swift in Sources */, - 3AEB576B0CFFB902AAB9A011 /* ViewState.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -396,10 +317,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = Z3SPAA69G7; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)", - ); + FRAMEWORK_SEARCH_PATHS = ""; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "iOS Example/App/Info.plist"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -413,6 +331,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 0.0.2; + OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitExample; PRODUCT_NAME = "iOS Example"; SDKROOT = iphoneos; @@ -435,10 +354,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = Z3SPAA69G7; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)", - ); + FRAMEWORK_SEARCH_PATHS = ""; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "iOS Example/App/Info.plist"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -452,6 +368,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 0.0.2; + OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitExample; PRODUCT_NAME = "iOS Example"; SDKROOT = iphoneos; @@ -552,6 +469,14 @@ minimumVersion = 5.0.0; }; }; + C63EEAE62AD7A2B9003A64CA /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.6.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -560,6 +485,16 @@ package = 1380ADB47BEE21E098AC60ED /* XCRemoteSwiftPackageReference "RxDataSources" */; productName = RxDataSources; }; + C68473842AD7D7930071B1F6 /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = C63EEAE62AD7A2B9003A64CA /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; + C68473862AD7D7990071B1F6 /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = C63EEAE62AD7A2B9003A64CA /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 8FDDFD68FFEB45324727C0BA /* Project object */; diff --git a/Examples/iOS/iOS Example/Controller/ViewController+NetworkEventMonitor.swift b/Examples/iOS/iOS Example/Controller/ViewController+NetworkEventMonitor.swift deleted file mode 100644 index 50b5451..0000000 --- a/Examples/iOS/iOS Example/Controller/ViewController+NetworkEventMonitor.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// ViewController+NetworkEventMonitor.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 01/04/2023. -// - -import Foundation -import RxNetworkKit - -extension ViewController: NetworkEventMonitor { - func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { - debugPrint("") - } - func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { - debugPrint(session) - } - func urlSession(_ session: URLSession, didCreateTask task: URLSessionTask) { - debugPrint("Task created!") - } - func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - guard let error else { return } - debugPrint("Task did finish with error: \(error.localizedDescription)!") - } - func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { } -} diff --git a/Examples/iOS/iOS Example/Controller/ViewController+NetworkRequestInterceptor.swift b/Examples/iOS/iOS Example/Controller/ViewController+NetworkRequestInterceptor.swift deleted file mode 100644 index e625612..0000000 --- a/Examples/iOS/iOS Example/Controller/ViewController+NetworkRequestInterceptor.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ViewController+NetworkRequestInterceptor.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 01/04/2023. -// - -import Foundation -import RxNetworkKit - -extension ViewController: NetworkRequestInterceptor { - func adapt(_ request: URLRequest, for session: URLSession) -> URLRequest { - return request - } - func retryMaxAttempts(_ request: URLRequest, for session: URLSession) -> Int { - 5 - } - func retryPolicy(_ request: URLRequest, for session: URLSession) -> NetworkRequestRetryPolicy { - .constant(time: 5_000) - } - func shouldRetry(_ request: URLRequest, for session: URLSession, dueTo error: NetworkError) -> Bool { - true - } -} diff --git a/Examples/iOS/iOS Example/Controller/ViewController.swift b/Examples/iOS/iOS Example/Controller/ViewController.swift index f6c4472..0d1a0fc 100644 --- a/Examples/iOS/iOS Example/Controller/ViewController.swift +++ b/Examples/iOS/iOS Example/Controller/ViewController.swift @@ -6,11 +6,12 @@ // import UIKit +import SafariServices import RxSwift import RxCocoa import RxDataSources -import SafariServices import RxNetworkKit +import CoreExample class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! @@ -40,8 +41,12 @@ class ViewController: UIViewController { } /// Initializes ViewModel object. private func setupViewModel() { - let manager = NetworkManager(configuration: .default, requestInterceptor: self, eventMonitor: self) - viewModel = .init(networkManager: manager) + let requestInterceptor = RequestInterceptor() + let requestEventMointor = RequestEventMonitor() + let session = Session(configuration: .default, eventMonitor: requestEventMointor) + let restClient = RESTClient(session: session, requestInterceptor: requestInterceptor) + let httpClient = HTTPClient(session: session, requestInterceptor: requestInterceptor) + viewModel = .init(restClient: restClient, httpClient: httpClient) } /// Sets up views. private func setupUI() { @@ -122,7 +127,7 @@ class ViewController: UIViewController { /// - url: `URL` used to download image data. /// - cell: `TableViewCell` that the image data will be applied to. private func downloadTableViewCellImage(using url: URL, applyTo cell: TableViewCell) { - viewModel.downloadImage(DownloadRouter.default(url: url)) + viewModel.downloadImage(DownloadRequestRouter.default(url: url)) .observe(on: MainScheduler.instance) .subscribe(onNext: { switch $0 { diff --git a/Examples/iOS/iOS Example/Model/Model.swift b/Examples/iOS/iOS Example/Model/Model.swift deleted file mode 100644 index a7104db..0000000 --- a/Examples/iOS/iOS Example/Model/Model.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// Model.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 20/03/2023. -// - -import Foundation - -struct Model: Decodable { - let id: Int - let avatarURL: URL - let htmlURL: URL - let login: String - enum CodingKeys: String, CodingKey { - case id - case avatarURL = "avatar_url" - case htmlURL = "html_url" - case login - } -} diff --git a/Examples/macOS/macOS Example.xcodeproj/project.pbxproj b/Examples/macOS/macOS Example.xcodeproj/project.pbxproj index a11a105..8478395 100644 --- a/Examples/macOS/macOS Example.xcodeproj/project.pbxproj +++ b/Examples/macOS/macOS Example.xcodeproj/project.pbxproj @@ -3,45 +3,32 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 55; objects = { /* Begin PBXBuildFile section */ - C639752A2A95166A00D0AC13 /* ViewController+NetworkRequestInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C63975132A95166A00D0AC13 /* ViewController+NetworkRequestInterceptor.swift */; }; - C639752B2A95166A00D0AC13 /* ViewController+NetworkEventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C63975142A95166A00D0AC13 /* ViewController+NetworkEventMonitor.swift */; }; - C639752C2A95166A00D0AC13 /* ViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C63975162A95166A00D0AC13 /* ViewState.swift */; }; - C639752D2A95166A00D0AC13 /* ViewLoadType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C63975172A95166A00D0AC13 /* ViewLoadType.swift */; }; C639752E2A95166A00D0AC13 /* TableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C63975182A95166A00D0AC13 /* TableCellView.swift */; }; - C63975302A95166A00D0AC13 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = C639751C2A95166A00D0AC13 /* Model.swift */; }; - C63975312A95166A00D0AC13 /* DownloadRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C639751E2A95166A00D0AC13 /* DownloadRouter.swift */; }; - C63975322A95166A00D0AC13 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = C639751F2A95166A00D0AC13 /* Router.swift */; }; - C63975332A95166A00D0AC13 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C63975212A95166A00D0AC13 /* ViewModel.swift */; }; + C63EEB3E2AD7C29F003A64CA /* CoreExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C63EEB3D2AD7C29F003A64CA /* CoreExample.framework */; }; + C63EEB3F2AD7C29F003A64CA /* CoreExample.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C63EEB3D2AD7C29F003A64CA /* CoreExample.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C63EEB412AD7C2A4003A64CA /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C63EEB402AD7C2A4003A64CA /* RxNetworkKit.framework */; }; + C63EEB422AD7C2A4003A64CA /* RxNetworkKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C63EEB402AD7C2A4003A64CA /* RxNetworkKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C68473832AD7CFA90071B1F6 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = C68473822AD7CFA90071B1F6 /* RxCocoa */; }; + C68473892AD7D7A70071B1F6 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = C68473882AD7D7A70071B1F6 /* RxSwift */; }; C69FBA182A9408740062BFD7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69FBA172A9408740062BFD7 /* AppDelegate.swift */; }; C69FBA1A2A9408740062BFD7 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69FBA192A9408740062BFD7 /* ViewController.swift */; }; C69FBA1C2A9408770062BFD7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C69FBA1B2A9408770062BFD7 /* Assets.xcassets */; }; C69FBA1F2A9408770062BFD7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C69FBA1D2A9408770062BFD7 /* Main.storyboard */; }; - C69FBA322A940B090062BFD7 /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C69FBA312A940B090062BFD7 /* RxNetworkKit.framework */; }; - C69FBA332A940B090062BFD7 /* RxNetworkKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C69FBA312A940B090062BFD7 /* RxNetworkKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - C639750F2A9513FF00D0AC13 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C69FBA262A9409900062BFD7 /* RxNetworkKit.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 0B77DFD629D964D40077FBC0; - remoteInfo = RxNetworkKit; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ - C69FBA342A940B090062BFD7 /* Embed Frameworks */ = { + C623E7192AD6117300A20A0A /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - C69FBA332A940B090062BFD7 /* RxNetworkKit.framework in Embed Frameworks */, + C63EEB422AD7C2A4003A64CA /* RxNetworkKit.framework in Embed Frameworks */, + C63EEB3F2AD7C29F003A64CA /* CoreExample.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -49,23 +36,15 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - C63975132A95166A00D0AC13 /* ViewController+NetworkRequestInterceptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ViewController+NetworkRequestInterceptor.swift"; sourceTree = ""; }; - C63975142A95166A00D0AC13 /* ViewController+NetworkEventMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ViewController+NetworkEventMonitor.swift"; sourceTree = ""; }; - C63975162A95166A00D0AC13 /* ViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewState.swift; sourceTree = ""; }; - C63975172A95166A00D0AC13 /* ViewLoadType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewLoadType.swift; sourceTree = ""; }; + C623E7182AD6117200A20A0A /* macOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "macOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C63975182A95166A00D0AC13 /* TableCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableCellView.swift; sourceTree = ""; }; - C639751C2A95166A00D0AC13 /* Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; - C639751E2A95166A00D0AC13 /* DownloadRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadRouter.swift; sourceTree = ""; }; - C639751F2A95166A00D0AC13 /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; - C63975212A95166A00D0AC13 /* ViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; - C69FBA142A9408740062BFD7 /* macOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "macOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + C63EEB3D2AD7C29F003A64CA /* CoreExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoreExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C63EEB402AD7C2A4003A64CA /* RxNetworkKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RxNetworkKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C69FBA172A9408740062BFD7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C69FBA192A9408740062BFD7 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; C69FBA1B2A9408770062BFD7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; C69FBA1E2A9408770062BFD7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; C69FBA202A9408770062BFD7 /* macOS_Example.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS_Example.entitlements; sourceTree = ""; }; - C69FBA262A9409900062BFD7 /* RxNetworkKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RxNetworkKit.xcodeproj; path = ../../RxNetworkKit.xcodeproj; sourceTree = ""; }; - C69FBA312A940B090062BFD7 /* RxNetworkKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RxNetworkKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -73,27 +52,20 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C69FBA322A940B090062BFD7 /* RxNetworkKit.framework in Frameworks */, + C68473832AD7CFA90071B1F6 /* RxCocoa in Frameworks */, + C63EEB412AD7C2A4003A64CA /* RxNetworkKit.framework in Frameworks */, + C68473892AD7D7A70071B1F6 /* RxSwift in Frameworks */, + C63EEB3E2AD7C29F003A64CA /* CoreExample.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - C639750C2A9513F900D0AC13 /* Products */ = { - isa = PBXGroup; - children = ( - C63975102A9513FF00D0AC13 /* RxNetworkKit.framework */, - ); - name = Products; - sourceTree = ""; - }; C63975112A95166A00D0AC13 /* Controller */ = { isa = PBXGroup; children = ( C69FBA192A9408740062BFD7 /* ViewController.swift */, - C63975132A95166A00D0AC13 /* ViewController+NetworkRequestInterceptor.swift */, - C63975142A95166A00D0AC13 /* ViewController+NetworkEventMonitor.swift */, ); path = Controller; sourceTree = ""; @@ -103,63 +75,44 @@ children = ( C69FBA1D2A9408770062BFD7 /* Main.storyboard */, C63975182A95166A00D0AC13 /* TableCellView.swift */, - C63975162A95166A00D0AC13 /* ViewState.swift */, - C63975172A95166A00D0AC13 /* ViewLoadType.swift */, ); path = View; sourceTree = ""; }; - C639751B2A95166A00D0AC13 /* Model */ = { - isa = PBXGroup; - children = ( - C639751C2A95166A00D0AC13 /* Model.swift */, - ); - path = Model; - sourceTree = ""; - }; - C639751D2A95166A00D0AC13 /* Network */ = { + C63975222A95166A00D0AC13 /* App */ = { isa = PBXGroup; children = ( - C639751E2A95166A00D0AC13 /* DownloadRouter.swift */, - C639751F2A95166A00D0AC13 /* Router.swift */, + C69FBA172A9408740062BFD7 /* AppDelegate.swift */, + C69FBA1B2A9408770062BFD7 /* Assets.xcassets */, + C69FBA202A9408770062BFD7 /* macOS_Example.entitlements */, ); - path = Network; + path = App; sourceTree = ""; }; - C63975202A95166A00D0AC13 /* View Model */ = { + C63EEAFF2AD7A40E003A64CA /* Frameworks */ = { isa = PBXGroup; children = ( - C63975212A95166A00D0AC13 /* ViewModel.swift */, + C63EEB402AD7C2A4003A64CA /* RxNetworkKit.framework */, + C63EEB3D2AD7C29F003A64CA /* CoreExample.framework */, ); - path = "View Model"; + name = Frameworks; sourceTree = ""; }; - C63975222A95166A00D0AC13 /* App */ = { + C67D5AA02AD60F16002F588A /* Products */ = { isa = PBXGroup; children = ( - C69FBA172A9408740062BFD7 /* AppDelegate.swift */, - C69FBA1B2A9408770062BFD7 /* Assets.xcassets */, - C69FBA202A9408770062BFD7 /* macOS_Example.entitlements */, + C623E7182AD6117200A20A0A /* macOS Example.app */, ); - path = App; + name = Products; sourceTree = ""; }; C69FBA0B2A9408740062BFD7 = { isa = PBXGroup; children = ( - C69FBA262A9409900062BFD7 /* RxNetworkKit.xcodeproj */, C69FBA162A9408740062BFD7 /* macOS Example */, - C69FBA152A9408740062BFD7 /* Products */, - C69FBA302A940B090062BFD7 /* Frameworks */, - ); - sourceTree = ""; - }; - C69FBA152A9408740062BFD7 /* Products */ = { - isa = PBXGroup; - children = ( - C69FBA142A9408740062BFD7 /* macOS Example.app */, + C67D5AA02AD60F16002F588A /* Products */, + C63EEAFF2AD7A40E003A64CA /* Frameworks */, ); - name = Products; sourceTree = ""; }; C69FBA162A9408740062BFD7 /* macOS Example */ = { @@ -167,22 +120,11 @@ children = ( C63975222A95166A00D0AC13 /* App */, C63975112A95166A00D0AC13 /* Controller */, - C639751B2A95166A00D0AC13 /* Model */, - C639751D2A95166A00D0AC13 /* Network */, C63975152A95166A00D0AC13 /* View */, - C63975202A95166A00D0AC13 /* View Model */, ); path = "macOS Example"; sourceTree = ""; }; - C69FBA302A940B090062BFD7 /* Frameworks */ = { - isa = PBXGroup; - children = ( - C69FBA312A940B090062BFD7 /* RxNetworkKit.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -193,7 +135,7 @@ C69FBA102A9408740062BFD7 /* Sources */, C69FBA112A9408740062BFD7 /* Frameworks */, C69FBA122A9408740062BFD7 /* Resources */, - C69FBA342A940B090062BFD7 /* Embed Frameworks */, + C623E7192AD6117300A20A0A /* Embed Frameworks */, ); buildRules = ( ); @@ -201,9 +143,11 @@ ); name = "macOS Example"; packageProductDependencies = ( + C68473822AD7CFA90071B1F6 /* RxCocoa */, + C68473882AD7D7A70071B1F6 /* RxSwift */, ); productName = "macOS Example"; - productReference = C69FBA142A9408740062BFD7 /* macOS Example.app */; + productReference = C623E7182AD6117200A20A0A /* macOS Example.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -222,7 +166,7 @@ }; }; buildConfigurationList = C69FBA0F2A9408740062BFD7 /* Build configuration list for PBXProject "macOS Example" */; - compatibilityVersion = "Xcode 14.0"; + compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -231,15 +175,10 @@ ); mainGroup = C69FBA0B2A9408740062BFD7; packageReferences = ( + C63EEB0D2AD7A43D003A64CA /* XCRemoteSwiftPackageReference "RxSwift" */, ); - productRefGroup = C69FBA152A9408740062BFD7 /* Products */; + productRefGroup = C69FBA0B2A9408740062BFD7; projectDirPath = ""; - projectReferences = ( - { - ProductGroup = C639750C2A9513F900D0AC13 /* Products */; - ProjectRef = C69FBA262A9409900062BFD7 /* RxNetworkKit.xcodeproj */; - }, - ); projectRoot = ""; targets = ( C69FBA132A9408740062BFD7 /* macOS Example */, @@ -247,16 +186,6 @@ }; /* End PBXProject section */ -/* Begin PBXReferenceProxy section */ - C63975102A9513FF00D0AC13 /* RxNetworkKit.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = RxNetworkKit.framework; - remoteRef = C639750F2A9513FF00D0AC13 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - /* Begin PBXResourcesBuildPhase section */ C69FBA122A9408740062BFD7 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -274,16 +203,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C63975322A95166A00D0AC13 /* Router.swift in Sources */, - C63975302A95166A00D0AC13 /* Model.swift in Sources */, - C639752B2A95166A00D0AC13 /* ViewController+NetworkEventMonitor.swift in Sources */, - C63975312A95166A00D0AC13 /* DownloadRouter.swift in Sources */, C639752E2A95166A00D0AC13 /* TableCellView.swift in Sources */, - C639752C2A95166A00D0AC13 /* ViewState.swift in Sources */, - C639752A2A95166A00D0AC13 /* ViewController+NetworkRequestInterceptor.swift in Sources */, - C639752D2A95166A00D0AC13 /* ViewLoadType.swift in Sources */, C69FBA1A2A9408740062BFD7 /* ViewController.swift in Sources */, - C63975332A95166A00D0AC13 /* ViewModel.swift in Sources */, C69FBA182A9408740062BFD7 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -423,6 +344,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "macOS Example/App/macOS_Example.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; @@ -440,8 +362,10 @@ ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 0.0.2; + OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitMacOSExample; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; }; @@ -453,6 +377,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "macOS Example/App/macOS_Example.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; @@ -470,8 +395,10 @@ ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 0.0.2; + OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitMacOSExample; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; }; @@ -499,6 +426,30 @@ defaultConfigurationName = Debug; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + C63EEB0D2AD7A43D003A64CA /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + C68473822AD7CFA90071B1F6 /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = C63EEB0D2AD7A43D003A64CA /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; + C68473882AD7D7A70071B1F6 /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = C63EEB0D2AD7A43D003A64CA /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = C69FBA0C2A9408740062BFD7 /* Project object */; } diff --git a/Examples/macOS/macOS Example.xcodeproj/xcshareddata/xcschemes/macOS Example.xcscheme b/Examples/macOS/macOS Example.xcodeproj/xcshareddata/xcschemes/macOS Example.xcscheme new file mode 100644 index 0000000..4130709 --- /dev/null +++ b/Examples/macOS/macOS Example.xcodeproj/xcshareddata/xcschemes/macOS Example.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/macOS/macOS Example/Controller/ViewController+NetworkEventMonitor.swift b/Examples/macOS/macOS Example/Controller/ViewController+NetworkEventMonitor.swift deleted file mode 100644 index 50b5451..0000000 --- a/Examples/macOS/macOS Example/Controller/ViewController+NetworkEventMonitor.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// ViewController+NetworkEventMonitor.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 01/04/2023. -// - -import Foundation -import RxNetworkKit - -extension ViewController: NetworkEventMonitor { - func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { - debugPrint("") - } - func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { - debugPrint(session) - } - func urlSession(_ session: URLSession, didCreateTask task: URLSessionTask) { - debugPrint("Task created!") - } - func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - guard let error else { return } - debugPrint("Task did finish with error: \(error.localizedDescription)!") - } - func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { } -} diff --git a/Examples/macOS/macOS Example/Controller/ViewController+NetworkRequestInterceptor.swift b/Examples/macOS/macOS Example/Controller/ViewController+NetworkRequestInterceptor.swift deleted file mode 100644 index e625612..0000000 --- a/Examples/macOS/macOS Example/Controller/ViewController+NetworkRequestInterceptor.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ViewController+NetworkRequestInterceptor.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 01/04/2023. -// - -import Foundation -import RxNetworkKit - -extension ViewController: NetworkRequestInterceptor { - func adapt(_ request: URLRequest, for session: URLSession) -> URLRequest { - return request - } - func retryMaxAttempts(_ request: URLRequest, for session: URLSession) -> Int { - 5 - } - func retryPolicy(_ request: URLRequest, for session: URLSession) -> NetworkRequestRetryPolicy { - .constant(time: 5_000) - } - func shouldRetry(_ request: URLRequest, for session: URLSession, dueTo error: NetworkError) -> Bool { - true - } -} diff --git a/Examples/macOS/macOS Example/Controller/ViewController.swift b/Examples/macOS/macOS Example/Controller/ViewController.swift index bf53473..6b03e6e 100644 --- a/Examples/macOS/macOS Example/Controller/ViewController.swift +++ b/Examples/macOS/macOS Example/Controller/ViewController.swift @@ -7,8 +7,9 @@ import Cocoa import RxSwift -import RxCocoa +import RxRelay import RxNetworkKit +import CoreExample class ViewController: NSViewController { @IBOutlet weak var tableView: NSTableView! @@ -43,8 +44,12 @@ class ViewController: NSViewController { } /// Initializes ViewModel object. private func setupViewModel() { - let manager = NetworkManager(configuration: .default, requestInterceptor: self, eventMonitor: self) - viewModel = .init(networkManager: manager) + let requestInterceptor = RequestInterceptor() + let requestEventMointor = RequestEventMonitor() + let session = Session(configuration: .default, eventMonitor: requestEventMointor) + let restClient = RESTClient(session: session, requestInterceptor: requestInterceptor) + let httpClient = HTTPClient(session: session, requestInterceptor: requestInterceptor) + viewModel = .init(restClient: restClient, httpClient: httpClient) } /// Sets up views. private func setupUI() { @@ -119,7 +124,7 @@ class ViewController: NSViewController { /// - url: `URL` used to download image data. /// - cell: `TableViewCell` that the image data will be applied to. private func downloadTableViewCellImage(using url: URL, applyTo cell: TableCellView) { - viewModel.downloadImage(DownloadRouter.default(url: url)) + viewModel.downloadImage(DownloadRequestRouter.default(url: url)) .observe(on: MainScheduler.instance) .subscribe(onNext: { switch $0 { diff --git a/Examples/macOS/macOS Example/Network/DownloadRouter.swift b/Examples/macOS/macOS Example/Network/DownloadRouter.swift deleted file mode 100644 index cffa683..0000000 --- a/Examples/macOS/macOS Example/Network/DownloadRouter.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// DownloadRouter.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 01/04/2023. -// - -import Foundation -import RxNetworkKit - -enum DownloadRouter: NetworkDownloadRouter { - case `default`(url: URL) - var scheme: HTTPScheme { - .https - } - var domain: String { - "" - } - var path: String { - "" - } - var headers: [String : String] { - [:] - } - var parameters: [String : String]? { - nil - } - var url: URL? { - switch self { - case .default(let url): - return url - } - } -} diff --git a/Examples/macOS/macOS Example/Network/Router.swift b/Examples/macOS/macOS Example/Network/Router.swift deleted file mode 100644 index 564a378..0000000 --- a/Examples/macOS/macOS Example/Network/Router.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// Router.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 01/04/2023. -// - -import Foundation -import RxNetworkKit - -enum Router: NetworkRouter { - case `default` - var scheme: HTTPScheme { - .https - } - var method: HTTPMethod { - .get - } - var domain: String { - "api.github.com" - } - var path: String { - "users" - } - var headers: [String : String] { - ["Accept": "application/vnd.github+json"] - } - var parameters: [String : String]? { - nil - } - var body: [String : Any]? { - nil - } - var url: URL? { - let urlString = scheme.rawValue + domain + "/" + path - return URL(string: urlString) - } -} diff --git a/Examples/macOS/macOS Example/View Model/ViewModel.swift b/Examples/macOS/macOS Example/View Model/ViewModel.swift deleted file mode 100644 index aa5517e..0000000 --- a/Examples/macOS/macOS Example/View Model/ViewModel.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// ViewModel.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 31/03/2023. -// - -import Foundation -import RxSwift -import RxCocoa -import RxNetworkKit - -class ViewModel { - // MARK: Input - // Input sequence is the same as view state (only for loading states) - private(set) var viewState: PublishRelay = .init() - // MARK: Output - private(set) var users: Driver<[Model]>! - private(set) var error: Driver! - // MARK: Properties and Dependencies - private let networkManager: NetworkManager - private let disposeBag = DisposeBag() - /// Creates `ViewModel` instance - /// - /// - Parameter networkManager: `NetworkManager` object used for making network API calls. - init(networkManager: NetworkManager) { - self.networkManager = networkManager - bindOutput() - } - /// Creates observable sequence that results in image data. - /// - /// - Parameter router: `DownloadRouter` used to download image data. - /// - /// - Returns: observable sequence that results in image data. - func downloadImage(_ router: DownloadRouter) -> Observable { - networkManager.download(router) - } - /// Binds output sequence to input sequence. - private func bindOutput() { - // Create default sequence with default API call request. - let loadObservable = viewState - .filter( { ![.idle, .loading(loadType: .paginate), .error].contains($0) }) - .flatMapLatest{ [weak self] _ in - guard let self = self else { return Observable<[Model]>.empty().materialize() } - let single: Single<[Model]> = self.networkManager.request(Router.default) - return single - .asObservable() - .materialize() - } - .share() - // Initialize users and error sequence outputs - self.users = loadObservable - .compactMap { $0.element } - .asDriver(onErrorJustReturn: []) - self.error = loadObservable - .compactMap { $0.error as? NetworkError } - .asDriver(onErrorJustReturn: NetworkError.client(.transport(NSError(domain: "", code: 1, userInfo: nil)))) - // Bind output sequence to input sequence (view state) - self.users - .asObservable() - .map({ _ in .idle }) - .bind(to: viewState) - .disposed(by: disposeBag) - self.error - .asObservable() - .map({ _ in .error }) - .bind(to: viewState) - .disposed(by: disposeBag) - } -} diff --git a/Examples/macOS/macOS Example/View/ViewLoadType.swift b/Examples/macOS/macOS Example/View/ViewLoadType.swift deleted file mode 100644 index 3aab992..0000000 --- a/Examples/macOS/macOS Example/View/ViewLoadType.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// ViewLoadType.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 01/04/2023. -// - -enum ViewLoadType { - case initial - case refresh - case paginate -} diff --git a/Examples/macOS/macOS Example/View/ViewState.swift b/Examples/macOS/macOS Example/View/ViewState.swift deleted file mode 100644 index e5cd81d..0000000 --- a/Examples/macOS/macOS Example/View/ViewState.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// ViewState.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 01/04/2023. -// - -enum ViewState: Equatable { - case idle - case loading(loadType: ViewLoadType) - case error -} diff --git a/Package.resolved b/Package.resolved index 3e0fda4..96e6e94 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,22 @@ { "object": { "pins": [ + { + "package": "CoreHTTP", + "repositoryURL": "https://github.com/loay-ashraf/CoreHTTP", + "state": { + "branch": null, + "revision": "17b030a4a10ac9449e6c107bce13f112280aae6b", + "version": "1.0.0" + } + }, { "package": "RxSwift", "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", "state": { "branch": null, - "revision": "b4307ba0b6425c0ba4178e138799946c3da594f8", - "version": "6.5.0" + "revision": "9dcaa4b333db437b0fbfaf453fad29069044a8b4", + "version": "6.6.0" } }, { @@ -15,8 +24,8 @@ "repositoryURL": "https://github.com/RxSwiftCommunity/RxSwiftExt", "state": { "branch": null, - "revision": "827bd11853983383b708feaf7da95c560982b2b8", - "version": "6.0.1" + "revision": "eb4adf9f00a21b3efc3869a5218a6d7517e95222", + "version": "6.2.1" } } ] diff --git a/Package.swift b/Package.swift index 2f3913c..61d849c 100644 --- a/Package.swift +++ b/Package.swift @@ -19,9 +19,10 @@ let package = Package( dependencies: [ .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "6.6.0")), .package(url: "https://github.com/RxSwiftCommunity/RxSwiftExt", .upToNextMajor(from: "6.2.0")), + .package(url: "https://github.com/loay-ashraf/CoreHTTP", .upToNextMajor(from: "1.0.0")) ], targets: [ - .target(name: "RxNetworkKit", dependencies: ["RxSwift", "RxSwiftExt", .product(name: "RxCocoa", package: "RxSwift")], path: "Source"), + .target(name: "RxNetworkKit", dependencies: ["RxSwift", "RxSwiftExt", .product(name: "RxCocoa", package: "RxSwift"), "CoreHTTP"], path: "Source"), ], swiftLanguageVersions: [.v5] ) diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index 936b8d2..2324c13 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -8,38 +8,23 @@ /* Begin PBXBuildFile section */ 0B77E08929D965D30077FBC0 /* Reactive+URLSessionDownloadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E04D29D965D30077FBC0 /* Reactive+URLSessionDownloadResponse.swift */; }; - 0B77E08A29D965D30077FBC0 /* DownloadEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E04E29D965D30077FBC0 /* DownloadEvent.swift */; }; + 0B77E08A29D965D30077FBC0 /* HTTPDownloadRequestEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E04E29D965D30077FBC0 /* HTTPDownloadRequestEvent.swift */; }; 0B77E08B29D965D30077FBC0 /* URLSession+DownloadTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E04F29D965D30077FBC0 /* URLSession+DownloadTask.swift */; }; 0B77E08C29D965D30077FBC0 /* Reactive+URLSessionAdaptedDownloadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05029D965D30077FBC0 /* Reactive+URLSessionAdaptedDownloadResponse.swift */; }; - 0B77E08D29D965D30077FBC0 /* UploadFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05229D965D30077FBC0 /* UploadFormData.swift */; }; - 0B77E08E29D965D30077FBC0 /* UploadFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05329D965D30077FBC0 /* UploadFile.swift */; }; + 0B77E08D29D965D30077FBC0 /* HTTPUploadRequestFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05229D965D30077FBC0 /* HTTPUploadRequestFormData.swift */; }; + 0B77E08E29D965D30077FBC0 /* HTTPUploadRequestFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05329D965D30077FBC0 /* HTTPUploadRequestFile.swift */; }; 0B77E08F29D965D30077FBC0 /* URLSession+UploadTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05429D965D30077FBC0 /* URLSession+UploadTask.swift */; }; 0B77E09029D965D30077FBC0 /* Data+AppendString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05529D965D30077FBC0 /* Data+AppendString.swift */; }; 0B77E09129D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05629D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift */; }; - 0B77E09229D965D30077FBC0 /* UploadEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05729D965D30077FBC0 /* UploadEvent.swift */; }; + 0B77E09229D965D30077FBC0 /* HTTPUploadRequestEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05729D965D30077FBC0 /* HTTPUploadRequestEvent.swift */; }; 0B77E09329D965D30077FBC0 /* Reactive+URLSessionUploadResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05829D965D30077FBC0 /* Reactive+URLSessionUploadResponse.swift */; }; - 0B77E09429D965D30077FBC0 /* RequestInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05A29D965D30077FBC0 /* RequestInterceptor.swift */; }; - 0B77E09529D965D30077FBC0 /* RequestRetryPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05B29D965D30077FBC0 /* RequestRetryPolicy.swift */; }; - 0B77E09629D965D30077FBC0 /* RequestAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05C29D965D30077FBC0 /* RequestAdapter.swift */; }; - 0B77E09729D965D30077FBC0 /* RequestRetrier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E05D29D965D30077FBC0 /* RequestRetrier.swift */; }; - 0B77E09929D965D30077FBC0 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06029D965D30077FBC0 /* NetworkManager.swift */; }; 0B77E09A29D965D30077FBC0 /* HTTPMIMEType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06229D965D30077FBC0 /* HTTPMIMEType.swift */; }; - 0B77E09B29D965D30077FBC0 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06329D965D30077FBC0 /* HTTPMethod.swift */; }; - 0B77E09C29D965D30077FBC0 /* HTTPScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06429D965D30077FBC0 /* HTTPScheme.swift */; }; - 0B77E09D29D965D30077FBC0 /* HTTPURLResponse+StatusCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06529D965D30077FBC0 /* HTTPURLResponse+StatusCode.swift */; }; - 0B77E09E29D965D30077FBC0 /* HTTPStatusCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06629D965D30077FBC0 /* HTTPStatusCode.swift */; }; 0B77E09F29D965D30077FBC0 /* NWPath+InterfaceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06829D965D30077FBC0 /* NWPath+InterfaceType.swift */; }; 0B77E0A029D965D30077FBC0 /* NetworkReachabilityStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06929D965D30077FBC0 /* NetworkReachabilityStatus.swift */; }; 0B77E0A129D965D30077FBC0 /* NWInterfaceType+RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06A29D965D30077FBC0 /* NWInterfaceType+RawRepresentable.swift */; }; 0B77E0A229D965D30077FBC0 /* NWInterfaceType+CaseIterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06B29D965D30077FBC0 /* NWInterfaceType+CaseIterable.swift */; }; 0B77E0A329D965D30077FBC0 /* NetworkInterfaceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06C29D965D30077FBC0 /* NetworkInterfaceType.swift */; }; 0B77E0A429D965D30077FBC0 /* NetworkReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06D29D965D30077FBC0 /* NetworkReachability.swift */; }; - 0B77E0A529D965D30077FBC0 /* NetworkEventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E06F29D965D30077FBC0 /* NetworkEventMonitor.swift */; }; - 0B77E0A629D965D30077FBC0 /* DefaultNetworkAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E07129D965D30077FBC0 /* DefaultNetworkAPIError.swift */; }; - 0B77E0A729D965D30077FBC0 /* NetworkServerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E07229D965D30077FBC0 /* NetworkServerError.swift */; }; - 0B77E0A829D965D30077FBC0 /* NetworkClientError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E07329D965D30077FBC0 /* NetworkClientError.swift */; }; - 0B77E0A929D965D30077FBC0 /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E07429D965D30077FBC0 /* NetworkError.swift */; }; - 0B77E0AA29D965D30077FBC0 /* NetworkAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E07529D965D30077FBC0 /* NetworkAPIError.swift */; }; 0B77E0AB29D965D30077FBC0 /* Single+Retry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E07929D965D30077FBC0 /* Single+Retry.swift */; }; 0B77E0AC29D965D30077FBC0 /* Single+Decode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E07A29D965D30077FBC0 /* Single+Decode.swift */; }; 0B77E0AD29D965D30077FBC0 /* Single+CatchErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E07B29D965D30077FBC0 /* Single+CatchErrors.swift */; }; @@ -49,24 +34,26 @@ 0B77E0B129D965D30077FBC0 /* Observable+Decodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E08029D965D30077FBC0 /* Observable+Decodable.swift */; }; 0B77E0B229D965D30077FBC0 /* Completable+Retry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E08229D965D30077FBC0 /* Completable+Retry.swift */; }; 0B77E0B329D965D30077FBC0 /* Reactive+Curl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E08429D965D30077FBC0 /* Reactive+Curl.swift */; }; - 0B77E0B429D965D30077FBC0 /* NetworkDownloadRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E08629D965D30077FBC0 /* NetworkDownloadRouter.swift */; }; - 0B77E0B529D965D30077FBC0 /* NetworkRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E08729D965D30077FBC0 /* NetworkRouter.swift */; }; - 0B77E0B629D965D30077FBC0 /* NetworkUploadRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E08829D965D30077FBC0 /* NetworkUploadRouter.swift */; }; + 0B77E0B429D965D30077FBC0 /* HTTPDownloadRequestRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E08629D965D30077FBC0 /* HTTPDownloadRequestRouter.swift */; }; + 0B77E0B629D965D30077FBC0 /* HTTPUploadRequestRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B77E08829D965D30077FBC0 /* HTTPUploadRequestRouter.swift */; }; 0B77E0B929D968DE0077FBC0 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0B829D968DE0077FBC0 /* RxCocoa */; }; 0B77E0BB29D968DE0077FBC0 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BA29D968DE0077FBC0 /* RxRelay */; }; 0B77E0BD29D968DE0077FBC0 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BC29D968DE0077FBC0 /* RxSwift */; }; 0B77E0C029D969370077FBC0 /* RxSwiftExt in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BF29D969370077FBC0 /* RxSwiftExt */; }; C6049B162A95307800E5727E /* RxNetworkKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C6049B152A95307800E5727E /* RxNetworkKit.h */; }; + C61CB5642AF2CFDD006A203A /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = C61CB5632AF2CFDD006A203A /* Session.swift */; }; + C623E7B42AD6262A00A20A0A /* CoreHTTP in Frameworks */ = {isa = PBXBuildFile; productRef = C623E7B32AD6262A00A20A0A /* CoreHTTP */; }; + C6554A2B2AD5BBB60090DD3A /* RESTClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6554A2A2AD5BBB60090DD3A /* RESTClient.swift */; }; + C6554A2D2AD5C1560090DD3A /* HTTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6554A2C2AD5C1560090DD3A /* HTTPClient.swift */; }; + C658CF652AD5F648009E561D /* RxNetworkKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = C658CF642AD5F648009E561D /* RxNetworkKit.swift */; }; C69A78562ACF001400ECF092 /* Docs.docc in Sources */ = {isa = PBXBuildFile; fileRef = C69A78552ACEFF3200ECF092 /* Docs.docc */; }; - C6A9BEF62A93E2AE00459E32 /* DefaultHTTPErrorBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */; }; - C6A9BEF82A93E2D600459E32 /* HTTPErrorBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */; }; C6A9BEFA2A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */; }; C6A9BEFD2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */; }; C6A9BEFF2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */; }; C6B4B4C42AD47A2F009073ED /* WebSocketError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6B4B4C32AD47A2F009073ED /* WebSocketError.swift */; }; - C6BDFFE82ACDF3830022F675 /* Reactive+receive.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFE72ACDF3830022F675 /* Reactive+receive.swift */; }; - C6BDFFEA2ACDF3D90022F675 /* Reactive+send.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFE92ACDF3D90022F675 /* Reactive+send.swift */; }; - C6BDFFEC2ACDF4100022F675 /* Reactive+ping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFEB2ACDF4100022F675 /* Reactive+ping.swift */; }; + C6BDFFE82ACDF3830022F675 /* Reactive+WebSocketReceive.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFE72ACDF3830022F675 /* Reactive+WebSocketReceive.swift */; }; + C6BDFFEA2ACDF3D90022F675 /* Reactive+WebSocketSend.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFE92ACDF3D90022F675 /* Reactive+WebSocketSend.swift */; }; + C6BDFFEC2ACDF4100022F675 /* Reactive+WebSocketPing.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFEB2ACDF4100022F675 /* Reactive+WebSocketPing.swift */; }; C6BDFFEE2ACDF46A0022F675 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFED2ACDF46A0022F675 /* WebSocket.swift */; }; C6BDFFF02ACDF4AB0022F675 /* WebSocketCloseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFEF2ACDF4AB0022F675 /* WebSocketCloseHandler.swift */; }; C6BDFFF32ACDF5100022F675 /* WebSocketMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6BDFFF22ACDF50F0022F675 /* WebSocketMessage.swift */; }; @@ -77,38 +64,23 @@ 0B6BD87329DC0A9400502F47 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 0B77DFD629D964D40077FBC0 /* RxNetworkKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RxNetworkKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0B77E04D29D965D30077FBC0 /* Reactive+URLSessionDownloadResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reactive+URLSessionDownloadResponse.swift"; sourceTree = ""; }; - 0B77E04E29D965D30077FBC0 /* DownloadEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadEvent.swift; sourceTree = ""; }; + 0B77E04E29D965D30077FBC0 /* HTTPDownloadRequestEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPDownloadRequestEvent.swift; sourceTree = ""; }; 0B77E04F29D965D30077FBC0 /* URLSession+DownloadTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URLSession+DownloadTask.swift"; sourceTree = ""; }; 0B77E05029D965D30077FBC0 /* Reactive+URLSessionAdaptedDownloadResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reactive+URLSessionAdaptedDownloadResponse.swift"; sourceTree = ""; }; - 0B77E05229D965D30077FBC0 /* UploadFormData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UploadFormData.swift; sourceTree = ""; }; - 0B77E05329D965D30077FBC0 /* UploadFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UploadFile.swift; sourceTree = ""; }; + 0B77E05229D965D30077FBC0 /* HTTPUploadRequestFormData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPUploadRequestFormData.swift; sourceTree = ""; }; + 0B77E05329D965D30077FBC0 /* HTTPUploadRequestFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPUploadRequestFile.swift; sourceTree = ""; }; 0B77E05429D965D30077FBC0 /* URLSession+UploadTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URLSession+UploadTask.swift"; sourceTree = ""; }; 0B77E05529D965D30077FBC0 /* Data+AppendString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+AppendString.swift"; sourceTree = ""; }; 0B77E05629D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reactive+URLSessionAdaptedUploadResponse.swift"; sourceTree = ""; }; - 0B77E05729D965D30077FBC0 /* UploadEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UploadEvent.swift; sourceTree = ""; }; + 0B77E05729D965D30077FBC0 /* HTTPUploadRequestEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPUploadRequestEvent.swift; sourceTree = ""; }; 0B77E05829D965D30077FBC0 /* Reactive+URLSessionUploadResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reactive+URLSessionUploadResponse.swift"; sourceTree = ""; }; - 0B77E05A29D965D30077FBC0 /* RequestInterceptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestInterceptor.swift; sourceTree = ""; }; - 0B77E05B29D965D30077FBC0 /* RequestRetryPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestRetryPolicy.swift; sourceTree = ""; }; - 0B77E05C29D965D30077FBC0 /* RequestAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestAdapter.swift; sourceTree = ""; }; - 0B77E05D29D965D30077FBC0 /* RequestRetrier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestRetrier.swift; sourceTree = ""; }; - 0B77E06029D965D30077FBC0 /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; 0B77E06229D965D30077FBC0 /* HTTPMIMEType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPMIMEType.swift; sourceTree = ""; }; - 0B77E06329D965D30077FBC0 /* HTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; - 0B77E06429D965D30077FBC0 /* HTTPScheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPScheme.swift; sourceTree = ""; }; - 0B77E06529D965D30077FBC0 /* HTTPURLResponse+StatusCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HTTPURLResponse+StatusCode.swift"; sourceTree = ""; }; - 0B77E06629D965D30077FBC0 /* HTTPStatusCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPStatusCode.swift; sourceTree = ""; }; 0B77E06829D965D30077FBC0 /* NWPath+InterfaceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NWPath+InterfaceType.swift"; sourceTree = ""; }; 0B77E06929D965D30077FBC0 /* NetworkReachabilityStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkReachabilityStatus.swift; sourceTree = ""; }; 0B77E06A29D965D30077FBC0 /* NWInterfaceType+RawRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NWInterfaceType+RawRepresentable.swift"; sourceTree = ""; }; 0B77E06B29D965D30077FBC0 /* NWInterfaceType+CaseIterable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NWInterfaceType+CaseIterable.swift"; sourceTree = ""; }; 0B77E06C29D965D30077FBC0 /* NetworkInterfaceType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkInterfaceType.swift; sourceTree = ""; }; 0B77E06D29D965D30077FBC0 /* NetworkReachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkReachability.swift; sourceTree = ""; }; - 0B77E06F29D965D30077FBC0 /* NetworkEventMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkEventMonitor.swift; sourceTree = ""; }; - 0B77E07129D965D30077FBC0 /* DefaultNetworkAPIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultNetworkAPIError.swift; sourceTree = ""; }; - 0B77E07229D965D30077FBC0 /* NetworkServerError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkServerError.swift; sourceTree = ""; }; - 0B77E07329D965D30077FBC0 /* NetworkClientError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkClientError.swift; sourceTree = ""; }; - 0B77E07429D965D30077FBC0 /* NetworkError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; }; - 0B77E07529D965D30077FBC0 /* NetworkAPIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkAPIError.swift; sourceTree = ""; }; 0B77E07929D965D30077FBC0 /* Single+Retry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Single+Retry.swift"; sourceTree = ""; }; 0B77E07A29D965D30077FBC0 /* Single+Decode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Single+Decode.swift"; sourceTree = ""; }; 0B77E07B29D965D30077FBC0 /* Single+CatchErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Single+CatchErrors.swift"; sourceTree = ""; }; @@ -118,20 +90,21 @@ 0B77E08029D965D30077FBC0 /* Observable+Decodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+Decodable.swift"; sourceTree = ""; }; 0B77E08229D965D30077FBC0 /* Completable+Retry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Completable+Retry.swift"; sourceTree = ""; }; 0B77E08429D965D30077FBC0 /* Reactive+Curl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reactive+Curl.swift"; sourceTree = ""; }; - 0B77E08629D965D30077FBC0 /* NetworkDownloadRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkDownloadRouter.swift; sourceTree = ""; }; - 0B77E08729D965D30077FBC0 /* NetworkRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkRouter.swift; sourceTree = ""; }; - 0B77E08829D965D30077FBC0 /* NetworkUploadRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkUploadRouter.swift; sourceTree = ""; }; + 0B77E08629D965D30077FBC0 /* HTTPDownloadRequestRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPDownloadRequestRouter.swift; sourceTree = ""; }; + 0B77E08829D965D30077FBC0 /* HTTPUploadRequestRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPUploadRequestRouter.swift; sourceTree = ""; }; C6049B152A95307800E5727E /* RxNetworkKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxNetworkKit.h; sourceTree = ""; }; + C61CB5632AF2CFDD006A203A /* Session.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = ""; }; + C6554A2A2AD5BBB60090DD3A /* RESTClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTClient.swift; sourceTree = ""; }; + C6554A2C2AD5C1560090DD3A /* HTTPClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPClient.swift; sourceTree = ""; }; + C658CF642AD5F648009E561D /* RxNetworkKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxNetworkKit.swift; sourceTree = ""; }; C69A78552ACEFF3200ECF092 /* Docs.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Docs.docc; sourceTree = ""; }; - C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultHTTPErrorBody.swift; sourceTree = ""; }; - C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPErrorBody.swift; sourceTree = ""; }; C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+setAdditionalHTTPHeader.swift"; sourceTree = ""; }; C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionConfiguration+setUserAgentHTTPHeader.swift"; sourceTree = ""; }; C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+operatingSystemName.swift"; sourceTree = ""; }; C6B4B4C32AD47A2F009073ED /* WebSocketError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocketError.swift; sourceTree = ""; }; - C6BDFFE72ACDF3830022F675 /* Reactive+receive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+receive.swift"; sourceTree = ""; }; - C6BDFFE92ACDF3D90022F675 /* Reactive+send.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+send.swift"; sourceTree = ""; }; - C6BDFFEB2ACDF4100022F675 /* Reactive+ping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+ping.swift"; sourceTree = ""; }; + C6BDFFE72ACDF3830022F675 /* Reactive+WebSocketReceive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+WebSocketReceive.swift"; sourceTree = ""; }; + C6BDFFE92ACDF3D90022F675 /* Reactive+WebSocketSend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+WebSocketSend.swift"; sourceTree = ""; }; + C6BDFFEB2ACDF4100022F675 /* Reactive+WebSocketPing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+WebSocketPing.swift"; sourceTree = ""; }; C6BDFFED2ACDF46A0022F675 /* WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = ""; }; C6BDFFEF2ACDF4AB0022F675 /* WebSocketCloseHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocketCloseHandler.swift; sourceTree = ""; }; C6BDFFF22ACDF50F0022F675 /* WebSocketMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocketMessage.swift; sourceTree = ""; }; @@ -145,6 +118,7 @@ files = ( 0B77E0BD29D968DE0077FBC0 /* RxSwift in Frameworks */, 0B77E0BB29D968DE0077FBC0 /* RxRelay in Frameworks */, + C623E7B42AD6262A00A20A0A /* CoreHTTP in Frameworks */, 0B77E0C029D969370077FBC0 /* RxSwiftExt in Frameworks */, 0B77E0B929D968DE0077FBC0 /* RxCocoa in Frameworks */, ); @@ -156,8 +130,8 @@ 0B77DFCC29D964D40077FBC0 = { isa = PBXGroup; children = ( - C69A78552ACEFF3200ECF092 /* Docs.docc */, 0B6BD87329DC0A9400502F47 /* Package.swift */, + C69A78552ACEFF3200ECF092 /* Docs.docc */, 0B77E04A29D965D30077FBC0 /* Source */, 0B77DFD729D964D40077FBC0 /* Products */, ); @@ -174,213 +148,175 @@ 0B77E04A29D965D30077FBC0 /* Source */ = { isa = PBXGroup; children = ( - C6BDFFE62ACDF3260022F675 /* Web Socket */, - 0B77E04B29D965D30077FBC0 /* Custom Requests */, - 0B77E05929D965D30077FBC0 /* Request Interceptor */, - 0B77E05F29D965D30077FBC0 /* Manager */, + C61CB5622AF2CF31006A203A /* Session */, + C6554A292AD5B5600090DD3A /* REST */, 0B77E06129D965D30077FBC0 /* HTTP */, 0B77E06729D965D30077FBC0 /* Reachability */, - 0B77E06E29D965D30077FBC0 /* Event Monitor */, - 0B77E07029D965D30077FBC0 /* Error */, - 0B77E07629D965D30077FBC0 /* Rx Extensions */, - 0B77E08529D965D30077FBC0 /* Router */, C6049B152A95307800E5727E /* RxNetworkKit.h */, + C658CF642AD5F648009E561D /* RxNetworkKit.swift */, ); path = Source; sourceTree = ""; }; - 0B77E04B29D965D30077FBC0 /* Custom Requests */ = { - isa = PBXGroup; - children = ( - 0B77E04C29D965D30077FBC0 /* Download */, - 0B77E05129D965D30077FBC0 /* Upload */, - ); - path = "Custom Requests"; - sourceTree = ""; - }; - 0B77E04C29D965D30077FBC0 /* Download */ = { + 0B77E06129D965D30077FBC0 /* HTTP */ = { isa = PBXGroup; children = ( - 0B77E04E29D965D30077FBC0 /* DownloadEvent.swift */, - 0B77E04F29D965D30077FBC0 /* URLSession+DownloadTask.swift */, - 0B77E04D29D965D30077FBC0 /* Reactive+URLSessionDownloadResponse.swift */, - 0B77E05029D965D30077FBC0 /* Reactive+URLSessionAdaptedDownloadResponse.swift */, + C6554A362AD5C6A60090DD3A /* Extensions */, + C6554A382AD5C7DF0090DD3A /* Types */, ); - path = Download; + path = HTTP; sourceTree = ""; }; - 0B77E05129D965D30077FBC0 /* Upload */ = { + 0B77E06729D965D30077FBC0 /* Reachability */ = { isa = PBXGroup; children = ( - 0B77E05229D965D30077FBC0 /* UploadFormData.swift */, - 0B77E05329D965D30077FBC0 /* UploadFile.swift */, - 0B77E05429D965D30077FBC0 /* URLSession+UploadTask.swift */, - 0B77E05529D965D30077FBC0 /* Data+AppendString.swift */, - 0B77E05729D965D30077FBC0 /* UploadEvent.swift */, - 0B77E05829D965D30077FBC0 /* Reactive+URLSessionUploadResponse.swift */, - 0B77E05629D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift */, + 0B77E06C29D965D30077FBC0 /* NetworkInterfaceType.swift */, + 0B77E06D29D965D30077FBC0 /* NetworkReachability.swift */, + 0B77E06929D965D30077FBC0 /* NetworkReachabilityStatus.swift */, + 0B77E06B29D965D30077FBC0 /* NWInterfaceType+CaseIterable.swift */, + 0B77E06A29D965D30077FBC0 /* NWInterfaceType+RawRepresentable.swift */, + 0B77E06829D965D30077FBC0 /* NWPath+InterfaceType.swift */, ); - path = Upload; + path = Reachability; sourceTree = ""; }; - 0B77E05929D965D30077FBC0 /* Request Interceptor */ = { + C61CB5622AF2CF31006A203A /* Session */ = { isa = PBXGroup; children = ( - 0B77E05A29D965D30077FBC0 /* RequestInterceptor.swift */, - 0B77E05B29D965D30077FBC0 /* RequestRetryPolicy.swift */, - 0B77E05C29D965D30077FBC0 /* RequestAdapter.swift */, - 0B77E05D29D965D30077FBC0 /* RequestRetrier.swift */, + C61CB5632AF2CFDD006A203A /* Session.swift */, ); - path = "Request Interceptor"; + path = Session; sourceTree = ""; }; - 0B77E05F29D965D30077FBC0 /* Manager */ = { + C6554A292AD5B5600090DD3A /* REST */ = { isa = PBXGroup; children = ( - 0B77E06029D965D30077FBC0 /* NetworkManager.swift */, - C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */, - C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */, - C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */, + C6554A372AD5C7AD0090DD3A /* Extensions */, + C6554A392AD5C7FB0090DD3A /* Types */, ); - path = Manager; + path = REST; sourceTree = ""; }; - 0B77E06129D965D30077FBC0 /* HTTP */ = { + C6554A2E2AD5C4F30090DD3A /* Parameters */ = { isa = PBXGroup; children = ( - C6A9BEF52A93E2AE00459E32 /* DefaultHTTPErrorBody.swift */, - C6A9BEF72A93E2D600459E32 /* HTTPErrorBody.swift */, + 0B77E05329D965D30077FBC0 /* HTTPUploadRequestFile.swift */, + 0B77E05229D965D30077FBC0 /* HTTPUploadRequestFormData.swift */, 0B77E06229D965D30077FBC0 /* HTTPMIMEType.swift */, - 0B77E06329D965D30077FBC0 /* HTTPMethod.swift */, - 0B77E06429D965D30077FBC0 /* HTTPScheme.swift */, - 0B77E06629D965D30077FBC0 /* HTTPStatusCode.swift */, - 0B77E06529D965D30077FBC0 /* HTTPURLResponse+StatusCode.swift */, ); - path = HTTP; + path = Parameters; sourceTree = ""; }; - 0B77E06729D965D30077FBC0 /* Reachability */ = { + C6554A312AD5C5940090DD3A /* Router */ = { isa = PBXGroup; children = ( - 0B77E06829D965D30077FBC0 /* NWPath+InterfaceType.swift */, - 0B77E06929D965D30077FBC0 /* NetworkReachabilityStatus.swift */, - 0B77E06A29D965D30077FBC0 /* NWInterfaceType+RawRepresentable.swift */, - 0B77E06B29D965D30077FBC0 /* NWInterfaceType+CaseIterable.swift */, - 0B77E06C29D965D30077FBC0 /* NetworkInterfaceType.swift */, - 0B77E06D29D965D30077FBC0 /* NetworkReachability.swift */, + 0B77E08629D965D30077FBC0 /* HTTPDownloadRequestRouter.swift */, + 0B77E08829D965D30077FBC0 /* HTTPUploadRequestRouter.swift */, ); - path = Reachability; + path = Router; sourceTree = ""; }; - 0B77E06E29D965D30077FBC0 /* Event Monitor */ = { + C6554A322AD5C64D0090DD3A /* Request */ = { isa = PBXGroup; children = ( - 0B77E06F29D965D30077FBC0 /* NetworkEventMonitor.swift */, + C6554A342AD5C6830090DD3A /* Event */, + C6554A2E2AD5C4F30090DD3A /* Parameters */, + C6554A312AD5C5940090DD3A /* Router */, ); - path = "Event Monitor"; + path = Request; sourceTree = ""; }; - 0B77E07029D965D30077FBC0 /* Error */ = { + C6554A342AD5C6830090DD3A /* Event */ = { isa = PBXGroup; children = ( - 0B77E07129D965D30077FBC0 /* DefaultNetworkAPIError.swift */, - 0B77E07229D965D30077FBC0 /* NetworkServerError.swift */, - 0B77E07329D965D30077FBC0 /* NetworkClientError.swift */, - 0B77E07429D965D30077FBC0 /* NetworkError.swift */, - 0B77E07529D965D30077FBC0 /* NetworkAPIError.swift */, + 0B77E04E29D965D30077FBC0 /* HTTPDownloadRequestEvent.swift */, + 0B77E05729D965D30077FBC0 /* HTTPUploadRequestEvent.swift */, ); - path = Error; + path = Event; sourceTree = ""; }; - 0B77E07629D965D30077FBC0 /* Rx Extensions */ = { + C6554A352AD5C6950090DD3A /* Client */ = { isa = PBXGroup; children = ( - 0B77E07729D965D30077FBC0 /* Rx+NetworkOps */, - 0B77E08329D965D30077FBC0 /* Rx+CurlCommand */, + C6554A2C2AD5C1560090DD3A /* HTTPClient.swift */, ); - path = "Rx Extensions"; + path = Client; sourceTree = ""; }; - 0B77E07729D965D30077FBC0 /* Rx+NetworkOps */ = { + C6554A362AD5C6A60090DD3A /* Extensions */ = { isa = PBXGroup; children = ( - 0B77E07829D965D30077FBC0 /* Single */, - 0B77E07E29D965D30077FBC0 /* Observable */, - 0B77E08129D965D30077FBC0 /* Completable */, + 0B77E05529D965D30077FBC0 /* Data+AppendString.swift */, + 0B77E08029D965D30077FBC0 /* Observable+Decodable.swift */, + 0B77E07F29D965D30077FBC0 /* Observable+Retry.swift */, + C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */, + 0B77E08429D965D30077FBC0 /* Reactive+Curl.swift */, + 0B77E05029D965D30077FBC0 /* Reactive+URLSessionAdaptedDownloadResponse.swift */, + 0B77E05629D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift */, + 0B77E04D29D965D30077FBC0 /* Reactive+URLSessionDownloadResponse.swift */, + 0B77E05829D965D30077FBC0 /* Reactive+URLSessionUploadResponse.swift */, + C6BDFFEB2ACDF4100022F675 /* Reactive+WebSocketPing.swift */, + C6BDFFE72ACDF3830022F675 /* Reactive+WebSocketReceive.swift */, + C6BDFFE92ACDF3D90022F675 /* Reactive+WebSocketSend.swift */, + 0B77E04F29D965D30077FBC0 /* URLSession+DownloadTask.swift */, + 0B77E05429D965D30077FBC0 /* URLSession+UploadTask.swift */, + C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */, + C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */, ); - path = "Rx+NetworkOps"; + path = Extensions; sourceTree = ""; }; - 0B77E07829D965D30077FBC0 /* Single */ = { + C6554A372AD5C7AD0090DD3A /* Extensions */ = { isa = PBXGroup; children = ( - 0B77E07929D965D30077FBC0 /* Single+Retry.swift */, - 0B77E07A29D965D30077FBC0 /* Single+Decode.swift */, 0B77E07B29D965D30077FBC0 /* Single+CatchErrors.swift */, - 0B77E07C29D965D30077FBC0 /* Single+VerifyResponse.swift */, 0B77E07D29D965D30077FBC0 /* Single+Decodable.swift */, + 0B77E07A29D965D30077FBC0 /* Single+Decode.swift */, + 0B77E08229D965D30077FBC0 /* Completable+Retry.swift */, + 0B77E07929D965D30077FBC0 /* Single+Retry.swift */, + 0B77E07C29D965D30077FBC0 /* Single+VerifyResponse.swift */, ); - path = Single; - sourceTree = ""; - }; - 0B77E07E29D965D30077FBC0 /* Observable */ = { - isa = PBXGroup; - children = ( - 0B77E07F29D965D30077FBC0 /* Observable+Retry.swift */, - 0B77E08029D965D30077FBC0 /* Observable+Decodable.swift */, - ); - path = Observable; + path = Extensions; sourceTree = ""; }; - 0B77E08129D965D30077FBC0 /* Completable */ = { + C6554A382AD5C7DF0090DD3A /* Types */ = { isa = PBXGroup; children = ( - 0B77E08229D965D30077FBC0 /* Completable+Retry.swift */, + C6554A352AD5C6950090DD3A /* Client */, + C6554A322AD5C64D0090DD3A /* Request */, + C6BDFFE62ACDF3260022F675 /* Web Socket */, ); - path = Completable; + path = Types; sourceTree = ""; }; - 0B77E08329D965D30077FBC0 /* Rx+CurlCommand */ = { + C6554A392AD5C7FB0090DD3A /* Types */ = { isa = PBXGroup; children = ( - 0B77E08429D965D30077FBC0 /* Reactive+Curl.swift */, + C6554A3A2AD5C8050090DD3A /* Client */, ); - path = "Rx+CurlCommand"; + path = Types; sourceTree = ""; }; - 0B77E08529D965D30077FBC0 /* Router */ = { + C6554A3A2AD5C8050090DD3A /* Client */ = { isa = PBXGroup; children = ( - 0B77E08629D965D30077FBC0 /* NetworkDownloadRouter.swift */, - 0B77E08729D965D30077FBC0 /* NetworkRouter.swift */, - 0B77E08829D965D30077FBC0 /* NetworkUploadRouter.swift */, + C6554A2A2AD5BBB60090DD3A /* RESTClient.swift */, ); - path = Router; + path = Client; sourceTree = ""; }; C6BDFFE62ACDF3260022F675 /* Web Socket */ = { isa = PBXGroup; children = ( - C6BDFFF12ACDF4CB0022F675 /* Extensions */, C6BDFFED2ACDF46A0022F675 /* WebSocket.swift */, - C6BDFFEF2ACDF4AB0022F675 /* WebSocketCloseHandler.swift */, - C6BDFFF22ACDF50F0022F675 /* WebSocketMessage.swift */, C6BDFFF42ACDF5250022F675 /* WebSocketCloseCode.swift */, + C6BDFFEF2ACDF4AB0022F675 /* WebSocketCloseHandler.swift */, C6B4B4C32AD47A2F009073ED /* WebSocketError.swift */, + C6BDFFF22ACDF50F0022F675 /* WebSocketMessage.swift */, ); path = "Web Socket"; sourceTree = ""; }; - C6BDFFF12ACDF4CB0022F675 /* Extensions */ = { - isa = PBXGroup; - children = ( - C6BDFFE72ACDF3830022F675 /* Reactive+receive.swift */, - C6BDFFE92ACDF3D90022F675 /* Reactive+send.swift */, - C6BDFFEB2ACDF4100022F675 /* Reactive+ping.swift */, - ); - path = Extensions; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -414,8 +350,9 @@ 0B77E0BA29D968DE0077FBC0 /* RxRelay */, 0B77E0BC29D968DE0077FBC0 /* RxSwift */, 0B77E0BF29D969370077FBC0 /* RxSwiftExt */, + C623E7B32AD6262A00A20A0A /* CoreHTTP */, ); - productName = RxNetworking; + productName = RxNetworkKit; productReference = 0B77DFD629D964D40077FBC0 /* RxNetworkKit.framework */; productType = "com.apple.product-type.framework"; }; @@ -445,8 +382,9 @@ packageReferences = ( 0B77E0B729D968DE0077FBC0 /* XCRemoteSwiftPackageReference "RxSwift" */, 0B77E0BE29D969370077FBC0 /* XCRemoteSwiftPackageReference "RxSwiftExt" */, + C623E7B22AD6262A00A20A0A /* XCRemoteSwiftPackageReference "CoreHTTP" */, ); - productRefGroup = 0B77DFD729D964D40077FBC0 /* Products */; + productRefGroup = 0B77DFCC29D964D40077FBC0; projectDirPath = ""; projectRoot = ""; targets = ( @@ -472,63 +410,49 @@ files = ( C6BDFFF02ACDF4AB0022F675 /* WebSocketCloseHandler.swift in Sources */, 0B77E0AD29D965D30077FBC0 /* Single+CatchErrors.swift in Sources */, - 0B77E0B529D965D30077FBC0 /* NetworkRouter.swift in Sources */, - 0B77E09529D965D30077FBC0 /* RequestRetryPolicy.swift in Sources */, 0B77E0B329D965D30077FBC0 /* Reactive+Curl.swift in Sources */, - C6A9BEF82A93E2D600459E32 /* HTTPErrorBody.swift in Sources */, - 0B77E0A929D965D30077FBC0 /* NetworkError.swift in Sources */, - C6BDFFEA2ACDF3D90022F675 /* Reactive+send.swift in Sources */, + C6BDFFEA2ACDF3D90022F675 /* Reactive+WebSocketSend.swift in Sources */, 0B77E09F29D965D30077FBC0 /* NWPath+InterfaceType.swift in Sources */, - 0B77E09C29D965D30077FBC0 /* HTTPScheme.swift in Sources */, 0B77E0A029D965D30077FBC0 /* NetworkReachabilityStatus.swift in Sources */, - 0B77E09D29D965D30077FBC0 /* HTTPURLResponse+StatusCode.swift in Sources */, 0B77E0AE29D965D30077FBC0 /* Single+VerifyResponse.swift in Sources */, - 0B77E0B429D965D30077FBC0 /* NetworkDownloadRouter.swift in Sources */, + 0B77E0B429D965D30077FBC0 /* HTTPDownloadRequestRouter.swift in Sources */, 0B77E09A29D965D30077FBC0 /* HTTPMIMEType.swift in Sources */, + C658CF652AD5F648009E561D /* RxNetworkKit.swift in Sources */, C6A9BEFF2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift in Sources */, - 0B77E08E29D965D30077FBC0 /* UploadFile.swift in Sources */, - 0B77E09729D965D30077FBC0 /* RequestRetrier.swift in Sources */, - 0B77E09E29D965D30077FBC0 /* HTTPStatusCode.swift in Sources */, - 0B77E08D29D965D30077FBC0 /* UploadFormData.swift in Sources */, + 0B77E08E29D965D30077FBC0 /* HTTPUploadRequestFile.swift in Sources */, + 0B77E08D29D965D30077FBC0 /* HTTPUploadRequestFormData.swift in Sources */, 0B77E0AF29D965D30077FBC0 /* Single+Decodable.swift in Sources */, C69A78562ACF001400ECF092 /* Docs.docc in Sources */, 0B77E0A129D965D30077FBC0 /* NWInterfaceType+RawRepresentable.swift in Sources */, - 0B77E0A529D965D30077FBC0 /* NetworkEventMonitor.swift in Sources */, 0B77E0B129D965D30077FBC0 /* Observable+Decodable.swift in Sources */, - 0B77E09929D965D30077FBC0 /* NetworkManager.swift in Sources */, - 0B77E08A29D965D30077FBC0 /* DownloadEvent.swift in Sources */, + 0B77E08A29D965D30077FBC0 /* HTTPDownloadRequestEvent.swift in Sources */, C6A9BEFD2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift in Sources */, + C6554A2B2AD5BBB60090DD3A /* RESTClient.swift in Sources */, 0B77E09129D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift in Sources */, C6A9BEFA2A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift in Sources */, C6BDFFF32ACDF5100022F675 /* WebSocketMessage.swift in Sources */, C6B4B4C42AD47A2F009073ED /* WebSocketError.swift in Sources */, - 0B77E0A629D965D30077FBC0 /* DefaultNetworkAPIError.swift in Sources */, 0B77E0A429D965D30077FBC0 /* NetworkReachability.swift in Sources */, 0B77E0B229D965D30077FBC0 /* Completable+Retry.swift in Sources */, 0B77E0B029D965D30077FBC0 /* Observable+Retry.swift in Sources */, 0B77E08B29D965D30077FBC0 /* URLSession+DownloadTask.swift in Sources */, C6BDFFF52ACDF5250022F675 /* WebSocketCloseCode.swift in Sources */, - 0B77E0A729D965D30077FBC0 /* NetworkServerError.swift in Sources */, - 0B77E0B629D965D30077FBC0 /* NetworkUploadRouter.swift in Sources */, - C6A9BEF62A93E2AE00459E32 /* DefaultHTTPErrorBody.swift in Sources */, + 0B77E0B629D965D30077FBC0 /* HTTPUploadRequestRouter.swift in Sources */, 0B77E0AB29D965D30077FBC0 /* Single+Retry.swift in Sources */, 0B77E08F29D965D30077FBC0 /* URLSession+UploadTask.swift in Sources */, 0B77E08C29D965D30077FBC0 /* Reactive+URLSessionAdaptedDownloadResponse.swift in Sources */, - 0B77E09429D965D30077FBC0 /* RequestInterceptor.swift in Sources */, - 0B77E09629D965D30077FBC0 /* RequestAdapter.swift in Sources */, - 0B77E0A829D965D30077FBC0 /* NetworkClientError.swift in Sources */, 0B77E0A229D965D30077FBC0 /* NWInterfaceType+CaseIterable.swift in Sources */, C6BDFFEE2ACDF46A0022F675 /* WebSocket.swift in Sources */, + C61CB5642AF2CFDD006A203A /* Session.swift in Sources */, 0B77E09329D965D30077FBC0 /* Reactive+URLSessionUploadResponse.swift in Sources */, - 0B77E09B29D965D30077FBC0 /* HTTPMethod.swift in Sources */, 0B77E0AC29D965D30077FBC0 /* Single+Decode.swift in Sources */, 0B77E0A329D965D30077FBC0 /* NetworkInterfaceType.swift in Sources */, - 0B77E09229D965D30077FBC0 /* UploadEvent.swift in Sources */, + 0B77E09229D965D30077FBC0 /* HTTPUploadRequestEvent.swift in Sources */, 0B77E09029D965D30077FBC0 /* Data+AppendString.swift in Sources */, - C6BDFFE82ACDF3830022F675 /* Reactive+receive.swift in Sources */, + C6BDFFE82ACDF3830022F675 /* Reactive+WebSocketReceive.swift in Sources */, 0B77E08929D965D30077FBC0 /* Reactive+URLSessionDownloadResponse.swift in Sources */, - C6BDFFEC2ACDF4100022F675 /* Reactive+ping.swift in Sources */, - 0B77E0AA29D965D30077FBC0 /* NetworkAPIError.swift in Sources */, + C6554A2D2AD5C1560090DD3A /* HTTPClient.swift in Sources */, + C6BDFFEC2ACDF4100022F675 /* Reactive+WebSocketPing.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -670,6 +594,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_MODULE_VERIFIER = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -708,6 +633,7 @@ DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_MODULE_VERIFIER = YES; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -773,6 +699,14 @@ minimumVersion = 6.2.0; }; }; + C623E7B22AD6262A00A20A0A /* XCRemoteSwiftPackageReference "CoreHTTP" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/loay-ashraf/CoreHTTP"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -796,6 +730,11 @@ package = 0B77E0BE29D969370077FBC0 /* XCRemoteSwiftPackageReference "RxSwiftExt" */; productName = RxSwiftExt; }; + C623E7B32AD6262A00A20A0A /* CoreHTTP */ = { + isa = XCSwiftPackageProductDependency; + package = C623E7B22AD6262A00A20A0A /* XCRemoteSwiftPackageReference "CoreHTTP" */; + productName = CoreHTTP; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 0B77DFCD29D964D40077FBC0 /* Project object */; diff --git a/RxNetworkKit.xcworkspace/contents.xcworkspacedata b/RxNetworkKit.xcworkspace/contents.xcworkspacedata index db63a18..1281c77 100644 --- a/RxNetworkKit.xcworkspace/contents.xcworkspacedata +++ b/RxNetworkKit.xcworkspace/contents.xcworkspacedata @@ -2,7 +2,10 @@ + location = "group:RxNetworkKit.xcodeproj"> + + diff --git a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved index e70bc7c..e903d19 100644 --- a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,14 @@ { "pins" : [ + { + "identity" : "corehttp", + "kind" : "remoteSourceControl", + "location" : "https://github.com/loay-ashraf/CoreHTTP", + "state" : { + "revision" : "17b030a4a10ac9449e6c107bce13f112280aae6b", + "version" : "1.0.0" + } + }, { "identity" : "rxdatasources", "kind" : "remoteSourceControl", diff --git a/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj b/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7f43934 --- /dev/null +++ b/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj @@ -0,0 +1,481 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 55; + objects = { + +/* Begin PBXBuildFile section */ + C62E151A2AD77B8F003CB8FA /* CoreExample.h in Headers */ = {isa = PBXBuildFile; fileRef = C62E15192AD77B8F003CB8FA /* CoreExample.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C62E15202AD77BCC003CB8FA /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62E14D22AD7771E003CB8FA /* Model.swift */; }; + C62E15212AD77BD3003CB8FA /* RequestRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62E14D42AD77729003CB8FA /* RequestRouter.swift */; }; + C62E15222AD77BD3003CB8FA /* DownloadRequestRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62E14D52AD77729003CB8FA /* DownloadRequestRouter.swift */; }; + C62E15232AD77BDA003CB8FA /* ViewLoadType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62E14D92AD77736003CB8FA /* ViewLoadType.swift */; }; + C62E15242AD77BDA003CB8FA /* ViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62E14D82AD77736003CB8FA /* ViewState.swift */; }; + C62E15252AD77BDF003CB8FA /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62E14DC2AD77747003CB8FA /* ViewModel.swift */; }; + C62E15292AD77C39003CB8FA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = C62E15282AD77C39003CB8FA /* RxSwift */; }; + C62E153E2AD78417003CB8FA /* RequestInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62E153D2AD78417003CB8FA /* RequestInterceptor.swift */; }; + C62E15402AD78483003CB8FA /* RequestEventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62E153F2AD78483003CB8FA /* RequestEventMonitor.swift */; }; + C63EEB352AD7BE17003A64CA /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C62E15042AD77AA4003CB8FA /* RxNetworkKit.framework */; }; + C63EEB4A2AD7C4B6003A64CA /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = C63EEB492AD7C4B6003A64CA /* RxCocoa */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + C62E14D22AD7771E003CB8FA /* Model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = ""; }; + C62E14D42AD77729003CB8FA /* RequestRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestRouter.swift; sourceTree = ""; }; + C62E14D52AD77729003CB8FA /* DownloadRequestRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadRequestRouter.swift; sourceTree = ""; }; + C62E14D82AD77736003CB8FA /* ViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewState.swift; sourceTree = ""; }; + C62E14D92AD77736003CB8FA /* ViewLoadType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewLoadType.swift; sourceTree = ""; }; + C62E14DC2AD77747003CB8FA /* ViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; + C62E15042AD77AA4003CB8FA /* RxNetworkKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RxNetworkKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C62E15172AD77B8F003CB8FA /* CoreExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C62E15192AD77B8F003CB8FA /* CoreExample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreExample.h; sourceTree = ""; }; + C62E153D2AD78417003CB8FA /* RequestInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestInterceptor.swift; sourceTree = ""; }; + C62E153F2AD78483003CB8FA /* RequestEventMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestEventMonitor.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C62E15142AD77B8F003CB8FA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C63EEB4A2AD7C4B6003A64CA /* RxCocoa in Frameworks */, + C63EEB352AD7BE17003A64CA /* RxNetworkKit.framework in Frameworks */, + C62E15292AD77C39003CB8FA /* RxSwift in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C62E14BB2AD776BD003CB8FA = { + isa = PBXGroup; + children = ( + C62E14C62AD776BD003CB8FA /* Source */, + C62E14C52AD776BD003CB8FA /* Products */, + C62E15032AD77AA4003CB8FA /* Frameworks */, + ); + sourceTree = ""; + }; + C62E14C52AD776BD003CB8FA /* Products */ = { + isa = PBXGroup; + children = ( + C62E15172AD77B8F003CB8FA /* CoreExample.framework */, + ); + name = Products; + sourceTree = ""; + }; + C62E14C62AD776BD003CB8FA /* Source */ = { + isa = PBXGroup; + children = ( + C62E14CE2AD776F1003CB8FA /* Model */, + C62E14CF2AD776F7003CB8FA /* Network */, + C62E14D12AD7770B003CB8FA /* View */, + C62E14D02AD776FF003CB8FA /* View Model */, + C62E15192AD77B8F003CB8FA /* CoreExample.h */, + ); + path = Source; + sourceTree = ""; + }; + C62E14CE2AD776F1003CB8FA /* Model */ = { + isa = PBXGroup; + children = ( + C62E14D22AD7771E003CB8FA /* Model.swift */, + ); + path = Model; + sourceTree = ""; + }; + C62E14CF2AD776F7003CB8FA /* Network */ = { + isa = PBXGroup; + children = ( + C62E14D52AD77729003CB8FA /* DownloadRequestRouter.swift */, + C62E14D42AD77729003CB8FA /* RequestRouter.swift */, + C62E153D2AD78417003CB8FA /* RequestInterceptor.swift */, + C62E153F2AD78483003CB8FA /* RequestEventMonitor.swift */, + ); + path = Network; + sourceTree = ""; + }; + C62E14D02AD776FF003CB8FA /* View Model */ = { + isa = PBXGroup; + children = ( + C62E14DC2AD77747003CB8FA /* ViewModel.swift */, + ); + path = "View Model"; + sourceTree = ""; + }; + C62E14D12AD7770B003CB8FA /* View */ = { + isa = PBXGroup; + children = ( + C62E14D92AD77736003CB8FA /* ViewLoadType.swift */, + C62E14D82AD77736003CB8FA /* ViewState.swift */, + ); + path = View; + sourceTree = ""; + }; + C62E15032AD77AA4003CB8FA /* Frameworks */ = { + isa = PBXGroup; + children = ( + C62E15042AD77AA4003CB8FA /* RxNetworkKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + C62E15122AD77B8F003CB8FA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + C62E151A2AD77B8F003CB8FA /* CoreExample.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + C62E15162AD77B8F003CB8FA /* CoreExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = C62E151B2AD77B8F003CB8FA /* Build configuration list for PBXNativeTarget "CoreExample" */; + buildPhases = ( + C62E15122AD77B8F003CB8FA /* Headers */, + C62E15132AD77B8F003CB8FA /* Sources */, + C62E15142AD77B8F003CB8FA /* Frameworks */, + C62E15152AD77B8F003CB8FA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CoreExample; + packageProductDependencies = ( + C62E15282AD77C39003CB8FA /* RxSwift */, + C63EEB492AD7C4B6003A64CA /* RxCocoa */, + ); + productName = CoreExample; + productReference = C62E15172AD77B8F003CB8FA /* CoreExample.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C62E14BC2AD776BD003CB8FA /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + C62E15162AD77B8F003CB8FA = { + CreatedOnToolsVersion = 15.0; + LastSwiftMigration = 1500; + }; + }; + }; + buildConfigurationList = C62E14BF2AD776BD003CB8FA /* Build configuration list for PBXProject "CoreExample" */; + compatibilityVersion = "Xcode 13.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C62E14BB2AD776BD003CB8FA; + packageReferences = ( + C62E14FE2AD779FF003CB8FA /* XCRemoteSwiftPackageReference "RxSwift" */, + ); + productRefGroup = C62E14C52AD776BD003CB8FA /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C62E15162AD77B8F003CB8FA /* CoreExample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C62E15152AD77B8F003CB8FA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C62E15132AD77B8F003CB8FA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C62E15222AD77BD3003CB8FA /* DownloadRequestRouter.swift in Sources */, + C62E15242AD77BDA003CB8FA /* ViewState.swift in Sources */, + C62E153E2AD78417003CB8FA /* RequestInterceptor.swift in Sources */, + C62E15232AD77BDA003CB8FA /* ViewLoadType.swift in Sources */, + C62E15212AD77BD3003CB8FA /* RequestRouter.swift in Sources */, + C62E15402AD78483003CB8FA /* RequestEventMonitor.swift in Sources */, + C62E15202AD77BCC003CB8FA /* Model.swift in Sources */, + C62E15252AD77BDF003CB8FA /* ViewModel.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + C62E14C92AD776BD003CB8FA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + C62E14CA2AD776BD003CB8FA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C62E151C2AD77B8F003CB8FA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = Z3SPAA69G7; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + MARKETING_VERSION = 1.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.las.CoreExample; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + C62E151D2AD77B8F003CB8FA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = Z3SPAA69G7; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + MARKETING_VERSION = 1.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.las.CoreExample; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C62E14BF2AD776BD003CB8FA /* Build configuration list for PBXProject "CoreExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C62E14C92AD776BD003CB8FA /* Debug */, + C62E14CA2AD776BD003CB8FA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C62E151B2AD77B8F003CB8FA /* Build configuration list for PBXNativeTarget "CoreExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C62E151C2AD77B8F003CB8FA /* Debug */, + C62E151D2AD77B8F003CB8FA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + C62E14FE2AD779FF003CB8FA /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + C62E15282AD77C39003CB8FA /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = C62E14FE2AD779FF003CB8FA /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; + C63EEB492AD7C4B6003A64CA /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = C62E14FE2AD779FF003CB8FA /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = C62E14BC2AD776BD003CB8FA /* Project object */; +} diff --git a/Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..7b57c5f --- /dev/null +++ b/Shared/CoreExample/CoreExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "rxswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactiveX/RxSwift", + "state" : { + "revision" : "9dcaa4b333db437b0fbfaf453fad29069044a8b4", + "version" : "6.6.0" + } + } + ], + "version" : 2 +} diff --git a/Shared/CoreExample/CoreExample.xcodeproj/xcshareddata/xcschemes/CoreExample.xcscheme b/Shared/CoreExample/CoreExample.xcodeproj/xcshareddata/xcschemes/CoreExample.xcscheme new file mode 100644 index 0000000..62cd4ce --- /dev/null +++ b/Shared/CoreExample/CoreExample.xcodeproj/xcshareddata/xcschemes/CoreExample.xcscheme @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Shared/CoreExample/Source/CoreExample.h b/Shared/CoreExample/Source/CoreExample.h new file mode 100644 index 0000000..44ec9dd --- /dev/null +++ b/Shared/CoreExample/Source/CoreExample.h @@ -0,0 +1,18 @@ +// +// CoreExample.h +// CoreExample +// +// Created by Loay Ashraf on 12/10/2023. +// + +#import + +//! Project version number for CoreExample. +FOUNDATION_EXPORT double CoreExampleVersionNumber; + +//! Project version string for CoreExample. +FOUNDATION_EXPORT const unsigned char CoreExampleVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Examples/macOS/macOS Example/Model/Model.swift b/Shared/CoreExample/Source/Model/Model.swift similarity index 64% rename from Examples/macOS/macOS Example/Model/Model.swift rename to Shared/CoreExample/Source/Model/Model.swift index a7104db..6e7434f 100644 --- a/Examples/macOS/macOS Example/Model/Model.swift +++ b/Shared/CoreExample/Source/Model/Model.swift @@ -7,11 +7,11 @@ import Foundation -struct Model: Decodable { - let id: Int - let avatarURL: URL - let htmlURL: URL - let login: String +public struct Model: Decodable { + public let id: Int + public let avatarURL: URL + public let htmlURL: URL + public let login: String enum CodingKeys: String, CodingKey { case id case avatarURL = "avatar_url" diff --git a/Examples/iOS/iOS Example/Network/DownloadRouter.swift b/Shared/CoreExample/Source/Network/DownloadRequestRouter.swift similarity index 50% rename from Examples/iOS/iOS Example/Network/DownloadRouter.swift rename to Shared/CoreExample/Source/Network/DownloadRequestRouter.swift index cffa683..7d572c4 100644 --- a/Examples/iOS/iOS Example/Network/DownloadRouter.swift +++ b/Shared/CoreExample/Source/Network/DownloadRequestRouter.swift @@ -1,5 +1,5 @@ // -// DownloadRouter.swift +// DownloadRequestRouter.swift // RxNetworkKit // // Created by Loay Ashraf on 01/04/2023. @@ -8,24 +8,24 @@ import Foundation import RxNetworkKit -enum DownloadRouter: NetworkDownloadRouter { +public enum DownloadRequestRouter: HTTPDownloadRequestRouter { case `default`(url: URL) - var scheme: HTTPScheme { + public var scheme: HTTPScheme { .https } - var domain: String { + public var domain: String { "" } - var path: String { + public var path: String { "" } - var headers: [String : String] { + public var headers: [String : String] { [:] } - var parameters: [String : String]? { + public var parameters: [String : String]? { nil } - var url: URL? { + public var url: URL? { switch self { case .default(let url): return url diff --git a/Shared/CoreExample/Source/Network/RequestEventMonitor.swift b/Shared/CoreExample/Source/Network/RequestEventMonitor.swift new file mode 100644 index 0000000..fa1de5d --- /dev/null +++ b/Shared/CoreExample/Source/Network/RequestEventMonitor.swift @@ -0,0 +1,26 @@ +// +// RequestEventMonitor.swift +// CoreExample +// +// Created by Loay Ashraf on 12/10/2023. +// + +import RxNetworkKit + +public class RequestEventMonitor: NSObject, HTTPRequestEventMonitor { + public override init() { } + public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { + debugPrint("") + } + public func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { + debugPrint(session) + } + public func urlSession(_ session: URLSession, didCreateTask task: URLSessionTask) { + debugPrint("Task created!") + } + public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { + guard let error else { return } + debugPrint("Task did finish with error: \(error.localizedDescription)!") + } + public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { } +} diff --git a/Shared/CoreExample/Source/Network/RequestInterceptor.swift b/Shared/CoreExample/Source/Network/RequestInterceptor.swift new file mode 100644 index 0000000..9d527bb --- /dev/null +++ b/Shared/CoreExample/Source/Network/RequestInterceptor.swift @@ -0,0 +1,24 @@ +// +// RequestInterceptor.swift +// CoreExample +// +// Created by Loay Ashraf on 12/10/2023. +// + +import RxNetworkKit + +public class RequestInterceptor: HTTPRequestInterceptor { + public init() { } + public func adapt(_ request: URLRequest, for session: URLSession) -> URLRequest { + return request + } + public func retryMaxAttempts(_ request: URLRequest, for session: URLSession) -> Int { + 5 + } + public func retryPolicy(_ request: URLRequest, for session: URLSession) -> HTTPRequestRetryPolicy { + .constant(time: 5_000) + } + public func shouldRetry(_ request: URLRequest, for session: URLSession, dueTo error: HTTPError) -> Bool { + true + } +} diff --git a/Examples/iOS/iOS Example/Network/Router.swift b/Shared/CoreExample/Source/Network/RequestRouter.swift similarity index 53% rename from Examples/iOS/iOS Example/Network/Router.swift rename to Shared/CoreExample/Source/Network/RequestRouter.swift index 564a378..ecc9702 100644 --- a/Examples/iOS/iOS Example/Network/Router.swift +++ b/Shared/CoreExample/Source/Network/RequestRouter.swift @@ -1,5 +1,5 @@ // -// Router.swift +// RequestRouter.swift // RxNetworkKit // // Created by Loay Ashraf on 01/04/2023. @@ -8,30 +8,30 @@ import Foundation import RxNetworkKit -enum Router: NetworkRouter { +public enum RequestRouter: HTTPRequestRouter { case `default` - var scheme: HTTPScheme { + public var scheme: HTTPScheme { .https } - var method: HTTPMethod { + public var method: HTTPMethod { .get } - var domain: String { + public var domain: String { "api.github.com" } - var path: String { + public var path: String { "users" } - var headers: [String : String] { + public var headers: [String : String] { ["Accept": "application/vnd.github+json"] } - var parameters: [String : String]? { + public var parameters: [String : String]? { nil } - var body: [String : Any]? { + public var body: [String : Any]? { nil } - var url: URL? { + public var url: URL? { let urlString = scheme.rawValue + domain + "/" + path return URL(string: urlString) } diff --git a/Examples/iOS/iOS Example/View Model/ViewModel.swift b/Shared/CoreExample/Source/View Model/ViewModel.swift similarity index 64% rename from Examples/iOS/iOS Example/View Model/ViewModel.swift rename to Shared/CoreExample/Source/View Model/ViewModel.swift index aa5517e..604e3a1 100644 --- a/Examples/iOS/iOS Example/View Model/ViewModel.swift +++ b/Shared/CoreExample/Source/View Model/ViewModel.swift @@ -10,21 +10,24 @@ import RxSwift import RxCocoa import RxNetworkKit -class ViewModel { +public class ViewModel { // MARK: Input // Input sequence is the same as view state (only for loading states) - private(set) var viewState: PublishRelay = .init() + public private(set) var viewState: PublishRelay = .init() // MARK: Output - private(set) var users: Driver<[Model]>! - private(set) var error: Driver! + public private(set) var users: Driver<[Model]>! + public private(set) var error: Driver! // MARK: Properties and Dependencies - private let networkManager: NetworkManager + private let restClient: RESTClient + private let httpClient: HTTPClient private let disposeBag = DisposeBag() /// Creates `ViewModel` instance /// - /// - Parameter networkManager: `NetworkManager` object used for making network API calls. - init(networkManager: NetworkManager) { - self.networkManager = networkManager + /// - Parameter restClient: `RESTClient` object used for making rest API calls. + /// - Parameter httpClient: `HTTPClient` object used for making http requests. + public init(restClient: RESTClient, httpClient: HTTPClient) { + self.restClient = restClient + self.httpClient = httpClient bindOutput() } /// Creates observable sequence that results in image data. @@ -32,8 +35,8 @@ class ViewModel { /// - Parameter router: `DownloadRouter` used to download image data. /// /// - Returns: observable sequence that results in image data. - func downloadImage(_ router: DownloadRouter) -> Observable { - networkManager.download(router) + public func downloadImage(_ router: DownloadRequestRouter) -> Observable { + httpClient.download(router) } /// Binds output sequence to input sequence. private func bindOutput() { @@ -42,7 +45,7 @@ class ViewModel { .filter( { ![.idle, .loading(loadType: .paginate), .error].contains($0) }) .flatMapLatest{ [weak self] _ in guard let self = self else { return Observable<[Model]>.empty().materialize() } - let single: Single<[Model]> = self.networkManager.request(Router.default) + let single: Single<[Model]> = self.restClient.request(RequestRouter.default) return single .asObservable() .materialize() @@ -53,8 +56,8 @@ class ViewModel { .compactMap { $0.element } .asDriver(onErrorJustReturn: []) self.error = loadObservable - .compactMap { $0.error as? NetworkError } - .asDriver(onErrorJustReturn: NetworkError.client(.transport(NSError(domain: "", code: 1, userInfo: nil)))) + .compactMap { $0.error as? HTTPError } + .asDriver(onErrorJustReturn: HTTPError.client(.transport(NSError(domain: "", code: 1, userInfo: nil)))) // Bind output sequence to input sequence (view state) self.users .asObservable() diff --git a/Examples/iOS/iOS Example/View/ViewLoadType.swift b/Shared/CoreExample/Source/View/ViewLoadType.swift similarity index 84% rename from Examples/iOS/iOS Example/View/ViewLoadType.swift rename to Shared/CoreExample/Source/View/ViewLoadType.swift index 3aab992..c3ced42 100644 --- a/Examples/iOS/iOS Example/View/ViewLoadType.swift +++ b/Shared/CoreExample/Source/View/ViewLoadType.swift @@ -5,7 +5,7 @@ // Created by Loay Ashraf on 01/04/2023. // -enum ViewLoadType { +public enum ViewLoadType { case initial case refresh case paginate diff --git a/Examples/iOS/iOS Example/View/ViewState.swift b/Shared/CoreExample/Source/View/ViewState.swift similarity index 82% rename from Examples/iOS/iOS Example/View/ViewState.swift rename to Shared/CoreExample/Source/View/ViewState.swift index e5cd81d..dd7a502 100644 --- a/Examples/iOS/iOS Example/View/ViewState.swift +++ b/Shared/CoreExample/Source/View/ViewState.swift @@ -5,7 +5,7 @@ // Created by Loay Ashraf on 01/04/2023. // -enum ViewState: Equatable { +public enum ViewState: Equatable { case idle case loading(loadType: ViewLoadType) case error diff --git a/Source/Error/DefaultNetworkAPIError.swift b/Source/Error/DefaultNetworkAPIError.swift deleted file mode 100644 index de4431c..0000000 --- a/Source/Error/DefaultNetworkAPIError.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// DefaultNetworkAPIError.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 19/02/2023. -// - -/// Default type used for decoding internal api error bodies. -public struct DefaultNetworkAPIError: NetworkAPIError { - - /// error message. - let message: String - -} diff --git a/Source/Error/NetworkAPIError.swift b/Source/Error/NetworkAPIError.swift deleted file mode 100644 index 77d6575..0000000 --- a/Source/Error/NetworkAPIError.swift +++ /dev/null @@ -1,9 +0,0 @@ -// -// NetworkAPIError.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 20/03/2023. -// - -/// Internal api error body type. -public protocol NetworkAPIError: Error, Decodable { } diff --git a/Source/Error/NetworkClientError.swift b/Source/Error/NetworkClientError.swift deleted file mode 100644 index 2724b5f..0000000 --- a/Source/Error/NetworkClientError.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// NetworkClientError.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 19/02/2023. -// - -/// Client-side (transport) network error. -public enum NetworkClientError: Error { - case http(HTTPStatusCode, HTTPErrorBody?) - case serialization(Error) - case transport(Error) -} diff --git a/Source/Error/NetworkError.swift b/Source/Error/NetworkError.swift deleted file mode 100644 index 493151d..0000000 --- a/Source/Error/NetworkError.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// NetworkError.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 19/02/2023. -// - -import Foundation - -/// Generic network error (client, server or api). -public enum NetworkError: Error { - case client(NetworkClientError) - case server(NetworkServerError) - case api(NetworkAPIError) - - /// Creates `NetworkError` instance. - /// - /// - Parameter response: `HTTPURLResponse` used to get response status code. - init?(_ response: HTTPURLResponse?, data: Data?, errorType: E.Type) { - if let response = response, - let httpStatusCode = response.status { - // Get Error body from response data if possible. - var httpErrorBody: E? - if let data = data { - httpErrorBody = try? JSONDecoder().decode(errorType.self, from: data) - } - // Get Error from response status code - switch httpStatusCode.responseType { - case .clientError: - self = .client(.http(httpStatusCode, httpErrorBody)) - case .serverError: - self = .server(.http(httpStatusCode, httpErrorBody)) - default: - return nil - } - } else { - return nil - } - } - -} diff --git a/Source/Error/NetworkServerError.swift b/Source/Error/NetworkServerError.swift deleted file mode 100644 index 8a9713e..0000000 --- a/Source/Error/NetworkServerError.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// NetworkServerError.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 19/02/2023. -// - -/// Server-side network error. -public enum NetworkServerError: Error { - case http(HTTPStatusCode, HTTPErrorBody?) - case generic(Error) -} diff --git a/Source/Event Monitor/NetworkEventMonitor.swift b/Source/Event Monitor/NetworkEventMonitor.swift deleted file mode 100644 index 8fe8991..0000000 --- a/Source/Event Monitor/NetworkEventMonitor.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// EventMonitor.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 28/03/2023. -// - -import Foundation - -// To monitor network events in a given session, all you have to do -// is implement its delegate methods, easy and simple isn't it? 🤔. -/// Session and Tasks delegate. -public typealias NetworkEventMonitor = URLSessionDelegate & URLSessionTaskDelegate & URLSessionDataDelegate & URLSessionStreamDelegate & URLSessionDownloadDelegate & URLSessionWebSocketDelegate diff --git a/Source/HTTP/DefaultHTTPErrorBody.swift b/Source/HTTP/DefaultHTTPErrorBody.swift deleted file mode 100644 index 7b2055f..0000000 --- a/Source/HTTP/DefaultHTTPErrorBody.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// DefaultHTTPErrorBody.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 21/08/2023. -// - -import Foundation - -/// Default type used for decoding http error bodies. -public struct DefaultHTTPErrorBody: HTTPErrorBody { - - /// response status code. - let statusCode: Int? - /// error message. - let message: String? - /// support identifier. - let supportId: String? - -} diff --git a/Source/Custom Requests/Upload/Data+AppendString.swift b/Source/HTTP/Extensions/Data+AppendString.swift similarity index 100% rename from Source/Custom Requests/Upload/Data+AppendString.swift rename to Source/HTTP/Extensions/Data+AppendString.swift diff --git a/Source/Rx Extensions/Rx+NetworkOps/Observable/Observable+Decodable.swift b/Source/HTTP/Extensions/Observable+Decodable.swift similarity index 79% rename from Source/Rx Extensions/Rx+NetworkOps/Observable/Observable+Decodable.swift rename to Source/HTTP/Extensions/Observable+Decodable.swift index 084cca8..27fdde0 100644 --- a/Source/Rx Extensions/Rx+NetworkOps/Observable/Observable+Decodable.swift +++ b/Source/HTTP/Extensions/Observable+Decodable.swift @@ -12,11 +12,11 @@ extension Observable where Element == (response: HTTPURLResponse, data: Data) { /// Creates `Completable` observable + handles transport errors. /// /// - Parameters: - /// - httpErrorType: `Decodable` http error body type. + /// - httpErrorType: `Decodable` http body error type. /// - apiErrorType: `Decodable` api error type. /// /// - Returns: Observable to be observed for values. - func decodable(_ httpErrorType: E.Type, apiErrorType: AE.Type) -> Completable { + func decodable(_ httpErrorType: E.Type, apiErrorType: AE.Type) -> Completable { asSingle() // catch any transport errors if thrown. .catchTransportError() @@ -29,11 +29,11 @@ extension Observable where Element == (response: HTTPURLResponse, data: Data) { /// /// - Parameters: /// - modelType: `Decodable` model type. - /// - httpErrorType: `Decodable` http error body type. + /// - httpErrorType: `Decodable` http body error type. /// - apiErrorType: `Decodable` api error type. /// /// - Returns: Observable to be observed for values. - func decodable(_ modelType: M.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Single { + func decodable(_ modelType: M.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Single { asSingle() // catch any transport errors if thrown. .catchTransportError() diff --git a/Source/Rx Extensions/Rx+NetworkOps/Observable/Observable+Retry.swift b/Source/HTTP/Extensions/Observable+Retry.swift similarity index 84% rename from Source/Rx Extensions/Rx+NetworkOps/Observable/Observable+Retry.swift rename to Source/HTTP/Extensions/Observable+Retry.swift index b805a37..712f23b 100644 --- a/Source/Rx Extensions/Rx+NetworkOps/Observable/Observable+Retry.swift +++ b/Source/HTTP/Extensions/Observable+Retry.swift @@ -24,12 +24,12 @@ extension ObservableType { /// /// - Returns: `Observable` sequence with provoded retry strategy applied. func retry(_ maxAttemptCount: Int = Int.max, - delay: NetworkRequestRetryPolicy, + delay: HTTPRequestRetryPolicy, didBecomeReachable: PublishRelay = NetworkReachability.shared.didBecomeReachable, - shouldRetry: @escaping (NetworkError) -> Bool = { _ in true }) -> Observable { + shouldRetry: @escaping (HTTPError) -> Bool = { _ in true }) -> Observable { return retry { (errors: Observable) in return errors.enumerated().flatMap { attempt, error -> Observable in - guard let networkError = error as? NetworkError, maxAttemptCount > attempt + 1, shouldRetry(networkError) else { + guard let networkError = error as? HTTPError, maxAttemptCount > attempt + 1, shouldRetry(networkError) else { return .error(error) } let timer = Observable.timer( diff --git a/Source/Manager/ProcessInfo+operatingSystemName.swift b/Source/HTTP/Extensions/ProcessInfo+operatingSystemName.swift similarity index 100% rename from Source/Manager/ProcessInfo+operatingSystemName.swift rename to Source/HTTP/Extensions/ProcessInfo+operatingSystemName.swift diff --git a/Source/Rx Extensions/Rx+CurlCommand/Reactive+Curl.swift b/Source/HTTP/Extensions/Reactive+Curl.swift similarity index 97% rename from Source/Rx Extensions/Rx+CurlCommand/Reactive+Curl.swift rename to Source/HTTP/Extensions/Reactive+Curl.swift index 5d6669b..ca663cb 100644 --- a/Source/Rx Extensions/Rx+CurlCommand/Reactive+Curl.swift +++ b/Source/HTTP/Extensions/Reactive+Curl.swift @@ -43,12 +43,13 @@ extension Reactive where Base: URLSession { return returnValue } /// converts `URLResponse` to string. + /// /// - Parameters: /// - response: `URLResponse` to be converted to string /// - error: `NSError` error returned in response. /// - interval: `TimeInterval` taken by request to complete. /// - /// - Returns: result string from `URLResponse`convertion. + /// - Returns: result string from `URLResponse`conversion. func convertResponseToString(_ response: URLResponse?, _ error: NSError?, _ interval: TimeInterval) -> String { let ms = Int(interval * 1000) if let response = response as? HTTPURLResponse { diff --git a/Source/Custom Requests/Download/Reactive+URLSessionAdaptedDownloadResponse.swift b/Source/HTTP/Extensions/Reactive+URLSessionAdaptedDownloadResponse.swift similarity index 62% rename from Source/Custom Requests/Download/Reactive+URLSessionAdaptedDownloadResponse.swift rename to Source/HTTP/Extensions/Reactive+URLSessionAdaptedDownloadResponse.swift index 99adaba..abc4b20 100644 --- a/Source/Custom Requests/Download/Reactive+URLSessionAdaptedDownloadResponse.swift +++ b/Source/HTTP/Extensions/Reactive+URLSessionAdaptedDownloadResponse.swift @@ -8,26 +8,27 @@ import Foundation import RxSwift import RxCocoa +import RxSwiftExt extension Reactive where Base: URLSession { /// Creates an observable with `DownloadEvent` type. /// /// - Parameters: /// - request: `URLRequest` used to create upload task and its observables. - /// - httpErrorType: `HTTPErrorBody` http error body type. - /// - apiErrorType: `NetworkAPIError` type for expected error in HTTP response body. + /// - httpErrorType: `HTTPBodyError` http body error type. + /// - apiErrorType: `HTTPAPIError` type for expected error in HTTP response body. /// /// - Returns: a `Observable` object of `DownloadEvent` type. - func downloadResponse(request: URLRequest, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable { + func downloadResponse(request: URLRequest, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable { let observables = downloadResponse(request: request) let progressObservable = observables .0 - .map { DownloadEvent.progress(progress: $0) } + .map { HTTPDownloadRequestEvent.progress(progress: $0) } .asObservable() let responseObservable = observables .1 .decodable(E.self, apiErrorType: AE.self) - .map { DownloadEvent.completedWithData(data: $0.data) } + .map { HTTPDownloadRequestEvent.completedWithData(data: $0.data) } .asObservable() let mergedObservable = responseObservable.merge(with: progressObservable) return mergedObservable @@ -37,20 +38,20 @@ extension Reactive where Base: URLSession { /// - Parameters: /// - request: `URLRequest` used to create upload task and its observables. /// - url: `URL` used to save downloaded file to disk. - /// - httpErrorType: `HTTPErrorBody` http error body type. - /// - apiErrorType: `NetworkAPIError` type for expected error in HTTP response body. + /// - httpErrorType: `HTTPBodyError` http body error type. + /// - apiErrorType: `HTTPAPIError` type for expected error in HTTP response body. /// /// - Returns: a `Observable` object of `DownloadEvent` type. - func downloadResponse(request: URLRequest, saveTo url: URL, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable { + func downloadResponse(request: URLRequest, saveTo url: URL, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable { let observables = downloadResponse(request: request, saveTo: url) let progressObservable = observables .0 - .map { DownloadEvent.progress(progress: $0) } + .map { HTTPDownloadRequestEvent.progress(progress: $0) } .asObservable() let responseObservable = observables .1 .decodable(E.self, apiErrorType: AE.self) - .map { _ in DownloadEvent.completed } + .map { _ in HTTPDownloadRequestEvent.completed } .asObservable() let mergedObservable = responseObservable.merge(with: progressObservable) return mergedObservable diff --git a/Source/Custom Requests/Upload/Reactive+URLSessionAdaptedUploadResponse.swift b/Source/HTTP/Extensions/Reactive+URLSessionAdaptedUploadResponse.swift similarity index 60% rename from Source/Custom Requests/Upload/Reactive+URLSessionAdaptedUploadResponse.swift rename to Source/HTTP/Extensions/Reactive+URLSessionAdaptedUploadResponse.swift index 88ae8a4..3eb02ba 100644 --- a/Source/Custom Requests/Upload/Reactive+URLSessionAdaptedUploadResponse.swift +++ b/Source/HTTP/Extensions/Reactive+URLSessionAdaptedUploadResponse.swift @@ -8,28 +8,29 @@ import Foundation import RxSwift import RxCocoa +import RxSwiftExt extension Reactive where Base: URLSession { /// Creates an observable with `UploadEvent` type. /// /// - Parameters: /// - request: `URLRequest` used to create upload task and its observables. - /// - file: `UploadFile` object to be uploaded. + /// - file: `HTTPUploadRequestFile` object to be uploaded. /// - modelType: `Decodable` type for model in HTTP response body. - /// - httpErrorType: `HTTPErrorBody` http error body type. - /// - apiErrorType: `NetworkAPIError` type for expected error in HTTP response body. + /// - httpErrorType: `HTTPBodyError` http body error type. + /// - apiErrorType: `HTTPAPIError` type for expected error in HTTP response body. /// /// - Returns: a `Observable` object of `UploadEvent` type. - func uploadResponse(request: URLRequest, file: UploadFile, modelType: T.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable> { + func uploadResponse(request: URLRequest, file: HTTPUploadRequestFile, modelType: T.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable> { let observables = uploadResponse(request: request, file: file) let progressObservable = observables .0 - .map { UploadEvent.progress(progress: $0) } + .map { HTTPUploadRequestEvent.progress(progress: $0) } .asObservable() let responseObservable = observables .1 .decodable(T.self, httpErrorType: E.self, apiErrorType: AE.self) - .map { UploadEvent.completed(model: $0) } + .map { HTTPUploadRequestEvent.completed(model: $0) } .asObservable() let mergedObservable = responseObservable.merge(with: progressObservable) return mergedObservable @@ -38,22 +39,22 @@ extension Reactive where Base: URLSession { /// /// - Parameters: /// - request: `URLRequest` used to create upload task and its observables. - /// - formData: `UploadFormData` object that includes parameters and files to be uploaded. + /// - formData: `HTTPUploadRequestFormData` object that includes parameters and files to be uploaded. /// - modelType: `Decodable` type for model in HTTP response body. - /// - httpErrorType: `HTTPErrorBody` http error body type. - /// - apiErrorType: `NetworkAPIError` type for expected error in HTTP response body. + /// - httpErrorType: `HTTPBodyError` http body error type. + /// - apiErrorType: `HTTPAPIError` type for expected error in HTTP response body. /// /// - Returns: a `Observable` object of `UploadEvent` type. - func uploadResponse(request: URLRequest, formData: UploadFormData, modelType: T.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable> { + func uploadResponse(request: URLRequest, formData: HTTPUploadRequestFormData, modelType: T.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Observable> { let observables = uploadResponse(request: request, formData: formData) let progressObservable = observables .0 - .map { UploadEvent.progress(progress: $0) } + .map { HTTPUploadRequestEvent.progress(progress: $0) } .asObservable() let responseObservable = observables .1 .decodable(T.self, httpErrorType: E.self, apiErrorType: AE.self) - .map { UploadEvent.completed(model: $0) } + .map { HTTPUploadRequestEvent.completed(model: $0) } .asObservable() let mergedObservable = responseObservable.merge(with: progressObservable) return mergedObservable diff --git a/Source/Custom Requests/Download/Reactive+URLSessionDownloadResponse.swift b/Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift similarity index 98% rename from Source/Custom Requests/Download/Reactive+URLSessionDownloadResponse.swift rename to Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift index c6fa24c..6eef6df 100644 --- a/Source/Custom Requests/Download/Reactive+URLSessionDownloadResponse.swift +++ b/Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift @@ -65,7 +65,7 @@ extension Reactive where Base: URLSession { /// /// - Parameters: /// - request: `URLRequest` used to create upload task and its observables. - /// - formData: `UploadFormData` object that includes parameters and files to be uploaded. + /// - url: `URL` path that the downloaded file will be downloaded to. /// /// - Returns: a tuple of progress `PublishSubject` and response and data `Single`. func downloadResponse(request: URLRequest, saveTo url: URL) -> (PublishSubject, Single<(response: HTTPURLResponse, data: Data)>) { diff --git a/Source/Custom Requests/Upload/Reactive+URLSessionUploadResponse.swift b/Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift similarity index 90% rename from Source/Custom Requests/Upload/Reactive+URLSessionUploadResponse.swift rename to Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift index f8a5bb0..97e0244 100644 --- a/Source/Custom Requests/Upload/Reactive+URLSessionUploadResponse.swift +++ b/Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift @@ -15,10 +15,10 @@ extension Reactive where Base: URLSession { /// /// - Parameters: /// - request: `URLRequest` used to create upload task and its observables. - /// - file: `UploadFile` object to be uploaded. + /// - file: `HTTPUploadRequestFile` object to be uploaded. /// /// - Returns: a tuple of progress `PublishSubject` and response and data `Single`. - func uploadResponse(request: URLRequest, file: UploadFile) -> (PublishSubject, Single<(response: HTTPURLResponse, data: Data)>) { + func uploadResponse(request: URLRequest, file: HTTPUploadRequestFile) -> (PublishSubject, Single<(response: HTTPURLResponse, data: Data)>) { // we must keep refernce to task progress observation object var taskProgressObservation: NSKeyValueObservation? let taskProgressSubject = PublishSubject() @@ -66,10 +66,10 @@ extension Reactive where Base: URLSession { /// /// - Parameters: /// - request: `URLRequest` used to create upload task and its observables. - /// - formData: `UploadFormData` object that includes parameters and files to be uploaded. + /// - formData: `HTTPUploadRequestFormData` object that includes parameters and files to be uploaded. /// /// - Returns: a tuple of progress `PublishSubject` and response and data `Single`. - func uploadResponse(request: URLRequest, formData: UploadFormData) -> (PublishSubject, Single<(response: HTTPURLResponse, data: Data)>) { + func uploadResponse(request: URLRequest, formData: HTTPUploadRequestFormData) -> (PublishSubject, Single<(response: HTTPURLResponse, data: Data)>) { // we must keep refernce to task progress observation object var taskProgressObservation: NSKeyValueObservation? let taskProgressSubject = PublishSubject() diff --git a/Source/Web Socket/Extensions/Reactive+ping.swift b/Source/HTTP/Extensions/Reactive+WebSocketPing.swift similarity index 95% rename from Source/Web Socket/Extensions/Reactive+ping.swift rename to Source/HTTP/Extensions/Reactive+WebSocketPing.swift index deea8d2..c2489bb 100644 --- a/Source/Web Socket/Extensions/Reactive+ping.swift +++ b/Source/HTTP/Extensions/Reactive+WebSocketPing.swift @@ -1,5 +1,5 @@ // -// Reactive+ping.swift +// Reactive+WebSocketPing.swift // RxNetworkKit // // Created by Loay Ashraf on 04/10/2023. diff --git a/Source/Web Socket/Extensions/Reactive+receive.swift b/Source/HTTP/Extensions/Reactive+WebSocketReceive.swift similarity index 97% rename from Source/Web Socket/Extensions/Reactive+receive.swift rename to Source/HTTP/Extensions/Reactive+WebSocketReceive.swift index 988d0b9..38ac351 100644 --- a/Source/Web Socket/Extensions/Reactive+receive.swift +++ b/Source/HTTP/Extensions/Reactive+WebSocketReceive.swift @@ -1,5 +1,5 @@ // -// Reactive+receive.swift +// Reactive+WebSocketReceive.swift // RxNetworkKit // // Created by Loay Ashraf on 04/10/2023. diff --git a/Source/Web Socket/Extensions/Reactive+send.swift b/Source/HTTP/Extensions/Reactive+WebSocketSend.swift similarity index 95% rename from Source/Web Socket/Extensions/Reactive+send.swift rename to Source/HTTP/Extensions/Reactive+WebSocketSend.swift index 693ae8b..35e69d0 100644 --- a/Source/Web Socket/Extensions/Reactive+send.swift +++ b/Source/HTTP/Extensions/Reactive+WebSocketSend.swift @@ -1,5 +1,5 @@ // -// Reactive+send.swift +// Reactive+WebSocketSend.swift.swift // RxNetworkKit // // Created by Loay Ashraf on 04/10/2023. diff --git a/Source/Custom Requests/Download/URLSession+DownloadTask.swift b/Source/HTTP/Extensions/URLSession+DownloadTask.swift similarity index 100% rename from Source/Custom Requests/Download/URLSession+DownloadTask.swift rename to Source/HTTP/Extensions/URLSession+DownloadTask.swift diff --git a/Source/Custom Requests/Upload/URLSession+UploadTask.swift b/Source/HTTP/Extensions/URLSession+UploadTask.swift similarity index 81% rename from Source/Custom Requests/Upload/URLSession+UploadTask.swift rename to Source/HTTP/Extensions/URLSession+UploadTask.swift index 91995e3..7562371 100644 --- a/Source/Custom Requests/Upload/URLSession+UploadTask.swift +++ b/Source/HTTP/Extensions/URLSession+UploadTask.swift @@ -12,11 +12,11 @@ extension URLSession { /// /// - Parameters: /// - request: `URLRequest` used to create data task. - /// - file: `File` object that includes name, data, url and HTTP MIME type. + /// - file: `HTTPUploadRequestFile` object that includes name, data, url and HTTP MIME type. /// - completionHandler: completion handler to be called on task completion. /// /// - Returns: upload`URLSessionDataTask` created using given request and file. - func fileUploadTask(with request: URLRequest, from file: UploadFile, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { + func fileUploadTask(with request: URLRequest, from file: HTTPUploadRequestFile, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { let fileData = extractFileData(file) let request = adaptUploadRequest(originalRequest: request, withContentType: file.mimeType.rawValue, withBody: fileData) let task = dataTask(with: request, completionHandler: completionHandler) @@ -26,11 +26,11 @@ extension URLSession { /// /// - Parameters: /// - request: `URLRequest` used to create data task. - /// - formData: `FormData` object that includes text data fields and files to be uploaded. + /// - formData: `HTTPUploadRequestFormData` object that includes text data fields and files to be uploaded. /// - completionHandler: completion handler to be called on task completion. /// /// - Returns: upload`URLSessionDataTask` created using given request and form data. - func formDataUploadTask(with request: URLRequest, from formData: UploadFormData, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { + func formDataUploadTask(with request: URLRequest, from formData: HTTPUploadRequestFormData, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { let boundary = generateFormBoundary() let dataBody = createFormDataBody(formData: formData, boundary: boundary) let request = adaptUploadRequest(originalRequest: request, withContentType: "multipart/form-data; boundary=\(boundary)", withBody: dataBody) @@ -57,10 +57,10 @@ extension URLSession { /// Exxtracts data from file object. /// - /// - Parameter file: `File` object that includes data or url. + /// - Parameter file: `HTTPUploadRequestFile` object that includes data or url. /// /// - Returns: `Data` object representing the file. - fileprivate func extractFileData(_ file: UploadFile) -> Data? { + fileprivate func extractFileData(_ file: HTTPUploadRequestFile) -> Data? { var data: Data? = nil if let fileData = file.data { data = fileData @@ -73,11 +73,11 @@ extension URLSession { /// Creates HTTP body data using given form data and boundary. /// /// - Parameters: - /// - formData: `FormData` object used to create HTTP body. + /// - formData: `HTTPUploadRequestFormData` object used to create HTTP body. /// - boundary: `String` boundary used to mark start of body section. /// /// - Returns: `Data` object representing HTTP body. - fileprivate func createFormDataBody(formData: UploadFormData, boundary: String) -> Data { + fileprivate func createFormDataBody(formData: HTTPUploadRequestFormData, boundary: String) -> Data { let files = formData.files let lineBreak = "\r\n" var body = Data() diff --git a/Source/Manager/URLSessionConfiguration+setAdditionalHTTPHeader.swift b/Source/HTTP/Extensions/URLSessionConfiguration+setAdditionalHTTPHeader.swift similarity index 100% rename from Source/Manager/URLSessionConfiguration+setAdditionalHTTPHeader.swift rename to Source/HTTP/Extensions/URLSessionConfiguration+setAdditionalHTTPHeader.swift diff --git a/Source/Manager/URLSessionConfiguration+setUserAgentHTTPHeader.swift b/Source/HTTP/Extensions/URLSessionConfiguration+setUserAgentHTTPHeader.swift similarity index 92% rename from Source/Manager/URLSessionConfiguration+setUserAgentHTTPHeader.swift rename to Source/HTTP/Extensions/URLSessionConfiguration+setUserAgentHTTPHeader.swift index ae43e45..05b5908 100644 --- a/Source/Manager/URLSessionConfiguration+setUserAgentHTTPHeader.swift +++ b/Source/HTTP/Extensions/URLSessionConfiguration+setUserAgentHTTPHeader.swift @@ -11,7 +11,7 @@ extension URLSessionConfiguration { /// Sets `User-Agent` header as an additional HTTP header. func setUserAgentHTTPHeader() { let mainBundle = Bundle.main - let frameworkBundle = Bundle.init(for: NetworkManager.self) + let frameworkBundle = Bundle.init(for: HTTPClient.self) let mainBundleIndentifier = mainBundle.bundleIdentifier ?? "Unknown Client Identifier" let frameworkBundleVersion = frameworkBundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String let osName = ProcessInfo.processInfo.operatingSystemName diff --git a/Source/HTTP/HTTPErrorBody.swift b/Source/HTTP/HTTPErrorBody.swift deleted file mode 100644 index 7b3ce1f..0000000 --- a/Source/HTTP/HTTPErrorBody.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// HTTPErrorBody.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 21/08/2023. -// - -import Foundation - -/// HTTP error body type. -public protocol HTTPErrorBody: Decodable { } diff --git a/Source/HTTP/HTTPMethod.swift b/Source/HTTP/HTTPMethod.swift deleted file mode 100644 index cb23363..0000000 --- a/Source/HTTP/HTTPMethod.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// HTTPMethod.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 20/03/2023. -// - -/// An enumeration of the types of http methods. -public enum HTTPMethod: String { - case get = "GET" - case put = "PUT" - case post = "POST" - case delete = "DELETE" - case head = "HEAD" - case options = "OPTIONS" - case trace = "TRACE" - case connect = "CONNECT" -} diff --git a/Source/HTTP/HTTPScheme.swift b/Source/HTTP/HTTPScheme.swift deleted file mode 100644 index d807277..0000000 --- a/Source/HTTP/HTTPScheme.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// HTTPScheme.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 20/03/2023. -// - -/// An enumeration of the types of http schemes. -public enum HTTPScheme: String { - case http = "http://" - case https = "https://" -} diff --git a/Source/HTTP/HTTPStatusCode.swift b/Source/HTTP/HTTPStatusCode.swift deleted file mode 100644 index 819121a..0000000 --- a/Source/HTTP/HTTPStatusCode.swift +++ /dev/null @@ -1,193 +0,0 @@ -// -// HTTPStatusCode.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 16/02/2023. -// - -import Foundation - -/// This is a list of Hypertext Transfer Protocol (HTTP) response status codes. -/// It includes codes from IETF internet standards, other IETF RFCs, other specifications, and some additional commonly used codes. -/// The first digit of the status code specifies one of five classes of response; an HTTP client must recognise these five classes at a minimum. -public enum HTTPStatusCode: Int, Error { - /// The response class representation of status codes, these get grouped by their first digit. - enum ResponseType { - /// - 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. - case informational - /// - success: This class of status codes indicates the action requested by the client was received, understood, accepted, and processed successfully. - case success - /// - redirection: This class of status code indicates the client must take additional action to complete the request. - case redirection - /// - clientError: This class of status code is intended for situations in which the client seems to have erred. - case clientError - /// - serverError: This class of status code indicates the server failed to fulfill an apparently valid request. - case serverError - /// - undefined: The class of the status code cannot be resolved. - case undefined - } - // - // Informational - 1xx - // - /// - continue: The server has received the request headers and the client should proceed to send the request body. - case `continue` = 100 - /// - switchingProtocols: The requester has asked the server to switch protocols and the server has agreed to do so. - case switchingProtocols = 101 - /// - processing: This code indicates that the server has received and is processing the request, but no response is available yet. - case processing = 102 - // - // Success - 2xx - // - /// - ok: Standard response for successful HTTP requests. - case ok = 200 - /// - created: The request has been fulfilled, resulting in the creation of a new resource. - case created = 201 - /// - accepted: The request has been accepted for processing, but the processing has not been completed. - case accepted = 202 - /// - nonAuthoritativeInformation: The server is a transforming proxy (e.g. a Web accelerator) that received a 200 OK from its origin, but is returning a modified version of the origin's response. - case nonAuthoritativeInformation = 203 - /// - noContent: The server successfully processed the request and is not returning any content. - case noContent = 204 - /// - resetContent: The server successfully processed the request, but is not returning any content. - case resetContent = 205 - /// - partialContent: The server is delivering only part of the resource (byte serving) due to a range header sent by the client. - case partialContent = 206 - /// - 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. - case multiStatus = 207 - /// - alreadyReported: The members of a DAV binding have already been enumerated in a previous reply to this request, and are not being included again. - case alreadyReported = 208 - /// - 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. - case IMUsed = 226 - // - // Redirection - 3xx - // - /// - multipleChoices: Indicates multiple options for the resource from which the client may choose - case multipleChoices = 300 - /// - movedPermanently: This and all future requests should be directed to the given URI. - case movedPermanently = 301 - /// - found: The resource was found. - case found = 302 - /// - seeOther: The response to the request can be found under another URI using a GET method. - case seeOther = 303 - /// - notModified: Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-None-Match. - case notModified = 304 - /// - useProxy: The requested resource is available only through a proxy, the address for which is provided in the response. - case useProxy = 305 - /// - switchProxy: No longer used. Originally meant "Subsequent requests should use the specified proxy. - case switchProxy = 306 - /// - temporaryRedirect: The request should be repeated with another URI. - case temporaryRedirect = 307 - /// - permenantRedirect: The request and all future requests should be repeated using another URI. - case permenantRedirect = 308 - // - // Client Error - 4xx - // - /// - badRequest: The server cannot or will not process the request due to an apparent client error. - case badRequest = 400 - /// - unauthorized: Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. - case unauthorized = 401 - /// - paymentRequired: The content available on the server requires payment. - case paymentRequired = 402 - /// - forbidden: The request was a valid request, but the server is refusing to respond to it. - case forbidden = 403 - /// - notFound: The requested resource could not be found but may be available in the future. - case notFound = 404 - /// - 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 - case methodNotAllowed = 405 - /// - notAcceptable: The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request. - case notAcceptable = 406 - /// - proxyAuthenticationRequired: The client must first authenticate itself with the proxy. - case proxyAuthenticationRequired = 407 - /// - requestTimeout: The server timed out waiting for the request. - case requestTimeout = 408 - /// - conflict: Indicates that the request could not be processed because of conflict in the request, such as an edit conflict between multiple simultaneous updates. - case conflict = 409 - /// - gone: Indicates that the resource requested is no longer available and will not be available again. - case gone = 410 - /// - lengthRequired: The request did not specify the length of its content, which is required by the requested resource. - case lengthRequired = 411 - /// - preconditionFailed: The server does not meet one of the preconditions that the requester put on the request. - case preconditionFailed = 412 - /// - payloadTooLarge: The request is larger than the server is willing or able to process. - case payloadTooLarge = 413 - /// - URITooLong: The URI provided was too long for the server to process. - case URITooLong = 414 - /// - unsupportedMediaType: The request entity has a media type which the server or resource does not support. - case unsupportedMediaType = 415 - /// - rangeNotSatisfiable: The client has asked for a portion of the file (byte serving), but the server cannot supply that portion. - case rangeNotSatisfiable = 416 - /// - expectationFailed: The server cannot meet the requirements of the Expect request-header field. - case expectationFailed = 417 - /// - teapot: This HTTP status is used as an Easter egg in some websites. - case teapot = 418 - /// - misdirectedRequest: The request was directed at a server that is not able to produce a response. - case misdirectedRequest = 421 - /// - unprocessableEntity: The request was well-formed but was unable to be followed due to semantic errors. - case unprocessableEntity = 422 - /// - locked: The resource that is being accessed is locked. - case locked = 423 - /// - failedDependency: The request failed due to failure of a previous request (e.g., a PROPPATCH). - case failedDependency = 424 - /// - upgradeRequired: The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field. - case upgradeRequired = 426 - /// - preconditionRequired: The origin server requires the request to be conditional. - case preconditionRequired = 428 - /// - tooManyRequests: The user has sent too many requests in a given amount of time. - case tooManyRequests = 429 - /// - requestHeaderFieldsTooLarge: The server is unwilling to process the request because either an individual header field, or all the header fields collectively, are too large. - case requestHeaderFieldsTooLarge = 431 - /// - noResponse: Used to indicate that the server has returned no information to the client and closed the connection. - case noResponse = 444 - /// - 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. - case unavailableForLegalReasons = 451 - /// - SSLCertificateError: An expansion of the 400 Bad Request response code, used when the client has provided an invalid client certificate. - case SSLCertificateError = 495 - /// - SSLCertificateRequired: An expansion of the 400 Bad Request response code, used when a client certificate is required but not provided. - case SSLCertificateRequired = 496 - /// - 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. - case HTTPRequestSentToHTTPSPort = 497 - /// - clientClosedRequest: Used when the client has closed the request before the server could send a response. - case clientClosedRequest = 499 - // - // Server Error - 5xx - // - /// - internalServerError: A generic error message, given when an unexpected condition was encountered and no more specific message is suitable. - case internalServerError = 500 - /// - notImplemented: The server either does not recognize the request method, or it lacks the ability to fulfill the request. - case notImplemented = 501 - /// - badGateway: The server was acting as a gateway or proxy and received an invalid response from the upstream server. - case badGateway = 502 - /// - serviceUnavailable: The server is currently unavailable (because it is overloaded or down for maintenance). Generally, this is a temporary state. - case serviceUnavailable = 503 - /// - gatewayTimeout: The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. - case gatewayTimeout = 504 - /// - HTTPVersionNotSupported: The server does not support the HTTP protocol version used in the request. - case HTTPVersionNotSupported = 505 - /// - variantAlsoNegotiates: Transparent content negotiation for the request results in a circular reference. - case variantAlsoNegotiates = 506 - /// - insufficientStorage: The server is unable to store the representation needed to complete the request. - case insufficientStorage = 507 - /// - loopDetected: The server detected an infinite loop while processing the request. - case loopDetected = 508 - /// - notExtended: Further extensions to the request are required for the server to fulfill it. - case notExtended = 510 - /// - networkAuthenticationRequired: The client needs to authenticate to gain network access. - case networkAuthenticationRequired = 511 - /// The class (or group) which the status code belongs to. - var responseType: ResponseType { - switch self.rawValue { - case 100..<200: - return .informational - case 200..<300: - return .success - case 300..<400: - return .redirection - case 400..<500: - return .clientError - case 500..<600: - return .serverError - default: - return .undefined - } - } -} diff --git a/Source/HTTP/HTTPURLResponse+StatusCode.swift b/Source/HTTP/HTTPURLResponse+StatusCode.swift deleted file mode 100644 index 5056e38..0000000 --- a/Source/HTTP/HTTPURLResponse+StatusCode.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// HTTPURLResponse+StatusCode.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 25/03/2023. -// - -import Foundation - -extension HTTPURLResponse { - var status: HTTPStatusCode? { - return HTTPStatusCode(rawValue: statusCode) - } -} diff --git a/Source/HTTP/Types/Client/HTTPClient.swift b/Source/HTTP/Types/Client/HTTPClient.swift new file mode 100644 index 0000000..a659e11 --- /dev/null +++ b/Source/HTTP/Types/Client/HTTPClient.swift @@ -0,0 +1,147 @@ +// +// HTTPClient.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 10/10/2023. +// + +import Foundation +import RxSwift + +/// Entry point for creating http requests. +public class HTTPClient { + + /// Principal `Session` object that encapsulates `URLSession` instance. + private let session: Session + /// Principal `URLSession` object used to create request tasks. + private var urlSession: URLSession { + session.urlSession + } + /// Principal `HTTPRequestInterceptor` object used to intercept requests. + private let requestInterceptor: HTTPRequestInterceptor + + /// Creates a `RESTClient` instance. + /// + /// - Parameters: + /// - session: `Session` object that encapsulates `URLSession` instance. + /// - requestInterceptor: `HTTPRequestInterceptor` object used for intercepting requests. + public init(session: Session, requestInterceptor: HTTPRequestInterceptor) { + // Initialize manager's properties. + self.session = session + self.requestInterceptor = requestInterceptor + } + + /// Creates a `Observable` object encapsulating download request using given `Router`. + /// Use this method if you are downloading a relatively small file (keeps data in memory). + /// + /// - Parameters: + /// - router: `HTTPDownloadRequestRouter` object used to create request. + /// - httpErrorType: `HTTPBodyError` http body error type. + /// - apiErrorType: `HTTPAPIError` type expected to be received in response body. + /// + /// - Returns: `Observable` object encapsulating download request. + public func download(_ router: HTTPDownloadRequestRouter, _ httpErrorType: E.Type = DefaultHTTPBodyError.self, _ apiErrorType: AE.Type = DefaultHTTPAPIError.self) -> Observable { + let originalRequest = router.asURLRequest() + let adaptedRequest = requestInterceptor.adapt(originalRequest, for: urlSession) + let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: urlSession) + let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: urlSession) + let shouldRetry = { (error: HTTPError) in + self.requestInterceptor.shouldRetry(adaptedRequest, for: self.urlSession, dueTo: error) + } + let observable = urlSession + .rx + .downloadResponse(request: adaptedRequest, httpErrorType: E.self, apiErrorType: AE.self) + .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) + return observable + } + + /// Creates a `Observable` object encapsulating download request using given `Router`. + /// Use this method if you are downloading a relatively small file (saves file to disk). + /// + /// - Parameters: + /// - router: `HTTPDownloadRequestRouter` object used to create request. + /// - fileURL: `URL` used to save downloaded file to disk. + /// - httpErrorType: `HTTPBodyError` http body error type. + /// - apiErrorType: `HTTPAPIError` type expected to be received in response body. + /// + /// - Returns: `Observable` object encapsulating download request. + public func download(_ router: HTTPDownloadRequestRouter, _ fileURL: URL, _ httpErrorType: E.Type = DefaultHTTPBodyError.self, _ apiErrorType: AE.Type = DefaultHTTPAPIError.self) -> Observable { + let originalRequest = router.asURLRequest() + let adaptedRequest = requestInterceptor.adapt(originalRequest, for: urlSession) + let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: urlSession) + let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: urlSession) + let shouldRetry = { (error: HTTPError) in + self.requestInterceptor.shouldRetry(adaptedRequest, for: self.urlSession, dueTo: error) + } + let observable = urlSession + .rx + .downloadResponse(request: adaptedRequest, saveTo: fileURL, httpErrorType: E.self, apiErrorType: AE.self) + .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) + return observable + } + + /// Creates a `Observable` object encapsulating upload request using given `Router`. + /// Use this method if you are uploading single file at a time. + /// + /// - Parameters: + /// - router: `HTTPUploadRequestRouter` object used to create request. + /// - file: `HTTPUploadRequestFile` object including file details for upload. + /// - httpErrorType: `HTTPBodyError` http body error type. + /// - apiErrorType: `HTTPAPIError` type expected to be received in response body. + /// + /// - Returns: `Observable` object encapsulating upload request. + public func upload(_ router: HTTPUploadRequestRouter, _ file: HTTPUploadRequestFile, _ httpErrorType: E.Type = DefaultHTTPBodyError.self, _ apiErrorType: AE.Type = DefaultHTTPAPIError.self) -> Observable> { + let originalRequest = router.asURLRequest() + let adaptedRequest = requestInterceptor.adapt(originalRequest, for: urlSession) + let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: urlSession) + let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: urlSession) + let shouldRetry = { (error: HTTPError) in + self.requestInterceptor.shouldRetry(adaptedRequest, for: self.urlSession, dueTo: error) + } + let observable = urlSession + .rx + .uploadResponse(request: adaptedRequest, file: file, modelType: T.self, httpErrorType: E.self, apiErrorType: AE.self) + .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) + return observable + } + + /// Creates a `Observable` object encapsulating upload request using given `Router`. + /// Use this method if you are uploading multiple files at a time. + /// + /// - Parameters: + /// - router: `HTTPUploadRequestRouter` object used to create request. + /// - formData: `HTTPUploadRequestFormData` object including parameters and files for upload. + /// - httpErrorType: `HTTPBodyError` http body error type. + /// - apiErrorType: `HTTPAPIError` type expected to be received in response body. + /// + /// - Returns: `Observable` object encapsulating upload request. + public func upload(_ router: HTTPUploadRequestRouter, _ formData: HTTPUploadRequestFormData, _ httpErrorType: E.Type = DefaultHTTPBodyError.self, _ apiErrorType: AE.Type = DefaultHTTPAPIError.self) -> Observable> { + let originalRequest = router.asURLRequest() + let adaptedRequest = requestInterceptor.adapt(originalRequest, for: urlSession) + let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: urlSession) + let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: urlSession) + let shouldRetry = { (error: HTTPError) in + self.requestInterceptor.shouldRetry(adaptedRequest, for: self.urlSession, dueTo: error) + } + let observable = urlSession + .rx + .uploadResponse(request: adaptedRequest, formData: formData, modelType: T.self, httpErrorType: E.self, apiErrorType: AE.self) + .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) + return observable + } + + /// Creates websocket object and establishes connection using provided url and protocols. + /// + /// - Parameters: + /// - url: `URL` of websocket server. + /// - protocols: `[String]` websocket connection protocols. + /// - closeHandler: `WebSocketCloseHandler` used to provide close coda and reason upon connection closure. + /// + /// - Returns: `WebSocket` object that represents the connection. + public func webSocket(_ url: URL, _ protocols: [String], _ closeHandler: WebSocketCloseHandler) -> WebSocket { + let task = urlSession.webSocketTask(with: url, protocols: protocols) + let webSocket = WebSocket(task: task, closeHandler: closeHandler) + return webSocket + } + +} diff --git a/Source/Custom Requests/Download/DownloadEvent.swift b/Source/HTTP/Types/Request/Event/HTTPDownloadRequestEvent.swift similarity index 78% rename from Source/Custom Requests/Download/DownloadEvent.swift rename to Source/HTTP/Types/Request/Event/HTTPDownloadRequestEvent.swift index d02a00b..91cbb1e 100644 --- a/Source/Custom Requests/Download/DownloadEvent.swift +++ b/Source/HTTP/Types/Request/Event/HTTPDownloadRequestEvent.swift @@ -1,5 +1,5 @@ // -// DownloadEvent.swift +// HTTPDownloadRequestEvent.swift // RxNetworkKit // // Created by Loay Ashraf on 25/03/2023. @@ -8,7 +8,7 @@ import Foundation /// An enumeration of the types of events received during a download operation. -public enum DownloadEvent { +public enum HTTPDownloadRequestEvent { case completed case completedWithData(data: Data?) case progress(progress: Progress) diff --git a/Source/Custom Requests/Upload/UploadEvent.swift b/Source/HTTP/Types/Request/Event/HTTPUploadRequestEvent.swift similarity index 73% rename from Source/Custom Requests/Upload/UploadEvent.swift rename to Source/HTTP/Types/Request/Event/HTTPUploadRequestEvent.swift index 4f2e711..8089d82 100644 --- a/Source/Custom Requests/Upload/UploadEvent.swift +++ b/Source/HTTP/Types/Request/Event/HTTPUploadRequestEvent.swift @@ -1,5 +1,5 @@ // -// UploadEvent.swift +// HTTPUploadRequestEvent.swift // RxNetworkKit // // Created by Loay Ashraf on 22/03/2023. @@ -8,7 +8,7 @@ import Foundation /// An enumeration of the types of events received during an upload operation. -public enum UploadEvent { +public enum HTTPUploadRequestEvent { case completed(model: T) case progress(progress: Progress) } diff --git a/Source/HTTP/HTTPMIMEType.swift b/Source/HTTP/Types/Request/Parameters/HTTPMIMEType.swift similarity index 100% rename from Source/HTTP/HTTPMIMEType.swift rename to Source/HTTP/Types/Request/Parameters/HTTPMIMEType.swift diff --git a/Source/Custom Requests/Upload/UploadFile.swift b/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFile.swift similarity index 95% rename from Source/Custom Requests/Upload/UploadFile.swift rename to Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFile.swift index c7b4e03..10d84c6 100644 --- a/Source/Custom Requests/Upload/UploadFile.swift +++ b/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFile.swift @@ -1,5 +1,5 @@ // -// UploadFile.swift +// HTTPUploadRequestFile.swift // RxNetworkKit // // Created by Loay Ashraf on 24/03/2023. @@ -8,7 +8,7 @@ import Foundation /// Holds file details for multipart form upload, -public struct UploadFile { +public struct HTTPUploadRequestFile { /// key used for file record. let key: String diff --git a/Source/Custom Requests/Upload/UploadFormData.swift b/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFormData.swift similarity index 72% rename from Source/Custom Requests/Upload/UploadFormData.swift rename to Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFormData.swift index 866a569..e4f0a7e 100644 --- a/Source/Custom Requests/Upload/UploadFormData.swift +++ b/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFormData.swift @@ -1,5 +1,5 @@ // -// UploadFormData.swift +// HTTPUploadRequestFormData.swift // RxNetworkKit // // Created by Loay Ashraf on 24/03/2023. @@ -8,11 +8,11 @@ import Foundation /// Holds details for a mulipart form upload. -public struct UploadFormData { +public struct HTTPUploadRequestFormData { /// parameters (text data fields) to be included in the form HTTP body. let parameters: [String: String] /// files to be included in the form HTTP body. - let files: [UploadFile] + let files: [HTTPUploadRequestFile] } diff --git a/Source/Router/NetworkDownloadRouter.swift b/Source/HTTP/Types/Request/Router/HTTPDownloadRequestRouter.swift similarity index 70% rename from Source/Router/NetworkDownloadRouter.swift rename to Source/HTTP/Types/Request/Router/HTTPDownloadRequestRouter.swift index d8353bb..9bfd6e8 100644 --- a/Source/Router/NetworkDownloadRouter.swift +++ b/Source/HTTP/Types/Request/Router/HTTPDownloadRequestRouter.swift @@ -1,5 +1,5 @@ // -// NetworkDownloadRouter.swift +// HTTPDownloadRequestRouter.swift // RxNetworkKit // // Created by Loay Ashraf on 25/03/2023. @@ -7,10 +7,11 @@ import Foundation + /// Holds download request details. -public protocol NetworkDownloadRouter: NetworkRouter { } +public protocol HTTPDownloadRequestRouter: HTTPRequestRouter { } -public extension NetworkDownloadRouter { +public extension HTTPDownloadRequestRouter { /// By Default: HTTP method is GET for download requests. var method: HTTPMethod { diff --git a/Source/Router/NetworkUploadRouter.swift b/Source/HTTP/Types/Request/Router/HTTPUploadRequestRouter.swift similarity index 73% rename from Source/Router/NetworkUploadRouter.swift rename to Source/HTTP/Types/Request/Router/HTTPUploadRequestRouter.swift index e60d9d0..49fb0e4 100644 --- a/Source/Router/NetworkUploadRouter.swift +++ b/Source/HTTP/Types/Request/Router/HTTPUploadRequestRouter.swift @@ -1,5 +1,5 @@ // -// NetworkUploadRouter.swift +// HTTPUploadRequestRouter.swift // RxNetworkKit // // Created by Loay Ashraf on 24/03/2023. @@ -8,9 +8,9 @@ import Foundation /// Holds upload request details. -public protocol NetworkUploadRouter: NetworkRouter { } +public protocol HTTPUploadRequestRouter: HTTPRequestRouter { } -public extension NetworkUploadRouter { +public extension HTTPUploadRequestRouter { /// By Default: HTTP method is `POST` for upload requests. var method: HTTPMethod { .post diff --git a/Source/Web Socket/WebSocket.swift b/Source/HTTP/Types/Web Socket/WebSocket.swift similarity index 100% rename from Source/Web Socket/WebSocket.swift rename to Source/HTTP/Types/Web Socket/WebSocket.swift diff --git a/Source/Web Socket/WebSocketCloseCode.swift b/Source/HTTP/Types/Web Socket/WebSocketCloseCode.swift similarity index 100% rename from Source/Web Socket/WebSocketCloseCode.swift rename to Source/HTTP/Types/Web Socket/WebSocketCloseCode.swift diff --git a/Source/Web Socket/WebSocketCloseHandler.swift b/Source/HTTP/Types/Web Socket/WebSocketCloseHandler.swift similarity index 100% rename from Source/Web Socket/WebSocketCloseHandler.swift rename to Source/HTTP/Types/Web Socket/WebSocketCloseHandler.swift diff --git a/Source/Web Socket/WebSocketError.swift b/Source/HTTP/Types/Web Socket/WebSocketError.swift similarity index 100% rename from Source/Web Socket/WebSocketError.swift rename to Source/HTTP/Types/Web Socket/WebSocketError.swift diff --git a/Source/Web Socket/WebSocketMessage.swift b/Source/HTTP/Types/Web Socket/WebSocketMessage.swift similarity index 100% rename from Source/Web Socket/WebSocketMessage.swift rename to Source/HTTP/Types/Web Socket/WebSocketMessage.swift diff --git a/Source/Manager/NetworkManager.swift b/Source/Manager/NetworkManager.swift deleted file mode 100644 index 9f294b8..0000000 --- a/Source/Manager/NetworkManager.swift +++ /dev/null @@ -1,202 +0,0 @@ -// -// NetworkManager.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 16/02/2023. -// - -import Foundation -import RxSwift -import RxSwiftExt - -/// Entry point for creating and managing network requests. -public class NetworkManager { - - /// Principal `URLSession` used to create request tasks. - private let session: URLSession - /// Principal `NetworkRequestInterceptor` used to intercept requests. - private let requestInterceptor: NetworkRequestInterceptor - /// Principal `NetworkEventMonitor` used to monitor request tasks. - private let eventMonitor: NetworkEventMonitor - - /// Creates a `NetworkManager` instance. - /// - /// - Parameters: - /// - configuration: `URLSessionConfiguration` object used to create `URLSession` instance. - /// - requestInterceptor: `NetworkRequestInterceptor` object used for intercepting requests. - /// - eventMonitor: `NetworkEventMonitor` object for monitoring events for session. - public init(configuration: URLSessionConfiguration, requestInterceptor: NetworkRequestInterceptor, eventMonitor: NetworkEventMonitor) { - // Apply User-Agent header as a part of HTTP aditional headers. - configuration.setUserAgentHTTPHeader() - // Initialize manager's properties. - URLSession.rx.shouldLogRequest = { _ in false } - self.session = .init(configuration: configuration, delegate: eventMonitor, delegateQueue: nil) - self.requestInterceptor = requestInterceptor - self.eventMonitor = eventMonitor - } - - /// Creates a `Completable` observable encapsulating data request using given `Router`. - /// Use this method if you are expecting empty response body. - /// - /// - Parameters: - /// - router: `Router` object used to create request. - /// - httpErrorType: `HTTPErrorBody` http error body type. - /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. - /// - /// - Returns: `Completable` observable encapsulating data request. - public func request(_ router: NetworkRouter, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Completable { - - let originalRequest = router.asURLRequest() - let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) - let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: session) - let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: session) - let shouldRetry = { (error: NetworkError) in - self.requestInterceptor.shouldRetry(adaptedRequest, for: self.session, dueTo: error) - } - let observable = session - .rx - .response(request: adaptedRequest) - .decodable(E.self, apiErrorType: AE.self) - .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) - return observable - } - - /// Creates a `Single` observable encapsulating data request using given `Router`. - /// Use this method if you are expecting data in response body. - /// - /// - Parameters: - /// - router: `Router` object used to create request. - /// - httpErrorType: `HTTPErrorBody` http error body type. - /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. - /// - /// - Returns: `Single` observable encapsulating data request. - public func request(_ router: NetworkRouter, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Single { - let originalRequest = router.asURLRequest() - let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) - let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: session) - let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: session) - let shouldRetry = { (error: NetworkError) in - self.requestInterceptor.shouldRetry(adaptedRequest, for: self.session, dueTo: error) - } - let observable = session - .rx - .response(request: adaptedRequest) - .decodable(T.self, httpErrorType: E.self, apiErrorType: AE.self) - .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) - return observable - } - - /// Creates a `Observable` object encapsulating download request using given `Router`. - /// Use this method if you are downloading a relatively small file (keeps data in memory). - /// - /// - Parameters: - /// - router: `Router` object used to create request. - /// - httpErrorType: `HTTPErrorBody` http error body type. - /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. - /// - /// - Returns: `Observable` object encapsulating download request. - public func download(_ router: NetworkRouter, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable { - let originalRequest = router.asURLRequest() - let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) - let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: session) - let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: session) - let shouldRetry = { (error: NetworkError) in - self.requestInterceptor.shouldRetry(adaptedRequest, for: self.session, dueTo: error) - } - let observable = session - .rx - .downloadResponse(request: adaptedRequest, httpErrorType: E.self, apiErrorType: AE.self) - .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) - return observable - } - - /// Creates a `Observable` object encapsulating download request using given `Router`. - /// Use this method if you are downloading a relatively small file (saves file to disk). - /// - /// - Parameters: - /// - router: `Router` object used to create request. - /// - fileURL: `URL` used to save downloaded file to disk. - /// - httpErrorType: `HTTPErrorBody` http error body type. - /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. - /// - /// - Returns: `Observable` object encapsulating download request. - public func download(_ router: NetworkRouter, _ fileURL: URL, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable { - let originalRequest = router.asURLRequest() - let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) - let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: session) - let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: session) - let shouldRetry = { (error: NetworkError) in - self.requestInterceptor.shouldRetry(adaptedRequest, for: self.session, dueTo: error) - } - let observable = session - .rx - .downloadResponse(request: adaptedRequest, saveTo: fileURL, httpErrorType: E.self, apiErrorType: AE.self) - .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) - return observable - } - - /// Creates a `Observable` object encapsulating upload request using given `Router`. - /// Use this method if you are uploading single file at a time. - /// - /// - Parameters: - /// - router: `Router` object used to create request. - /// - file: `UploadFile` object including file details for upload. - /// - httpErrorType: `HTTPErrorBody` http error body type. - /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. - /// - /// - Returns: `Observable` object encapsulating upload request. - public func upload(_ router: NetworkUploadRouter, _ file: UploadFile, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable> { - let originalRequest = router.asURLRequest() - let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) - let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: session) - let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: session) - let shouldRetry = { (error: NetworkError) in - self.requestInterceptor.shouldRetry(adaptedRequest, for: self.session, dueTo: error) - } - let observable = session - .rx - .uploadResponse(request: adaptedRequest, file: file, modelType: T.self, httpErrorType: E.self, apiErrorType: AE.self) - .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) - return observable - } - - /// Creates a `Observable` object encapsulating upload request using given `Router`. - /// Use this method if you are uploading multiple files at a time. - /// - /// - Parameters: - /// - router: `Router` object used to create request. - /// - formData: `UploadFormData` object including parameters and files for upload. - /// - httpErrorType: `HTTPErrorBody` http error body type. - /// - apiErrorType: `NetworkAPIError` type expected to be received in response body. - /// - /// - Returns: `Observable` object encapsulating upload request. - public func upload(_ router: NetworkUploadRouter, _ formData: UploadFormData, _ httpErrorType: E.Type = DefaultHTTPErrorBody.self, _ apiErrorType: AE.Type = DefaultNetworkAPIError.self) -> Observable> { - let originalRequest = router.asURLRequest() - let adaptedRequest = requestInterceptor.adapt(originalRequest, for: session) - let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: session) - let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: session) - let shouldRetry = { (error: NetworkError) in - self.requestInterceptor.shouldRetry(adaptedRequest, for: self.session, dueTo: error) - } - let observable = session - .rx - .uploadResponse(request: adaptedRequest, formData: formData, modelType: T.self, httpErrorType: E.self, apiErrorType: AE.self) - .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) - return observable - } - - /// Creates websocket object and establishes connection using provided url and protocols. - /// - /// - Parameters: - /// - url: `URL` of websocket server. - /// - protocols: `[String]` websocket connection protocols. - /// - closeHandler: `WebSocketCloseHandler` used to provide close coda and reason upon connection closure. - /// - /// - Returns: `WebSocket` object that represents the connection. - public func webSocket(_ url: URL, _ protocols: [String], _ closeHandler: WebSocketCloseHandler) -> WebSocket { - let task = session.webSocketTask(with: url, protocols: protocols) - let webSocket = WebSocket(task: task, closeHandler: closeHandler) - return webSocket - } - -} diff --git a/Source/Rx Extensions/Rx+NetworkOps/Completable/Completable+Retry.swift b/Source/REST/Extensions/Completable+Retry.swift similarity index 85% rename from Source/Rx Extensions/Rx+NetworkOps/Completable/Completable+Retry.swift rename to Source/REST/Extensions/Completable+Retry.swift index 6d4cd0b..4d6e98e 100644 --- a/Source/Rx Extensions/Rx+NetworkOps/Completable/Completable+Retry.swift +++ b/Source/REST/Extensions/Completable+Retry.swift @@ -8,6 +8,7 @@ import Foundation import RxSwift import RxCocoa +import CoreHTTP // This extension is inspired by Alex Grebenyuk's excellent blog https://kean.blog/post/smart-retry // Here's Alex's twitter: https://twitter.com/a_grebenyuk @@ -25,13 +26,13 @@ extension PrimitiveSequence where Trait == CompletableTrait, Element == Never { /// /// - Returns: `Completable` sequence with provoded retry strategy applied. func retry(_ maxAttemptCount: Int = Int.max, - delay: NetworkRequestRetryPolicy, + delay: HTTPRequestRetryPolicy, didBecomeReachable: PublishRelay = NetworkReachability.shared.didBecomeReachable, - shouldRetry: @escaping (NetworkError) -> Bool = { _ in true }) -> Completable { + shouldRetry: @escaping (HTTPError) -> Bool = { _ in true }) -> Completable { self .retry { (errors: Observable) in return errors.enumerated().flatMap { attempt, error -> Observable in - guard let networkError = error as? NetworkError, maxAttemptCount > attempt + 1, shouldRetry(networkError) else { + guard let networkError = error as? HTTPError, maxAttemptCount > attempt + 1, shouldRetry(networkError) else { return .error(error) } let timer = Observable.timer( diff --git a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+CatchErrors.swift b/Source/REST/Extensions/Single+CatchErrors.swift similarity index 74% rename from Source/Rx Extensions/Rx+NetworkOps/Single/Single+CatchErrors.swift rename to Source/REST/Extensions/Single+CatchErrors.swift index 6921669..7013590 100644 --- a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+CatchErrors.swift +++ b/Source/REST/Extensions/Single+CatchErrors.swift @@ -15,8 +15,8 @@ extension PrimitiveSequence where Trait == SingleTrait, Element == (response: HT /// - Returns: `Single` observable to be observed for values. func catchTransportError() -> Single { self.catch { - let clientError = NetworkClientError.transport($0) - throw NetworkError.client(clientError) + let clientError = HTTPClientError.transport($0) + throw HTTPError.client(clientError) } } } @@ -26,11 +26,11 @@ extension PrimitiveSequence where Trait == SingleTrait, Element: Decodable { /// - Returns: `Single` observable to be observed for values. func catchSerializationError() -> Single { self.catch { - if $0 is NetworkError, !($0 is DecodingError) { + if $0 is HTTPError, !($0 is DecodingError) { throw $0 } else { - let clientError = NetworkClientError.serialization($0) - throw NetworkError.client(clientError) + let clientError = HTTPClientError.serialization($0) + throw HTTPError.client(clientError) } } } diff --git a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+Decodable.swift b/Source/REST/Extensions/Single+Decodable.swift similarity index 80% rename from Source/Rx Extensions/Rx+NetworkOps/Single/Single+Decodable.swift rename to Source/REST/Extensions/Single+Decodable.swift index 592ee9b..70e18d2 100644 --- a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+Decodable.swift +++ b/Source/REST/Extensions/Single+Decodable.swift @@ -13,11 +13,11 @@ extension PrimitiveSequence where Trait == SingleTrait, Element == (response: HT /// Creates `Completable` observable + handles transport errors. /// /// - Parameters: - /// - httpErrorType: `Decodable` http error body type. + /// - httpErrorType: `Decodable` http body error type. /// - apiErrorType: `Decodable` api error type. /// /// - Returns: Observable to be observed for values. - func decodable(_ httpErrorType: E.Type, apiErrorType: AE.Type) -> Completable { + func decodable(_ httpErrorType: E.Type, apiErrorType: AE.Type) -> Completable { self // catch any transport errors if thrown. .catchTransportError() @@ -29,11 +29,11 @@ extension PrimitiveSequence where Trait == SingleTrait, Element == (response: HT /// Creates `Single` observable with the same element type + handles transport errors. /// /// - Parameters: - /// - httpErrorType: `Decodable` http error body type. + /// - httpErrorType: `Decodable` http body error type. /// - apiErrorType: `Decodable` api error type. /// /// - Returns: Observable to be observed for values. - func decodable(_ httpErrorType: E.Type, apiErrorType: AE.Type) -> Single { + func decodable(_ httpErrorType: E.Type, apiErrorType: AE.Type) -> Single { self // catch any transport errors if thrown. .catchTransportError() @@ -50,7 +50,7 @@ extension PrimitiveSequence where Trait == SingleTrait, Element == (response: HT /// - apiErrorType: `Decodable` api error type. /// /// - Returns: Observable to be observed for values. - func decodable(_ modelType: M.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Single { + func decodable(_ modelType: M.Type, httpErrorType: E.Type, apiErrorType: AE.Type) -> Single { self // catch any transport errors if thrown. .catchTransportError() diff --git a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+Decode.swift b/Source/REST/Extensions/Single+Decode.swift similarity index 80% rename from Source/Rx Extensions/Rx+NetworkOps/Single/Single+Decode.swift rename to Source/REST/Extensions/Single+Decode.swift index e52fd74..28806c3 100644 --- a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+Decode.swift +++ b/Source/REST/Extensions/Single+Decode.swift @@ -16,11 +16,11 @@ extension PrimitiveSequence where Trait == SingleTrait, Element == (response: HT /// - errorType: `Decodable` api error type. /// /// - Returns: `Completable` observable to be observed for values. - func decode(_ errorType: E.Type) -> Completable { + func decode(_ errorType: E.Type) -> Completable { map { let jsonDecoder = JSONDecoder() if let apiError = try? jsonDecoder.decode(errorType.self, from: $0.data) { - let networkError = NetworkError.api(apiError) + let networkError = HTTPError.api(apiError) throw networkError } else { return $0 @@ -34,11 +34,11 @@ extension PrimitiveSequence where Trait == SingleTrait, Element == (response: HT /// - errorType: `Decodable` api error type. /// /// - Returns: `Single` observable to be observed for values. - func decode(_ errorType: E.Type) -> Single { + func decode(_ errorType: E.Type) -> Single { map { let jsonDecoder = JSONDecoder() if let apiError = try? jsonDecoder.decode(errorType.self, from: $0.data) { - let networkError = NetworkError.api(apiError) + let networkError = HTTPError.api(apiError) throw networkError } else { return $0 @@ -52,7 +52,7 @@ extension PrimitiveSequence where Trait == SingleTrait, Element == (response: HT /// - errorType: `Decodable` api error type. /// /// - Returns: `Single` observable to be observed for values. - func decode(_ modelType: M.Type, errorType: E.Type) -> Single { + func decode(_ modelType: M.Type, errorType: E.Type) -> Single { map { let jsonDecoder = JSONDecoder() do { @@ -60,7 +60,7 @@ extension PrimitiveSequence where Trait == SingleTrait, Element == (response: HT return model } catch { let apiError = try jsonDecoder.decode(errorType.self, from: $0.1) - let networkError = NetworkError.api(apiError) + let networkError = HTTPError.api(apiError) throw networkError } } diff --git a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+Retry.swift b/Source/REST/Extensions/Single+Retry.swift similarity index 85% rename from Source/Rx Extensions/Rx+NetworkOps/Single/Single+Retry.swift rename to Source/REST/Extensions/Single+Retry.swift index e2807f9..43e0b4d 100644 --- a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+Retry.swift +++ b/Source/REST/Extensions/Single+Retry.swift @@ -25,13 +25,13 @@ extension PrimitiveSequence where Trait == SingleTrait, Element: Decodable { /// /// - Returns: `Single` sequence with provoded retry strategy applied. func retry(_ maxAttemptCount: Int = Int.max, - delay: NetworkRequestRetryPolicy, + delay: HTTPRequestRetryPolicy, didBecomeReachable: PublishRelay = NetworkReachability.shared.didBecomeReachable, - shouldRetry: @escaping (NetworkError) -> Bool = { _ in true }) -> Single { + shouldRetry: @escaping (HTTPError) -> Bool = { _ in true }) -> Single { self .retry { (errors: Observable) in return errors.enumerated().flatMap { attempt, error -> Observable in - guard let networkError = error as? NetworkError, maxAttemptCount > attempt + 1, shouldRetry(networkError) else { + guard let networkError = error as? HTTPError, maxAttemptCount > attempt + 1, shouldRetry(networkError) else { return .error(error) } let timer = Observable.timer( diff --git a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+VerifyResponse.swift b/Source/REST/Extensions/Single+VerifyResponse.swift similarity index 78% rename from Source/Rx Extensions/Rx+NetworkOps/Single/Single+VerifyResponse.swift rename to Source/REST/Extensions/Single+VerifyResponse.swift index 650f548..08e5dc1 100644 --- a/Source/Rx Extensions/Rx+NetworkOps/Single/Single+VerifyResponse.swift +++ b/Source/REST/Extensions/Single+VerifyResponse.swift @@ -13,9 +13,9 @@ extension PrimitiveSequence where Trait == SingleTrait, Element == (response: HT /// Verifies response's status code. /// /// - Returns: `Single` observable to be observed for values. - func verifyResponse(_ httpErrorType: E.Type) -> Single { + func verifyResponse(_ httpErrorType: E.Type) -> Single { map { (response: HTTPURLResponse, data: Data) in - if let networkError = NetworkError.init(response, data: data, errorType: E.self) { + if let networkError = HTTPError.init(response, data: data, errorType: E.self) { throw networkError } else { return (response, data) diff --git a/Source/REST/Types/Client/RESTClient.swift b/Source/REST/Types/Client/RESTClient.swift new file mode 100644 index 0000000..6ecbdeb --- /dev/null +++ b/Source/REST/Types/Client/RESTClient.swift @@ -0,0 +1,85 @@ +// +// RESTClient.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 10/10/2023. +// + +import Foundation +import RxSwift + +/// Entry point for creating rest requests. +public class RESTClient { + + /// Principal `Session` object that encapsulates `URLSession` instance. + private let session: Session + /// Principal `URLSession` object used to create request tasks. + private var urlSession: URLSession { + session.urlSession + } + /// Principal `HTTPRequestInterceptor` object used to intercept requests. + private let requestInterceptor: HTTPRequestInterceptor + + /// Creates a `RESTClient` instance. + /// + /// - Parameters: + /// - session: `Session` object that encapsulates `URLSession` instance. + /// - requestInterceptor: `HTTPRequestInterceptor` object used for intercepting requests. + public init(session: Session, requestInterceptor: HTTPRequestInterceptor) { + // Initialize manager's properties. + self.session = session + self.requestInterceptor = requestInterceptor + } + + /// Creates a `Completable` observable encapsulating data request using given `Router`. + /// Use this method if you are expecting empty response body. + /// + /// - Parameters: + /// - router: `Router` object used to create request. + /// - httpErrorType: `HTTPBodyError` http body error type. + /// - apiErrorType: `HTTPAPIError` type expected to be received in response body. + /// + /// - Returns: `Completable` observable encapsulating data request. + public func request(_ router: HTTPRequestRouter, _ httpErrorType: E.Type = DefaultHTTPBodyError.self, _ apiErrorType: AE.Type = DefaultHTTPAPIError.self) -> Completable { + + let originalRequest = router.asURLRequest() + let adaptedRequest = requestInterceptor.adapt(originalRequest, for: urlSession) + let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: urlSession) + let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: urlSession) + let shouldRetry = { (error: HTTPError) in + self.requestInterceptor.shouldRetry(adaptedRequest, for: self.urlSession, dueTo: error) + } + let observable = urlSession + .rx + .response(request: adaptedRequest) + .decodable(E.self, apiErrorType: AE.self) + .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) + return observable + } + + /// Creates a `Single` observable encapsulating data request using given `Router`. + /// Use this method if you are expecting data in response body. + /// + /// - Parameters: + /// - router: `Router` object used to create request. + /// - httpErrorType: `HTTPBodyError` http body error type. + /// - apiErrorType: `HTTPAPIError` type expected to be received in response body. + /// + /// - Returns: `Single` observable encapsulating data request. + public func request(_ router: HTTPRequestRouter, _ httpErrorType: E.Type = DefaultHTTPBodyError.self, _ apiErrorType: AE.Type = DefaultHTTPAPIError.self) -> Single { + let originalRequest = router.asURLRequest() + let adaptedRequest = requestInterceptor.adapt(originalRequest, for: urlSession) + let retryMaxAttempts = requestInterceptor.retryMaxAttempts(adaptedRequest, for: urlSession) + let retryPolicy = requestInterceptor.retryPolicy(adaptedRequest, for: urlSession) + let shouldRetry = { (error: HTTPError) in + self.requestInterceptor.shouldRetry(adaptedRequest, for: self.urlSession, dueTo: error) + } + let observable = urlSession + .rx + .response(request: adaptedRequest) + .decodable(T.self, httpErrorType: E.self, apiErrorType: AE.self) + .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) + return observable + } + +} diff --git a/Source/Request Interceptor/RequestAdapter.swift b/Source/Request Interceptor/RequestAdapter.swift deleted file mode 100644 index 534d32b..0000000 --- a/Source/Request Interceptor/RequestAdapter.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// RequestAdapter.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 27/03/2023. -// - -import Foundation - -/// Adapts outgoing requests. -public protocol NetworkRequestAdapter { - - /// Adapts a given request for a given session. - /// - /// - Parameters: - /// - request: current `URLRequest` - /// - session: current `URLSession` - /// - /// - Returns: `URLRequest` adapted request for a given session. - func adapt(_ request: URLRequest, for session: URLSession) -> URLRequest - -} diff --git a/Source/Request Interceptor/RequestInterceptor.swift b/Source/Request Interceptor/RequestInterceptor.swift deleted file mode 100644 index 1d3a83c..0000000 --- a/Source/Request Interceptor/RequestInterceptor.swift +++ /dev/null @@ -1,9 +0,0 @@ -// -// RequestInterceptor.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 27/03/2023. -// - -/// Request adapter and retrier. -public typealias NetworkRequestInterceptor = NetworkRequestAdapter & NetworkRequestRetrier diff --git a/Source/Request Interceptor/RequestRetrier.swift b/Source/Request Interceptor/RequestRetrier.swift deleted file mode 100644 index 5dc3e48..0000000 --- a/Source/Request Interceptor/RequestRetrier.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// RequestRetrier.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 27/03/2023. -// - -import Foundation - -/// Retries requests upon failure. -public protocol NetworkRequestRetrier { - - /// Provides maximum retry attempts for a given request and session. - /// - /// - Parameters: - /// - request: current `URLRequest` - /// - session: current `URLSession` - /// - /// - Returns: `Int` maximum retry attempts for given request and session. - func retryMaxAttempts(_ request: URLRequest, for session: URLSession) -> Int - - /// Provides retry policy for a given request and session. - /// - /// - Parameters: - /// - request: current `URLRequest` - /// - session: current `URLSession` - /// - /// - Returns: `NetworkRequestRetryPolicy` retry policy for given request and session. - func retryPolicy(_ request: URLRequest, for session: URLSession) -> NetworkRequestRetryPolicy - - /// Decides if a given request should be reried for a given session and error. - /// - /// - Parameters: - /// - request: current `URLRequest` - /// - session: current `URLSession` - /// - error: encountered `NetworkError` - /// - /// - Returns: `NetworkRequestRetryPolicy` retry policy for given request and session. - func shouldRetry(_ request: URLRequest, for session: URLSession, dueTo error: NetworkError) -> Bool - -} diff --git a/Source/Request Interceptor/RequestRetryPolicy.swift b/Source/Request Interceptor/RequestRetryPolicy.swift deleted file mode 100644 index 61256b2..0000000 --- a/Source/Request Interceptor/RequestRetryPolicy.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// RequestRetryPolicy.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 27/03/2023. -// - -import Foundation - -// This enum is inspired by Alex Grebenyuk excellent blog https://kean.blog/post/smart-retry -// Here's Alex's twitter: https://twitter.com/a_grebenyuk - -/// Policy for retrying failed requests. -public enum NetworkRequestRetryPolicy { - case immediate - case constant(time: Double) - case exponential(initial: Double, multiplier: Double, maxDelay: Double) - case custom(closure: (Int) -> Double) -} - -extension NetworkRequestRetryPolicy { - /// Creates time interavel (`Double`) from current policy and given attempt count. - /// - /// - Parameter attempt: current attempt count. - /// - /// - Returns: Time interval `Double` for delay. - func makeTimeInterval(_ attempt: Int) -> Double { - switch self { - case .immediate: return 0.0 - case .constant(let time): return time - case .exponential(let initial, let multiplier, let maxDelay): - // if it's first attempt, simply use initial delay, otherwise calculate delay - let delay = attempt == 1 ? initial : initial * pow(multiplier, Double(attempt - 1)) - return min(maxDelay, delay) - case .custom(let closure): return closure(attempt) - } - } -} diff --git a/Source/Router/NetworkRouter.swift b/Source/Router/NetworkRouter.swift deleted file mode 100644 index 455d90b..0000000 --- a/Source/Router/NetworkRouter.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// NetworkRouter.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 20/03/2023. -// - -import Foundation - -/// Holds request details. -public protocol NetworkRouter { - - /// http scheme of the request. - var scheme: HTTPScheme { get } - /// http method of the request. - var method: HTTPMethod { get } - /// url domain of the request, eg.: `github.com`. - var domain: String { get } - /// url path of the request, eg.: `api/users`. - var path: String { get } - /// http headers of the request. - var headers: [String: String] { get } - /// url query parameters of the request. - var parameters: [String: String]? { get } - /// http body of the request. - var body: [String: Any]? { get } - /// full url of the request (including: domain, path and query parameters). - var url: URL? { get } - - /// Creates a `URLRequest` object using router properties. - /// - /// - Returns: `URLRequest` created using router properties. - func asURLRequest() -> URLRequest - -} - -public extension NetworkRouter { - - /// Creates `URLRequest` object using router properties. - /// - /// - Returns: `URLRequest` created using router properties. - func asURLRequest() -> URLRequest { - var url: URL? = self.url - // Add qurey parameters - if let parameters = parameters { - var urlComponents = URLComponents(url: url!, resolvingAgainstBaseURL: false) - urlComponents?.queryItems = parameters.map { URLQueryItem(name: $0, value: $1) } - url = urlComponents?.url - } - var request = URLRequest(url: url!) - // Apply HTTP method - request.httpMethod = method.rawValue - // Apply HTTP body (if method is not GET) - if let body = body, - method != .get { - request.httpBody = try? JSONSerialization.data(withJSONObject: body) - } - // Apply HTTP headers - request.allHTTPHeaderFields = headers - return request - } - -} diff --git a/Source/RxNetworkKit.swift b/Source/RxNetworkKit.swift new file mode 100644 index 0000000..abc3b9d --- /dev/null +++ b/Source/RxNetworkKit.swift @@ -0,0 +1,8 @@ +// +// RxNetworkKit.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 11/10/2023. +// + +@_exported import CoreHTTP diff --git a/Source/Session/Session.swift b/Source/Session/Session.swift new file mode 100644 index 0000000..1b44622 --- /dev/null +++ b/Source/Session/Session.swift @@ -0,0 +1,39 @@ +// +// Session.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 01/11/2023. +// + +import Foundation + +public class Session { + + /// Principal `URLSession`object used to create request tasks. + let urlSession: URLSession + /// Principal `HTTPRequestEventMonitor` object used to monitor request tasks. + let eventMonitor: HTTPRequestEventMonitor + /// `OperationQueue` object used by event monitor. + private let eventMonitorQueue: OperationQueue + + /// Creates a `Session` instance. + /// + /// - Parameters: + /// - configuration: `URLSessionConfiguration` object used to create `URLSession` instance. + /// - eventMonitor: `HTTPRequestEventMonitor` object for monitoring events for session. + public init(configuration: URLSessionConfiguration, eventMonitor: HTTPRequestEventMonitor) { + // Apply User-Agent header as a part of HTTP aditional headers. + configuration.setUserAgentHTTPHeader() + // Initialize session's properties. + self.eventMonitor = eventMonitor + self.eventMonitorQueue = .init() + eventMonitorQueue.name = "RxNetworkKit/Session(\(UUID().uuidString))/Event Monitor Queue" + eventMonitorQueue.qualityOfService = .utility + eventMonitorQueue.maxConcurrentOperationCount = 20 + self.urlSession = .init(configuration: configuration, + delegate: eventMonitor, + delegateQueue: eventMonitorQueue) + URLSession.rx.shouldLogRequest = { _ in false } + } + +} From 4caf0782644d89a9c0ec9560c4ef812889274eb8 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Tue, 9 Jan 2024 18:06:25 +0200 Subject: [PATCH 79/95] Update build.yml --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f92d79f..9d65597 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,4 +29,3 @@ jobs: build-deploy-docs: name: Build and Deploy Docs uses: ./.github/workflows/build-deploy-docs.yml - if: github.ref == 'refs/heads/main' From cda76e9763a1eb3fe46f01f99a59b90b7dc4266d Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Tue, 9 Jan 2024 18:06:41 +0200 Subject: [PATCH 80/95] Update build.yml --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9d65597..f92d79f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,3 +29,4 @@ jobs: build-deploy-docs: name: Build and Deploy Docs uses: ./.github/workflows/build-deploy-docs.yml + if: github.ref == 'refs/heads/main' From 4b6f4c00a26fa0b084e3f56826ce3cd708f10074 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Tue, 9 Jan 2024 19:15:22 +0200 Subject: [PATCH 81/95] feat: bump Xcode version to 15.1.0 for workflow files Resolves: none. --- .github/workflows/build-deploy-docs.yml | 2 +- .github/workflows/build-ios.yml | 8 ++++---- .github/workflows/build-macos.yml | 4 ++-- .github/workflows/spm-lint.yml | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 7419c84..fd1bdea 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -20,7 +20,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.0.0' + xcode-version: '15.1.0' - name: Build Docs env: workspace: RxNetworkKit.xcworkspace diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index cef0351..5d98642 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -15,7 +15,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: 'latest-stable' + xcode-version: '15.1.0' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace @@ -34,7 +34,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: 'latest-stable' + xcode-version: '15.1.0' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace @@ -54,7 +54,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: 'latest-stable' + xcode-version: '15.1.0' - name: Build Example env: workspace: RxNetworkKit.xcworkspace @@ -74,7 +74,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: 'latest-stable' + xcode-version: '15.1.0' - name: Build Example env: workspace: RxNetworkKit.xcworkspace diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index d8ef568..a35ab7d 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -14,7 +14,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: 'latest-stable' + xcode-version: '15.1.0' - name: Build Framework env: workspace: RxNetworkKit.xcworkspace @@ -34,7 +34,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: 'latest-stable' + xcode-version: '15.1.0' - name: Build Example env: workspace: RxNetworkKit.xcworkspace diff --git a/.github/workflows/spm-lint.yml b/.github/workflows/spm-lint.yml index 0b3fb98..6f4dc84 100644 --- a/.github/workflows/spm-lint.yml +++ b/.github/workflows/spm-lint.yml @@ -28,7 +28,7 @@ jobs: - name: Set Xcode Version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '15.0.0' + xcode-version: '15.1.0' - name: Update Packages run: | swift package update From b13b1590ec81436d079fa2a0fc120db13fbd0fcb Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 10 Jan 2024 00:46:07 +0200 Subject: [PATCH 82/95] Feature/add watch os example (#62) * feat: add watch os example Resolves: none. * Update ViewController.swift * Update build.yml * Update build-watchos.yml * Update build-watchos.yml * Update README.md * Update README.md * Update README.md * feat: bump product version to 1.0.0 Resolves: none. --- .github/workflows/build-watchos.yml | 85 +++ .github/workflows/build.yml | 4 + .../iOS/iOS Example.xcodeproj/project.pbxproj | 4 +- .../Controller/ViewController.swift | 2 +- .../macOS Example.xcodeproj/project.pbxproj | 4 +- .../Controller/ViewController.swift | 2 +- .../watchOS Example.xcodeproj/project.pbxproj | 540 ++++++++++++++++++ .../xcschemes/watchOS Example.xcscheme | 91 +++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 13 + .../App/Assets.xcassets/Contents.json | 6 + .../watchOS Example/App/ExampleApp.swift | 17 + .../App/Preview Assets.xcassets/Contents.json | 6 + .../watchOS Example/View/MainView.swift | 45 ++ .../watchOS Example/View/RemoteImage.swift | 28 + .../View/RxBindingMediator.swift | 34 ++ .../View/RxImageDownloadBindingMediator.swift | 40 ++ README.md | 19 +- RxNetworkKit.xcodeproj/project.pbxproj | 14 +- .../contents.xcworkspacedata | 3 + .../CoreExample.xcodeproj/project.pbxproj | 10 +- 21 files changed, 946 insertions(+), 32 deletions(-) create mode 100644 .github/workflows/build-watchos.yml create mode 100644 Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj create mode 100644 Examples/watchOS/watchOS Example.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme create mode 100644 Examples/watchOS/watchOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Examples/watchOS/watchOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Examples/watchOS/watchOS Example/App/Assets.xcassets/Contents.json create mode 100644 Examples/watchOS/watchOS Example/App/ExampleApp.swift create mode 100644 Examples/watchOS/watchOS Example/App/Preview Assets.xcassets/Contents.json create mode 100644 Examples/watchOS/watchOS Example/View/MainView.swift create mode 100644 Examples/watchOS/watchOS Example/View/RemoteImage.swift create mode 100644 Examples/watchOS/watchOS Example/View/RxBindingMediator.swift create mode 100644 Examples/watchOS/watchOS Example/View/RxImageDownloadBindingMediator.swift diff --git a/.github/workflows/build-watchos.yml b/.github/workflows/build-watchos.yml new file mode 100644 index 0000000..df7b653 --- /dev/null +++ b/.github/workflows/build-watchos.yml @@ -0,0 +1,85 @@ +name: Build watchOS + +on: + workflow_call: + +jobs: + + framework-watchos-simulator: + name: Build Framework For watchOS Simulator + runs-on: macos-13 + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.1.0' + - name: Build Framework + env: + workspace: RxNetworkKit.xcworkspace + scheme: RxNetworkKit + destination: generic/platform=watchOS Simulator + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} + + framework-watchos: + name: Build Framework For watchOS Device + runs-on: macos-13 + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.1.0' + - name: Build Framework + env: + workspace: RxNetworkKit.xcworkspace + scheme: RxNetworkKit + destination: generic/platform=watchOS + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} + + example-watchos-simulator: + name: Build Example For watchOS Simulator + runs-on: macos-13 + needs: framework-watchos-simulator + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.1.0' + - name: Build Example + env: + workspace: RxNetworkKit.xcworkspace + scheme: watchOS Example + destination: generic/platform=watchOS Simulator + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} + + example-watchos: + name: Build Example For watchOS Device + runs-on: macos-13 + needs: framework-watchos + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.1.0' + - name: Build Example + env: + workspace: RxNetworkKit.xcworkspace + scheme: watchOS Example + destination: generic/platform=watchOS + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f92d79f..eb7adb9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,6 +26,10 @@ jobs: name: Build For macOS uses: ./.github/workflows/build-macos.yml + build-watchos: + name: Build For watchOS + uses: ./.github/workflows/build-watchos.yml + build-deploy-docs: name: Build and Deploy Docs uses: ./.github/workflows/build-deploy-docs.yml diff --git a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj index 2bbf03d..5df55ce 100644 --- a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj +++ b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj @@ -330,7 +330,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.0.2; + MARKETING_VERSION = 1.0.0; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitExample; PRODUCT_NAME = "iOS Example"; @@ -367,7 +367,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 0.0.2; + MARKETING_VERSION = 1.0.0; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitExample; PRODUCT_NAME = "iOS Example"; diff --git a/Examples/iOS/iOS Example/Controller/ViewController.swift b/Examples/iOS/iOS Example/Controller/ViewController.swift index 0d1a0fc..fc9bbc6 100644 --- a/Examples/iOS/iOS Example/Controller/ViewController.swift +++ b/Examples/iOS/iOS Example/Controller/ViewController.swift @@ -138,7 +138,7 @@ class ViewController: UIViewController { return } }) - .disposed(by: self.disposeBag) + .disposed(by: disposeBag) } /// Bind activityIndicator's isAnimating property to view state. private func bindActivityIndicatorIsAnimating() { diff --git a/Examples/macOS/macOS Example.xcodeproj/project.pbxproj b/Examples/macOS/macOS Example.xcodeproj/project.pbxproj index 8478395..562449c 100644 --- a/Examples/macOS/macOS Example.xcodeproj/project.pbxproj +++ b/Examples/macOS/macOS Example.xcodeproj/project.pbxproj @@ -361,7 +361,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 0.0.2; + MARKETING_VERSION = 1.0.0; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitMacOSExample; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -394,7 +394,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 0.0.2; + MARKETING_VERSION = 1.0.0; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitMacOSExample; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Examples/macOS/macOS Example/Controller/ViewController.swift b/Examples/macOS/macOS Example/Controller/ViewController.swift index 6b03e6e..efb1e4f 100644 --- a/Examples/macOS/macOS Example/Controller/ViewController.swift +++ b/Examples/macOS/macOS Example/Controller/ViewController.swift @@ -135,7 +135,7 @@ class ViewController: NSViewController { return } }) - .disposed(by: self.disposeBag) + .disposed(by: disposeBag) } /// Bind activityIndicator's isAnimating property to view state. private func bindActivityIndicatorIsAnimating() { diff --git a/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj b/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj new file mode 100644 index 0000000..21c878e --- /dev/null +++ b/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj @@ -0,0 +1,540 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + C60BAE4B2B4DC5B800C2338B /* watchOS Example Watch App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = C60BAE4A2B4DC5B800C2338B /* watchOS Example Watch App.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + C60BAE672B4DC64A00C2338B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C60BAE632B4DC64A00C2338B /* Assets.xcassets */; }; + C60BAE682B4DC64A00C2338B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C60BAE652B4DC64A00C2338B /* Preview Assets.xcassets */; }; + C66F79AF2B4DC8B600FDA1AC /* CoreExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C66F79AE2B4DC8B600FDA1AC /* CoreExample.framework */; }; + C66F79B02B4DC8B600FDA1AC /* CoreExample.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C66F79AE2B4DC8B600FDA1AC /* CoreExample.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C66F79B32B4DC8BD00FDA1AC /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C66F79B22B4DC8BD00FDA1AC /* RxNetworkKit.framework */; }; + C66F79B42B4DC8BD00FDA1AC /* RxNetworkKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C66F79B22B4DC8BD00FDA1AC /* RxNetworkKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C66F79B52B4DC9E300FDA1AC /* ExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60BAE622B4DC64A00C2338B /* ExampleApp.swift */; }; + C66F79B62B4DC9E900FDA1AC /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C60BAE662B4DC64A00C2338B /* MainView.swift */; }; + C66F79B92B4DCA9700FDA1AC /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = C66F79B82B4DCA9700FDA1AC /* RxCocoa */; }; + C66F79BB2B4DCA9700FDA1AC /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = C66F79BA2B4DCA9700FDA1AC /* RxSwift */; }; + C66F79C42B4DF9FA00FDA1AC /* RxImageDownloadBindingMediator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66F79C32B4DF9FA00FDA1AC /* RxImageDownloadBindingMediator.swift */; }; + C66F79C62B4DFA3300FDA1AC /* RxBindingMediator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66F79C52B4DFA3300FDA1AC /* RxBindingMediator.swift */; }; + C66F79C82B4DFA7000FDA1AC /* RemoteImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66F79C72B4DFA7000FDA1AC /* RemoteImage.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + C60BAE4C2B4DC5B800C2338B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C60BAE3E2B4DC5B700C2338B /* Project object */; + proxyType = 1; + remoteGlobalIDString = C60BAE492B4DC5B800C2338B; + remoteInfo = "watchOS Example Watch App"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + C60BAE5D2B4DC5BC00C2338B /* Embed Watch Content */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; + dstSubfolderSpec = 16; + files = ( + C60BAE4B2B4DC5B800C2338B /* watchOS Example Watch App.app in Embed Watch Content */, + ); + name = "Embed Watch Content"; + runOnlyForDeploymentPostprocessing = 0; + }; + C66F79B12B4DC8B600FDA1AC /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + C66F79B42B4DC8BD00FDA1AC /* RxNetworkKit.framework in Embed Frameworks */, + C66F79B02B4DC8B600FDA1AC /* CoreExample.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + C60BAE442B4DC5B800C2338B /* watchOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "watchOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + C60BAE4A2B4DC5B800C2338B /* watchOS Example Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "watchOS Example Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + C60BAE622B4DC64A00C2338B /* ExampleApp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleApp.swift; sourceTree = ""; }; + C60BAE632B4DC64A00C2338B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + C60BAE652B4DC64A00C2338B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + C60BAE662B4DC64A00C2338B /* MainView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; + C66F79AE2B4DC8B600FDA1AC /* CoreExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoreExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C66F79B22B4DC8BD00FDA1AC /* RxNetworkKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RxNetworkKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C66F79C32B4DF9FA00FDA1AC /* RxImageDownloadBindingMediator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxImageDownloadBindingMediator.swift; sourceTree = ""; }; + C66F79C52B4DFA3300FDA1AC /* RxBindingMediator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxBindingMediator.swift; sourceTree = ""; }; + C66F79C72B4DFA7000FDA1AC /* RemoteImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteImage.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C60BAE472B4DC5B800C2338B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C66F79B92B4DCA9700FDA1AC /* RxCocoa in Frameworks */, + C66F79B32B4DC8BD00FDA1AC /* RxNetworkKit.framework in Frameworks */, + C66F79BB2B4DCA9700FDA1AC /* RxSwift in Frameworks */, + C66F79AF2B4DC8B600FDA1AC /* CoreExample.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C60BAE3D2B4DC5B700C2338B = { + isa = PBXGroup; + children = ( + C60BAE612B4DC64A00C2338B /* watchOS Example */, + C60BAE452B4DC5B800C2338B /* Products */, + C66F79AD2B4DC8B600FDA1AC /* Frameworks */, + ); + sourceTree = ""; + }; + C60BAE452B4DC5B800C2338B /* Products */ = { + isa = PBXGroup; + children = ( + C60BAE442B4DC5B800C2338B /* watchOS Example.app */, + C60BAE4A2B4DC5B800C2338B /* watchOS Example Watch App.app */, + ); + name = Products; + sourceTree = ""; + }; + C60BAE612B4DC64A00C2338B /* watchOS Example */ = { + isa = PBXGroup; + children = ( + C66F79BD2B4DCEE100FDA1AC /* View */, + C66F79BC2B4DCAD000FDA1AC /* App */, + ); + path = "watchOS Example"; + sourceTree = ""; + }; + C66F79AD2B4DC8B600FDA1AC /* Frameworks */ = { + isa = PBXGroup; + children = ( + C66F79B22B4DC8BD00FDA1AC /* RxNetworkKit.framework */, + C66F79AE2B4DC8B600FDA1AC /* CoreExample.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + C66F79BC2B4DCAD000FDA1AC /* App */ = { + isa = PBXGroup; + children = ( + C60BAE652B4DC64A00C2338B /* Preview Assets.xcassets */, + C60BAE632B4DC64A00C2338B /* Assets.xcassets */, + C60BAE622B4DC64A00C2338B /* ExampleApp.swift */, + ); + path = App; + sourceTree = ""; + }; + C66F79BD2B4DCEE100FDA1AC /* View */ = { + isa = PBXGroup; + children = ( + C60BAE662B4DC64A00C2338B /* MainView.swift */, + C66F79C32B4DF9FA00FDA1AC /* RxImageDownloadBindingMediator.swift */, + C66F79C52B4DFA3300FDA1AC /* RxBindingMediator.swift */, + C66F79C72B4DFA7000FDA1AC /* RemoteImage.swift */, + ); + path = View; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C60BAE432B4DC5B800C2338B /* watchOS Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = C60BAE5E2B4DC5BC00C2338B /* Build configuration list for PBXNativeTarget "watchOS Example" */; + buildPhases = ( + C60BAE422B4DC5B800C2338B /* Resources */, + C60BAE5D2B4DC5BC00C2338B /* Embed Watch Content */, + ); + buildRules = ( + ); + dependencies = ( + C60BAE4D2B4DC5B800C2338B /* PBXTargetDependency */, + ); + name = "watchOS Example"; + productName = "watchOS Example"; + productReference = C60BAE442B4DC5B800C2338B /* watchOS Example.app */; + productType = "com.apple.product-type.application.watchapp2-container"; + }; + C60BAE492B4DC5B800C2338B /* watchOS Example Watch App */ = { + isa = PBXNativeTarget; + buildConfigurationList = C60BAE5A2B4DC5BC00C2338B /* Build configuration list for PBXNativeTarget "watchOS Example Watch App" */; + buildPhases = ( + C60BAE462B4DC5B800C2338B /* Sources */, + C60BAE472B4DC5B800C2338B /* Frameworks */, + C60BAE482B4DC5B800C2338B /* Resources */, + C66F79B12B4DC8B600FDA1AC /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "watchOS Example Watch App"; + packageProductDependencies = ( + C66F79B82B4DCA9700FDA1AC /* RxCocoa */, + C66F79BA2B4DCA9700FDA1AC /* RxSwift */, + ); + productName = "watchOS Example Watch App"; + productReference = C60BAE4A2B4DC5B800C2338B /* watchOS Example Watch App.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C60BAE3E2B4DC5B700C2338B /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1510; + LastUpgradeCheck = 1510; + TargetAttributes = { + C60BAE432B4DC5B800C2338B = { + CreatedOnToolsVersion = 15.1; + }; + C60BAE492B4DC5B800C2338B = { + CreatedOnToolsVersion = 15.1; + }; + }; + }; + buildConfigurationList = C60BAE412B4DC5B700C2338B /* Build configuration list for PBXProject "watchOS Example" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C60BAE3D2B4DC5B700C2338B; + packageReferences = ( + C66F79B72B4DCA9700FDA1AC /* XCRemoteSwiftPackageReference "RxSwift" */, + ); + productRefGroup = C60BAE452B4DC5B800C2338B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C60BAE432B4DC5B800C2338B /* watchOS Example */, + C60BAE492B4DC5B800C2338B /* watchOS Example Watch App */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C60BAE422B4DC5B800C2338B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C60BAE682B4DC64A00C2338B /* Preview Assets.xcassets in Resources */, + C60BAE672B4DC64A00C2338B /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C60BAE482B4DC5B800C2338B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C60BAE462B4DC5B800C2338B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C66F79C62B4DFA3300FDA1AC /* RxBindingMediator.swift in Sources */, + C66F79C42B4DF9FA00FDA1AC /* RxImageDownloadBindingMediator.swift in Sources */, + C66F79B62B4DC9E900FDA1AC /* MainView.swift in Sources */, + C66F79C82B4DFA7000FDA1AC /* RemoteImage.swift in Sources */, + C66F79B52B4DC9E300FDA1AC /* ExampleApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + C60BAE4D2B4DC5B800C2338B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C60BAE492B4DC5B800C2338B /* watchOS Example Watch App */; + targetProxy = C60BAE4C2B4DC5B800C2338B /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + C60BAE582B4DC5BC00C2338B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + C60BAE592B4DC5BC00C2338B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + C60BAE5B2B4DC5BC00C2338B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "watchOS\\ Example/App/Preview\\ Assets.xcassets"; + DEVELOPMENT_TEAM = Z3SPAA69G7; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "watchOS Example"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKWatchOnly = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.las.watchOSExample.watchkitapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 7.0; + }; + name = Debug; + }; + C60BAE5C2B4DC5BC00C2338B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "watchOS\\ Example/App/Preview\\ Assets.xcassets"; + DEVELOPMENT_TEAM = Z3SPAA69G7; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "watchOS Example"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_WKWatchOnly = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.las.watchOSExample.watchkitapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 4; + VALIDATE_PRODUCT = YES; + WATCHOS_DEPLOYMENT_TARGET = 7.0; + }; + name = Release; + }; + C60BAE5F2B4DC5BC00C2338B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = Z3SPAA69G7; + INFOPLIST_KEY_CFBundleDisplayName = "watchOS Example"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.las.watchOSExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + C60BAE602B4DC5BC00C2338B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = Z3SPAA69G7; + INFOPLIST_KEY_CFBundleDisplayName = "watchOS Example"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.las.watchOSExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C60BAE412B4DC5B700C2338B /* Build configuration list for PBXProject "watchOS Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C60BAE582B4DC5BC00C2338B /* Debug */, + C60BAE592B4DC5BC00C2338B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C60BAE5A2B4DC5BC00C2338B /* Build configuration list for PBXNativeTarget "watchOS Example Watch App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C60BAE5B2B4DC5BC00C2338B /* Debug */, + C60BAE5C2B4DC5BC00C2338B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C60BAE5E2B4DC5BC00C2338B /* Build configuration list for PBXNativeTarget "watchOS Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C60BAE5F2B4DC5BC00C2338B /* Debug */, + C60BAE602B4DC5BC00C2338B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + C66F79B72B4DCA9700FDA1AC /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + C66F79B82B4DCA9700FDA1AC /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = C66F79B72B4DCA9700FDA1AC /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; + C66F79BA2B4DCA9700FDA1AC /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = C66F79B72B4DCA9700FDA1AC /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = C60BAE3E2B4DC5B700C2338B /* Project object */; +} diff --git a/Examples/watchOS/watchOS Example.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme b/Examples/watchOS/watchOS Example.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme new file mode 100644 index 0000000..1059fb2 --- /dev/null +++ b/Examples/watchOS/watchOS Example.xcodeproj/xcshareddata/xcschemes/watchOS Example.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/watchOS/watchOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/watchOS/watchOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Examples/watchOS/watchOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/watchOS/watchOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/watchOS/watchOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..49c81cd --- /dev/null +++ b/Examples/watchOS/watchOS Example/App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "watchos", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/watchOS/watchOS Example/App/Assets.xcassets/Contents.json b/Examples/watchOS/watchOS Example/App/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/watchOS/watchOS Example/App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/watchOS/watchOS Example/App/ExampleApp.swift b/Examples/watchOS/watchOS Example/App/ExampleApp.swift new file mode 100644 index 0000000..ce90a54 --- /dev/null +++ b/Examples/watchOS/watchOS Example/App/ExampleApp.swift @@ -0,0 +1,17 @@ +// +// ExampleApp.swift +// watchOS Example Watch App +// +// Created by Loay Ashraf on 09/01/2024. +// + +import SwiftUI + +@main +struct ExampleApp: App { + var body: some Scene { + WindowGroup { + MainView() + } + } +} diff --git a/Examples/watchOS/watchOS Example/App/Preview Assets.xcassets/Contents.json b/Examples/watchOS/watchOS Example/App/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/watchOS/watchOS Example/App/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/watchOS/watchOS Example/View/MainView.swift b/Examples/watchOS/watchOS Example/View/MainView.swift new file mode 100644 index 0000000..24d77dc --- /dev/null +++ b/Examples/watchOS/watchOS Example/View/MainView.swift @@ -0,0 +1,45 @@ +// +// MainView.swift +// watchOS Example Watch App +// +// Created by Loay Ashraf on 09/01/2024. +// + +import SwiftUI +import RxNetworkKit +import CoreExample + +struct MainView: View { + @ObservedObject var users: RxBindingMediator<[Model]> + private let viewModel: ViewModel + + /// Initializer + init() { + let requestInterceptor = RequestInterceptor() + let requestEventMointor = RequestEventMonitor() + let session = Session(configuration: .default, eventMonitor: requestEventMointor) + let restClient = RESTClient(session: session, requestInterceptor: requestInterceptor) + let httpClient = HTTPClient(session: session, requestInterceptor: requestInterceptor) + viewModel = .init(restClient: restClient, httpClient: httpClient) + users = .init(input: viewModel.users, output: []) + } + + /// Body of the view + var body: some View { + ScrollView { + LazyVStack(content: { + ForEach(users.output, id: \.login) { user in + HStack(alignment: .center, spacing: 10.0) { + RemoteImage(downloadObesrvable: viewModel.downloadImage(DownloadRequestRouter.default(url: user.avatarURL))) + Text(user.login) + Spacer() + } + .padding(5) + } + }) + .onAppear { + viewModel.viewState.accept(.loading(loadType: .initial)) + } + } + } +} diff --git a/Examples/watchOS/watchOS Example/View/RemoteImage.swift b/Examples/watchOS/watchOS Example/View/RemoteImage.swift new file mode 100644 index 0000000..1b5ff2e --- /dev/null +++ b/Examples/watchOS/watchOS Example/View/RemoteImage.swift @@ -0,0 +1,28 @@ +// +// RemoteImage.swift +// watchOS Example Watch App +// +// Created by Loay Ashraf on 10/01/2024. +// + +import SwiftUI +import RxSwift +import RxNetworkKit + +struct RemoteImage: View { + @ObservedObject private var image: RxImageDownloadBindingMediator + + /// Initializer + init(downloadObesrvable: Observable) { + image = .init(input: downloadObesrvable, output: .init()) + } + + /// Body of the view + var body: some View { + Image(uiImage: image.output) + .resizable() + .frame(width: 50, height: 50) + .scaledToFit() + .clipShape(Circle()) + } +} diff --git a/Examples/watchOS/watchOS Example/View/RxBindingMediator.swift b/Examples/watchOS/watchOS Example/View/RxBindingMediator.swift new file mode 100644 index 0000000..6bf3020 --- /dev/null +++ b/Examples/watchOS/watchOS Example/View/RxBindingMediator.swift @@ -0,0 +1,34 @@ +// +// RxBindingMediator.swift +// watchOS Example Watch App +// +// Created by Loay Ashraf on 10/01/2024. +// + +import SwiftUI +import RxSwift +import RxCocoa +import RxNetworkKit + +/// This Class mediates and links Rx bindings to SwiftUI bindings +class RxBindingMediator: ObservableObject { + // MARK: - Properties + var input: Driver + @Published var output: T + // MARK: - Private Properties + private let disposeBag = DisposeBag() + // MARK: - Initializer + init(input: Driver, output: T) { + self.input = input + self.output = output + setupInOutBinding() + } + // MARK: - Instance Methods + private func setupInOutBinding() { + input + .drive(onNext: { [weak self] in + self?.output = $0 + }) + .disposed(by: disposeBag) + } +} diff --git a/Examples/watchOS/watchOS Example/View/RxImageDownloadBindingMediator.swift b/Examples/watchOS/watchOS Example/View/RxImageDownloadBindingMediator.swift new file mode 100644 index 0000000..7048859 --- /dev/null +++ b/Examples/watchOS/watchOS Example/View/RxImageDownloadBindingMediator.swift @@ -0,0 +1,40 @@ +// +// RxImageDownloadBindingMediator.swift +// watchOS Example Watch App +// +// Created by Loay Ashraf on 10/01/2024. +// + +import SwiftUI +import RxSwift +import RxNetworkKit + +/// This Class mediates and links Rx bindings to SwiftUI bindings +class RxImageDownloadBindingMediator: ObservableObject { + // MARK: - Properties + var input: Observable + @Published var output: UIImage + // MARK: - Private Properties + private let disposeBag = DisposeBag() + // MARK: - Initializer + init(input: Observable, output: UIImage) { + self.input = input + self.output = output + setupInOutBinding() + } + // MARK: - Instance Methods + private func setupInOutBinding() { + input + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] in + switch $0 { + case .completedWithData(let data): + guard let data = data, let image = UIImage(data: data) else { return } + self?.output = image + default: + return + } + }) + .disposed(by: disposeBag) + } +} diff --git a/README.md b/README.md index 769155c..3dab00c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # RxNetworkKit ![Swift](https://img.shields.io/badge/Swift-5.3-orange) -![Platforms](https://img.shields.io/badge/Platforms-iOS%20macOS-yellowgreen) +![Platforms](https://img.shields.io/badge/Platforms-iOS%20macOS%20watchOS-yellowgreen) ![iOS](https://img.shields.io/badge/iOS-14.0%2B-black) ![macOS](https://img.shields.io/badge/macOS-11.0%2B-black) -![Cocoapods](https://img.shields.io/badge/Cocoapods-compatible-red) +![watchOS](https://img.shields.io/badge/watchOS-7.0%2B-black) ![SPM](https://img.shields.io/badge/SPM-compatible-brightgreen) [![Twitter](https://img.shields.io/badge/Twitter-%40lashraf96-blue)](https://twitter.com/lashraf96) [![LinkedIn](https://img.shields.io/badge/LinkedIn-loay--ashraf-blue)](https://linkedin.com/in/loay-ashraf) @@ -114,19 +114,10 @@ uploadObservable | Platform | Minimum Swift Version | Installation | Status | | --- | --- | --- | --- | -| iOS 14.0+ / macOS 11.0+ | 5.3 | [CocoaPods](#cocoapods), [Swift Package Manager](#swift-package-manager), [Manual](#manually) | Fully Tested | +| iOS(iPadOS) 14.0+ / macOS 11.0+ / watchOS 7.0+ | 5.3 | [Swift Package Manager](#swift-package-manager), [Manual](#manually) | Fully Tested | ## Installation -### CocoaPods - -[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate RxNetworkKit into your Xcode project using CocoaPods, specify it in your `Podfile`: - -```ruby -pod 'RxNetworkKitX' -``` -P.S: We had to postfix with 'X' as there's a pod on trunk with same name but different case 🤦‍♂️ - ### Swift Package Manager The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler. @@ -139,10 +130,6 @@ dependencies: [ ] ``` -### Carthage - -Support for Carthage is coming soon. - ### Manually If you prefer not to use any of the aforementioned dependency managers, you can integrate RxNetworkKit into your project manually. diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index 2324c13..fc62a83 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -605,18 +605,19 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 0.0.2; + MARKETING_VERSION = 1.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx watchos watchsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,4"; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Debug; }; @@ -644,18 +645,19 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 0.0.2; + MARKETING_VERSION = 1.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx watchos watchsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,4"; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Release; }; diff --git a/RxNetworkKit.xcworkspace/contents.xcworkspacedata b/RxNetworkKit.xcworkspace/contents.xcworkspacedata index 1281c77..c0d5cf8 100644 --- a/RxNetworkKit.xcworkspace/contents.xcworkspacedata +++ b/RxNetworkKit.xcworkspace/contents.xcworkspacedata @@ -13,4 +13,7 @@ + + diff --git a/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj b/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj index 7f43934..c550a9c 100644 --- a/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj +++ b/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj @@ -376,15 +376,16 @@ PRODUCT_BUNDLE_IDENTIFIER = com.las.CoreExample; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx watchos watchsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,4"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Debug; }; @@ -419,14 +420,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.las.CoreExample; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx watchos watchsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,4"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Release; }; From 78b95adda4ec4185319872b3cbed41b4aa5a8ee7 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 10 Jan 2024 00:50:47 +0200 Subject: [PATCH 83/95] fix: silence warnings Resolves: none. --- .../HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift | 2 ++ Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift b/Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift index 6eef6df..ce815fb 100644 --- a/Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift +++ b/Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift @@ -55,6 +55,7 @@ extension Reactive where Base: URLSession { taskProgressSubject.onCompleted() } } + _ = taskProgressObservation task.resume() return Disposables.create(with: task.cancel) } @@ -106,6 +107,7 @@ extension Reactive where Base: URLSession { taskProgressSubject.onCompleted() } } + _ = taskProgressObservation task.resume() return Disposables.create(with: task.cancel) } diff --git a/Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift b/Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift index 97e0244..85390a2 100644 --- a/Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift +++ b/Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift @@ -56,6 +56,7 @@ extension Reactive where Base: URLSession { taskProgressSubject.onCompleted() } } + _ = taskProgressObservation task.resume() return Disposables.create(with: task.cancel) } @@ -107,6 +108,7 @@ extension Reactive where Base: URLSession { taskProgressSubject.onCompleted() } } + _ = taskProgressObservation task.resume() return Disposables.create(with: task.cancel) } From 47d0660a5fa669d3ab4d2a6290ae782a63f5211d Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 10 Jan 2024 00:53:41 +0200 Subject: [PATCH 84/95] feat: add watchOS platform to package manifest + sort files Resolves: none. --- .../watchOS/watchOS Example.xcodeproj/project.pbxproj | 8 ++++---- Package.swift | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj b/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj index 21c878e..a6759de 100644 --- a/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj +++ b/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj @@ -109,8 +109,8 @@ C60BAE612B4DC64A00C2338B /* watchOS Example */ = { isa = PBXGroup; children = ( - C66F79BD2B4DCEE100FDA1AC /* View */, C66F79BC2B4DCAD000FDA1AC /* App */, + C66F79BD2B4DCEE100FDA1AC /* View */, ); path = "watchOS Example"; sourceTree = ""; @@ -127,9 +127,9 @@ C66F79BC2B4DCAD000FDA1AC /* App */ = { isa = PBXGroup; children = ( - C60BAE652B4DC64A00C2338B /* Preview Assets.xcassets */, C60BAE632B4DC64A00C2338B /* Assets.xcassets */, C60BAE622B4DC64A00C2338B /* ExampleApp.swift */, + C60BAE652B4DC64A00C2338B /* Preview Assets.xcassets */, ); path = App; sourceTree = ""; @@ -138,9 +138,9 @@ isa = PBXGroup; children = ( C60BAE662B4DC64A00C2338B /* MainView.swift */, - C66F79C32B4DF9FA00FDA1AC /* RxImageDownloadBindingMediator.swift */, - C66F79C52B4DFA3300FDA1AC /* RxBindingMediator.swift */, C66F79C72B4DFA7000FDA1AC /* RemoteImage.swift */, + C66F79C52B4DFA3300FDA1AC /* RxBindingMediator.swift */, + C66F79C32B4DF9FA00FDA1AC /* RxImageDownloadBindingMediator.swift */, ); path = View; sourceTree = ""; diff --git a/Package.swift b/Package.swift index 61d849c..4f434c3 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ import PackageDescription let package = Package( name: "RxNetworkKit", platforms: [ - .iOS(.v14), .macOS(.v11) + .iOS(.v14), .macOS(.v11), .watchOS(.v7) ], products: [ .library(name: "RxNetworkKit", targets: ["RxNetworkKit"]), From 2cf9969ae970a7659f2c00520d478ecd7738841d Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 10 Jan 2024 03:46:11 +0200 Subject: [PATCH 85/95] Feature/add tvos support (#63) * feat: add tvOS support + example Resolves: none. * Update build.yml * Update build.yml * Update build-tvos.yml * feat: update documentation Resolves: none. --- .github/workflows/build-tvos.yml | 86 ++++ .github/workflows/build.yml | 4 + Docs.docc/Pages/RxNetworkKit.md | 1 + .../Controller/ViewController.swift | 2 +- .../tvOS Example.xcodeproj/project.pbxproj | 472 ++++++++++++++++++ .../xcschemes/tvOS Example.xcscheme | 77 +++ .../tvOS/tvOS Example/App/AppDelegate.swift | 40 ++ .../AccentColor.colorset/Contents.json | 11 + .../Content.imageset/Contents.json | 11 + .../Back.imagestacklayer/Contents.json | 6 + .../Contents.json | 17 + .../Content.imageset/Contents.json | 11 + .../Front.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 11 + .../Middle.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 16 + .../Back.imagestacklayer/Contents.json | 6 + .../App Icon.imagestack/Contents.json | 17 + .../Content.imageset/Contents.json | 16 + .../Front.imagestacklayer/Contents.json | 6 + .../Content.imageset/Contents.json | 16 + .../Middle.imagestacklayer/Contents.json | 6 + .../Contents.json | 32 ++ .../Contents.json | 16 + .../Top Shelf Image.imageset/Contents.json | 16 + .../App/Assets.xcassets/Contents.json | 6 + .../App/Base.lproj/LaunchScreen.storyboard | 24 + .../Controller/ViewController.swift | 161 ++++++ .../View/Base.lproj/Main.storyboard | 163 ++++++ .../tvOS Example/View/TableViewCell.swift | 26 + .../watchOS Example.xcodeproj/project.pbxproj | 4 + RxNetworkKit.xcodeproj/project.pbxproj | 10 +- .../contents.xcworkspacedata | 3 + .../CoreExample.xcodeproj/project.pbxproj | 10 +- Source/Session/Session.swift | 1 + 35 files changed, 1307 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/build-tvos.yml create mode 100644 Examples/tvOS/tvOS Example.xcodeproj/project.pbxproj create mode 100644 Examples/tvOS/tvOS Example.xcodeproj/xcshareddata/xcschemes/tvOS Example.xcscheme create mode 100644 Examples/tvOS/tvOS Example/App/AppDelegate.swift create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Assets.xcassets/Contents.json create mode 100644 Examples/tvOS/tvOS Example/App/Base.lproj/LaunchScreen.storyboard create mode 100644 Examples/tvOS/tvOS Example/Controller/ViewController.swift create mode 100644 Examples/tvOS/tvOS Example/View/Base.lproj/Main.storyboard create mode 100644 Examples/tvOS/tvOS Example/View/TableViewCell.swift diff --git a/.github/workflows/build-tvos.yml b/.github/workflows/build-tvos.yml new file mode 100644 index 0000000..e5793e8 --- /dev/null +++ b/.github/workflows/build-tvos.yml @@ -0,0 +1,86 @@ +name: Build tvOS + +on: + workflow_call: + +jobs: + + framework-tvos-simulator: + name: Build Framework For tvOS Simulator + runs-on: macos-13 + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.1.0' + - name: Build Framework + env: + workspace: RxNetworkKit.xcworkspace + scheme: RxNetworkKit + destination: generic/platform=tvOS Simulator + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} + + framework-tvos: + name: Build Framework For tvOS Device + runs-on: macos-13 + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.1.0' + - name: Build Framework + env: + workspace: RxNetworkKit.xcworkspace + scheme: RxNetworkKit + destination: generic/platform=tvOS + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} + + example-tvos-simulator: + name: Build Example For tvOS Simulator + runs-on: macos-13 + needs: framework-tvos-simulator + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.1.0' + - name: Build Example + env: + workspace: RxNetworkKit.xcworkspace + scheme: tvOS Example + destination: generic/platform=tvOS Simulator + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} + + example-tvos: + name: Build Example For tvOS Device + runs-on: macos-13 + needs: framework-tvos + + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set Xcode Version + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.1.0' + - name: Build Example + env: + workspace: RxNetworkKit.xcworkspace + scheme: tvOS Example + destination: generic/platform=tvOS + run: | + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty && exit ${PIPESTATUS[0]} + + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eb7adb9..d4ff127 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,6 +26,10 @@ jobs: name: Build For macOS uses: ./.github/workflows/build-macos.yml + build-tvos: + name: Build For tvOS + uses: ./.github/workflows/build-tvos.yml + build-watchos: name: Build For watchOS uses: ./.github/workflows/build-watchos.yml diff --git a/Docs.docc/Pages/RxNetworkKit.md b/Docs.docc/Pages/RxNetworkKit.md index 46495d9..8e269f6 100644 --- a/Docs.docc/Pages/RxNetworkKit.md +++ b/Docs.docc/Pages/RxNetworkKit.md @@ -18,6 +18,7 @@ It makes use of RxSwift's traits at request level to acheive a high level of spe ### Foundation +- ``Session`` - ``RESTClient`` - ``HTTPClient`` diff --git a/Examples/iOS/iOS Example/Controller/ViewController.swift b/Examples/iOS/iOS Example/Controller/ViewController.swift index fc9bbc6..5045a12 100644 --- a/Examples/iOS/iOS Example/Controller/ViewController.swift +++ b/Examples/iOS/iOS Example/Controller/ViewController.swift @@ -204,6 +204,6 @@ class ViewController: UIViewController { /// - Parameter url: HTML `URL` to user profile. private func navigateToUserProfile(with url: URL) { let safariVC = SFSafariViewController(url: url) - self.present(safariVC, animated: true, completion: nil) + present(safariVC, animated: true, completion: nil) } } diff --git a/Examples/tvOS/tvOS Example.xcodeproj/project.pbxproj b/Examples/tvOS/tvOS Example.xcodeproj/project.pbxproj new file mode 100644 index 0000000..a38162c --- /dev/null +++ b/Examples/tvOS/tvOS Example.xcodeproj/project.pbxproj @@ -0,0 +1,472 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + C66F79D62B4E10E400FDA1AC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66F79D52B4E10E400FDA1AC /* AppDelegate.swift */; }; + C66F79D82B4E10E400FDA1AC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66F79D72B4E10E400FDA1AC /* ViewController.swift */; }; + C66F79DB2B4E10E400FDA1AC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C66F79D92B4E10E400FDA1AC /* Main.storyboard */; }; + C66F79DD2B4E10E800FDA1AC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C66F79DC2B4E10E800FDA1AC /* Assets.xcassets */; }; + C66F79E02B4E10E800FDA1AC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C66F79DE2B4E10E800FDA1AC /* LaunchScreen.storyboard */; }; + C66F79E82B4E11C900FDA1AC /* CoreExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C66F79E72B4E11C900FDA1AC /* CoreExample.framework */; }; + C66F79E92B4E11C900FDA1AC /* CoreExample.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C66F79E72B4E11C900FDA1AC /* CoreExample.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C66F79EC2B4E11CE00FDA1AC /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C66F79EB2B4E11CE00FDA1AC /* RxNetworkKit.framework */; }; + C66F79ED2B4E11CE00FDA1AC /* RxNetworkKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C66F79EB2B4E11CE00FDA1AC /* RxNetworkKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + C66F79F02B4E122000FDA1AC /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = C66F79EF2B4E122000FDA1AC /* RxCocoa */; }; + C66F79F22B4E122000FDA1AC /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = C66F79F12B4E122000FDA1AC /* RxSwift */; }; + C66F79F72B4E138B00FDA1AC /* RxDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = C66F79F62B4E138B00FDA1AC /* RxDataSources */; }; + C66F79F92B4E150800FDA1AC /* TableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66F79F82B4E150800FDA1AC /* TableViewCell.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + C66F79EA2B4E11C900FDA1AC /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + C66F79ED2B4E11CE00FDA1AC /* RxNetworkKit.framework in Embed Frameworks */, + C66F79E92B4E11C900FDA1AC /* CoreExample.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + C66F79D22B4E10E400FDA1AC /* tvOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "tvOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + C66F79D52B4E10E400FDA1AC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + C66F79D72B4E10E400FDA1AC /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + C66F79DA2B4E10E400FDA1AC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + C66F79DC2B4E10E800FDA1AC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + C66F79DF2B4E10E800FDA1AC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + C66F79E72B4E11C900FDA1AC /* CoreExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoreExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C66F79EB2B4E11CE00FDA1AC /* RxNetworkKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RxNetworkKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C66F79F82B4E150800FDA1AC /* TableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewCell.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C66F79CF2B4E10E400FDA1AC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C66F79F02B4E122000FDA1AC /* RxCocoa in Frameworks */, + C66F79EC2B4E11CE00FDA1AC /* RxNetworkKit.framework in Frameworks */, + C66F79F72B4E138B00FDA1AC /* RxDataSources in Frameworks */, + C66F79F22B4E122000FDA1AC /* RxSwift in Frameworks */, + C66F79E82B4E11C900FDA1AC /* CoreExample.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C66F79C92B4E10E300FDA1AC = { + isa = PBXGroup; + children = ( + C66F79D42B4E10E400FDA1AC /* tvOS Example */, + C66F79D32B4E10E400FDA1AC /* Products */, + C66F79E62B4E11C900FDA1AC /* Frameworks */, + ); + sourceTree = ""; + }; + C66F79D32B4E10E400FDA1AC /* Products */ = { + isa = PBXGroup; + children = ( + C66F79D22B4E10E400FDA1AC /* tvOS Example.app */, + ); + name = Products; + sourceTree = ""; + }; + C66F79D42B4E10E400FDA1AC /* tvOS Example */ = { + isa = PBXGroup; + children = ( + C66F79FA2B4E154A00FDA1AC /* App */, + C66F79FB2B4E156700FDA1AC /* Controller */, + C66F79FC2B4E157B00FDA1AC /* View */, + ); + path = "tvOS Example"; + sourceTree = ""; + }; + C66F79E62B4E11C900FDA1AC /* Frameworks */ = { + isa = PBXGroup; + children = ( + C66F79EB2B4E11CE00FDA1AC /* RxNetworkKit.framework */, + C66F79E72B4E11C900FDA1AC /* CoreExample.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + C66F79FA2B4E154A00FDA1AC /* App */ = { + isa = PBXGroup; + children = ( + C66F79D52B4E10E400FDA1AC /* AppDelegate.swift */, + C66F79DC2B4E10E800FDA1AC /* Assets.xcassets */, + C66F79DE2B4E10E800FDA1AC /* LaunchScreen.storyboard */, + ); + path = App; + sourceTree = ""; + }; + C66F79FB2B4E156700FDA1AC /* Controller */ = { + isa = PBXGroup; + children = ( + C66F79D72B4E10E400FDA1AC /* ViewController.swift */, + ); + path = Controller; + sourceTree = ""; + }; + C66F79FC2B4E157B00FDA1AC /* View */ = { + isa = PBXGroup; + children = ( + C66F79D92B4E10E400FDA1AC /* Main.storyboard */, + C66F79F82B4E150800FDA1AC /* TableViewCell.swift */, + ); + path = View; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C66F79D12B4E10E400FDA1AC /* tvOS Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = C66F79E32B4E10E800FDA1AC /* Build configuration list for PBXNativeTarget "tvOS Example" */; + buildPhases = ( + C66F79CE2B4E10E400FDA1AC /* Sources */, + C66F79CF2B4E10E400FDA1AC /* Frameworks */, + C66F79D02B4E10E400FDA1AC /* Resources */, + C66F79EA2B4E11C900FDA1AC /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "tvOS Example"; + packageProductDependencies = ( + C66F79EF2B4E122000FDA1AC /* RxCocoa */, + C66F79F12B4E122000FDA1AC /* RxSwift */, + C66F79F62B4E138B00FDA1AC /* RxDataSources */, + ); + productName = "tvOS Example"; + productReference = C66F79D22B4E10E400FDA1AC /* tvOS Example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C66F79CA2B4E10E300FDA1AC /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1510; + LastUpgradeCheck = 1510; + TargetAttributes = { + C66F79D12B4E10E400FDA1AC = { + CreatedOnToolsVersion = 15.1; + }; + }; + }; + buildConfigurationList = C66F79CD2B4E10E300FDA1AC /* Build configuration list for PBXProject "tvOS Example" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C66F79C92B4E10E300FDA1AC; + packageReferences = ( + C66F79EE2B4E122000FDA1AC /* XCRemoteSwiftPackageReference "RxSwift" */, + C66F79F32B4E138B00FDA1AC /* XCRemoteSwiftPackageReference "RxDataSources" */, + ); + productRefGroup = C66F79D32B4E10E400FDA1AC /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C66F79D12B4E10E400FDA1AC /* tvOS Example */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C66F79D02B4E10E400FDA1AC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C66F79E02B4E10E800FDA1AC /* LaunchScreen.storyboard in Resources */, + C66F79DD2B4E10E800FDA1AC /* Assets.xcassets in Resources */, + C66F79DB2B4E10E400FDA1AC /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C66F79CE2B4E10E400FDA1AC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C66F79F92B4E150800FDA1AC /* TableViewCell.swift in Sources */, + C66F79D82B4E10E400FDA1AC /* ViewController.swift in Sources */, + C66F79D62B4E10E400FDA1AC /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + C66F79D92B4E10E400FDA1AC /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C66F79DA2B4E10E400FDA1AC /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + C66F79DE2B4E10E800FDA1AC /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + C66F79DF2B4E10E800FDA1AC /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C66F79E12B4E10E800FDA1AC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = appletvos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TVOS_DEPLOYMENT_TARGET = 14.0; + }; + name = Debug; + }; + C66F79E22B4E10E800FDA1AC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = appletvos; + SWIFT_COMPILATION_MODE = wholemodule; + TVOS_DEPLOYMENT_TARGET = 14.0; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + C66F79E42B4E10E800FDA1AC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = Z3SPAA69G7; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.las.tvOSExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 14.0; + }; + name = Debug; + }; + C66F79E52B4E10E800FDA1AC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = Z3SPAA69G7; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.las.tvOSExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 14.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C66F79CD2B4E10E300FDA1AC /* Build configuration list for PBXProject "tvOS Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C66F79E12B4E10E800FDA1AC /* Debug */, + C66F79E22B4E10E800FDA1AC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C66F79E32B4E10E800FDA1AC /* Build configuration list for PBXNativeTarget "tvOS Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C66F79E42B4E10E800FDA1AC /* Debug */, + C66F79E52B4E10E800FDA1AC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + C66F79EE2B4E122000FDA1AC /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.6.0; + }; + }; + C66F79F32B4E138B00FDA1AC /* XCRemoteSwiftPackageReference "RxDataSources" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/RxSwiftCommunity/RxDataSources"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.0.2; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + C66F79EF2B4E122000FDA1AC /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = C66F79EE2B4E122000FDA1AC /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; + C66F79F12B4E122000FDA1AC /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = C66F79EE2B4E122000FDA1AC /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; + C66F79F62B4E138B00FDA1AC /* RxDataSources */ = { + isa = XCSwiftPackageProductDependency; + package = C66F79F32B4E138B00FDA1AC /* XCRemoteSwiftPackageReference "RxDataSources" */; + productName = RxDataSources; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = C66F79CA2B4E10E300FDA1AC /* Project object */; +} diff --git a/Examples/tvOS/tvOS Example.xcodeproj/xcshareddata/xcschemes/tvOS Example.xcscheme b/Examples/tvOS/tvOS Example.xcodeproj/xcshareddata/xcschemes/tvOS Example.xcscheme new file mode 100644 index 0000000..9567f8d --- /dev/null +++ b/Examples/tvOS/tvOS Example.xcodeproj/xcshareddata/xcschemes/tvOS Example.xcscheme @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/tvOS/tvOS Example/App/AppDelegate.swift b/Examples/tvOS/tvOS Example/App/AppDelegate.swift new file mode 100644 index 0000000..556864a --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/AppDelegate.swift @@ -0,0 +1,40 @@ +// +// AppDelegate.swift +// tvOS Example +// +// Created by Loay Ashraf on 10/01/2024. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + +} + diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..2e00335 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json new file mode 100644 index 0000000..de59d88 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ] +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..2e00335 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..2e00335 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..795cce1 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json new file mode 100644 index 0000000..de59d88 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ] +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..795cce1 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 0000000..795cce1 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json new file mode 100644 index 0000000..f47ba43 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json @@ -0,0 +1,32 @@ +{ + "assets" : [ + { + "filename" : "App Icon - App Store.imagestack", + "idiom" : "tv", + "role" : "primary-app-icon", + "size" : "1280x768" + }, + { + "filename" : "App Icon.imagestack", + "idiom" : "tv", + "role" : "primary-app-icon", + "size" : "400x240" + }, + { + "filename" : "Top Shelf Image Wide.imageset", + "idiom" : "tv", + "role" : "top-shelf-image-wide", + "size" : "2320x720" + }, + { + "filename" : "Top Shelf Image.imageset", + "idiom" : "tv", + "role" : "top-shelf-image", + "size" : "1920x720" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json new file mode 100644 index 0000000..795cce1 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json new file mode 100644 index 0000000..795cce1 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Assets.xcassets/Contents.json b/Examples/tvOS/tvOS Example/App/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Examples/tvOS/tvOS Example/App/Base.lproj/LaunchScreen.storyboard b/Examples/tvOS/tvOS Example/App/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..660ba53 --- /dev/null +++ b/Examples/tvOS/tvOS Example/App/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/tvOS/tvOS Example/Controller/ViewController.swift b/Examples/tvOS/tvOS Example/Controller/ViewController.swift new file mode 100644 index 0000000..da5f1e8 --- /dev/null +++ b/Examples/tvOS/tvOS Example/Controller/ViewController.swift @@ -0,0 +1,161 @@ +// +// ViewController.swift +// tvOS Example +// +// Created by Loay Ashraf on 10/01/2024. +// + +import UIKit +import RxSwift +import RxCocoa +import RxDataSources +import RxNetworkKit +import CoreExample + +class ViewController: UIViewController { + @IBOutlet weak var tableView: UITableView! + @IBOutlet weak var reachbilityView: UIView! + @IBOutlet weak var reachabilityLabel: UILabel! + @IBOutlet weak var errorView: UIStackView! + @IBOutlet weak var errorImageView: UIImageView! + @IBOutlet weak var errorLabel: UILabel! + @IBOutlet weak var errorRetryButton: UIButton! + @IBOutlet weak var activityIndicator: UIActivityIndicatorView! + private var viewModel: ViewModel! + private let disposeBag: DisposeBag = .init() + /// View has loaded successfully. + override func viewDidLoad() { + super.viewDidLoad() + setupViewModel() + bindUI() + } + /// View is about to appear on screen. + /// + /// - Parameter animated: whether to animate view appearance or not + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + viewModel.viewState.accept(.loading(loadType: .initial)) + } + /// Initializes ViewModel object. + private func setupViewModel() { + let requestInterceptor = RequestInterceptor() + let requestEventMointor = RequestEventMonitor() + let session = Session(configuration: .default, eventMonitor: requestEventMointor) + let restClient = RESTClient(session: session, requestInterceptor: requestInterceptor) + let httpClient = HTTPClient(session: session, requestInterceptor: requestInterceptor) + viewModel = .init(restClient: restClient, httpClient: httpClient) + } + // MARK: - Bindings + /// Binds view state and events. + private func bindUI() { + bindUIState() + bindUIEvents() + } + // MARK: UI State Bindings + /// Binds view state. + private func bindUIState() { + bindTableViewIsHidden() + bindTableViewItems() + bindActivityIndicatorIsAnimating() + bindErrorViewIsHidden() + bindErrorViewText() + bindReachabilityText() + } + /// Presents new reachability status. + /// + /// - Parameter status: `NetworkReachabilityStatus` object to be presented. + private func presentReachabilityStatus(_ status: NetworkReachabilityStatus) { + var reachabilityText = "" + switch status { + case .reachable(let interfaceType): + reachabilityText = "Network is reachable via \(interfaceType.rawValue)." + case .unReachable: + reachabilityText = "Network is unreachable." + } + self.reachabilityLabel.text = reachabilityText + self.reachbilityView.isHidden = false + DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { + self.reachabilityLabel.text = "" + self.reachbilityView.isHidden = true + } + } + // MARK: UI Events Bindings + /// Binds view events. + private func bindUIEvents() { + // Bind errorView's retryButton to viewModel's viewState. + bindErrorViewRetryEvent() + } + /// Bind tableView's isHidden property to view state. + private func bindTableViewIsHidden() { + viewModel.viewState + .map({ $0 == .loading(loadType: .initial) || $0 == .error }) + .bind(to: tableView.rx.isHidden) + .disposed(by: disposeBag) + } + /// Bind viewModel's users sequence to tableView's items. + private func bindTableViewItems() { + viewModel.users + .drive(tableView.rx.items(cellIdentifier: "customCell", cellType: TableViewCell.self)) { index, model, cell in + cell.customTextLabel.text = model.login + self.downloadTableViewCellImage(using: model.avatarURL, applyTo: cell) + } + .disposed(by: disposeBag) + } + /// Downloads image data to be displayed inside `TableViewCell` object + /// + /// - Parameters: + /// - url: `URL` used to download image data. + /// - cell: `TableViewCell` that the image data will be applied to. + private func downloadTableViewCellImage(using url: URL, applyTo cell: TableViewCell) { + viewModel.downloadImage(DownloadRequestRouter.default(url: url)) + .observe(on: MainScheduler.instance) + .subscribe(onNext: { + switch $0 { + case .completedWithData(let data): + guard let data = data, let image = UIImage(data: data) else { return } + cell.customImageView.image = image + default: + return + } + }) + .disposed(by: disposeBag) + } + /// Bind activityIndicator's isAnimating property to view state. + private func bindActivityIndicatorIsAnimating() { + viewModel.viewState + .map({ $0 == .loading(loadType: .initial) }) + .bind(to: activityIndicator.rx.isAnimating) + .disposed(by: disposeBag) + } + /// Bind errorView's isHidden property to view state. + private func bindErrorViewIsHidden() { + viewModel.viewState + .map({ $0 != .error }) + .bind(to: errorView.rx.isHidden) + .disposed(by: disposeBag) + } + /// Bind viewModel's error sequence to errorLabel's text. + private func bindErrorViewText() { + viewModel.error + .map({ $0.localizedDescription }) + .drive(errorLabel.rx.text) + .disposed(by: disposeBag) + } + /// Bind NetworkReachability's status sequence to reachabiliytView and reachabilityLabel. + private func bindReachabilityText() { + NetworkReachability.shared.status + .observe(on: MainScheduler.instance) + .bind(onNext: { + self.presentReachabilityStatus($0) + }) + .disposed(by: disposeBag) + } + /// Bind errorView's retryButton to viewModel's viewState. + private func bindErrorViewRetryEvent() { + errorRetryButton.rx + .primaryAction + .map({ .loading(loadType: .initial) }) + .bind(to: viewModel.viewState) + .disposed(by: disposeBag) + } +} diff --git a/Examples/tvOS/tvOS Example/View/Base.lproj/Main.storyboard b/Examples/tvOS/tvOS Example/View/Base.lproj/Main.storyboard new file mode 100644 index 0000000..738327d --- /dev/null +++ b/Examples/tvOS/tvOS Example/View/Base.lproj/Main.storyboard @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/tvOS/tvOS Example/View/TableViewCell.swift b/Examples/tvOS/tvOS Example/View/TableViewCell.swift new file mode 100644 index 0000000..16b74b9 --- /dev/null +++ b/Examples/tvOS/tvOS Example/View/TableViewCell.swift @@ -0,0 +1,26 @@ +// +// TableViewCell.swift +// tvOS Example +// +// Created by Loay Ashraf on 10/01/2024. +// + +import UIKit + +class TableViewCell: UITableViewCell { + @IBOutlet weak var customTextLabel: UILabel! + @IBOutlet weak var customImageView: UIImageView! + override func awakeFromNib() { + super.awakeFromNib() + customImageView.layer.cornerRadius = 150.0 + customImageView.layer.cornerCurve = .continuous + } + override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) { + super.didUpdateFocus(in: context, with: coordinator) + if context.nextFocusedView == self { + customTextLabel.textColor = .black + } else { + customTextLabel.textColor = .white + } + } +} diff --git a/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj b/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj index a6759de..5d80ece 100644 --- a/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj +++ b/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj @@ -322,12 +322,14 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Debug; }; @@ -377,10 +379,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SWIFT_COMPILATION_MODE = wholemodule; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Release; }; diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index fc62a83..b0aa15c 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -611,12 +611,13 @@ PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx watchos watchsimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,4"; + TARGETED_DEVICE_FAMILY = "1,2,3,4"; + TVOS_DEPLOYMENT_TARGET = 14.0; WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Debug; @@ -651,12 +652,13 @@ PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx watchos watchsimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,4"; + TARGETED_DEVICE_FAMILY = "1,2,3,4"; + TVOS_DEPLOYMENT_TARGET = 14.0; WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Release; diff --git a/RxNetworkKit.xcworkspace/contents.xcworkspacedata b/RxNetworkKit.xcworkspace/contents.xcworkspacedata index c0d5cf8..c7114cb 100644 --- a/RxNetworkKit.xcworkspace/contents.xcworkspacedata +++ b/RxNetworkKit.xcworkspace/contents.xcworkspacedata @@ -13,6 +13,9 @@ + + diff --git a/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj b/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj index c550a9c..c908e4d 100644 --- a/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj +++ b/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj @@ -376,13 +376,14 @@ PRODUCT_BUNDLE_IDENTIFIER = com.las.CoreExample; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx watchos watchsimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,4"; + TARGETED_DEVICE_FAMILY = "1,2,3,4"; + TVOS_DEPLOYMENT_TARGET = 14.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WATCHOS_DEPLOYMENT_TARGET = 7.0; @@ -420,12 +421,13 @@ PRODUCT_BUNDLE_IDENTIFIER = com.las.CoreExample; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx watchos watchsimulator"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx watchos watchsimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2,4"; + TARGETED_DEVICE_FAMILY = "1,2,3,4"; + TVOS_DEPLOYMENT_TARGET = 14.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; WATCHOS_DEPLOYMENT_TARGET = 7.0; diff --git a/Source/Session/Session.swift b/Source/Session/Session.swift index 1b44622..4156b5a 100644 --- a/Source/Session/Session.swift +++ b/Source/Session/Session.swift @@ -7,6 +7,7 @@ import Foundation +/// Wrapper object for `URLSession` and `HTTPRequestEventMonitor` that can be shared between multiple clients. public class Session { /// Principal `URLSession`object used to create request tasks. From 4acd84b0f6ca078eacffc9bcc7155ec2771c576a Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 10 Jan 2024 03:52:44 +0200 Subject: [PATCH 86/95] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3dab00c..1e6acad 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # RxNetworkKit ![Swift](https://img.shields.io/badge/Swift-5.3-orange) -![Platforms](https://img.shields.io/badge/Platforms-iOS%20macOS%20watchOS-yellowgreen) +![Platforms](https://img.shields.io/badge/Platforms-iOS%20macOS%20tvOS%20watchOS-yellowgreen) ![iOS](https://img.shields.io/badge/iOS-14.0%2B-black) ![macOS](https://img.shields.io/badge/macOS-11.0%2B-black) +![tvOS](https://img.shields.io/badge/tvOS-14.0%2B-black) ![watchOS](https://img.shields.io/badge/watchOS-7.0%2B-black) ![SPM](https://img.shields.io/badge/SPM-compatible-brightgreen) [![Twitter](https://img.shields.io/badge/Twitter-%40lashraf96-blue)](https://twitter.com/lashraf96) @@ -114,7 +115,7 @@ uploadObservable | Platform | Minimum Swift Version | Installation | Status | | --- | --- | --- | --- | -| iOS(iPadOS) 14.0+ / macOS 11.0+ / watchOS 7.0+ | 5.3 | [Swift Package Manager](#swift-package-manager), [Manual](#manually) | Fully Tested | +| iOS(iPadOS) 14.0+ / macOS 11.0+ / tvOS 14.0+ / watchOS 7.0+ | 5.3 | [Swift Package Manager](#swift-package-manager), [Manual](#manually) | Fully Tested | ## Installation From 377cc67ce9b3fbb70bfd9469fa0b0d53fe61c535 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 10 Jan 2024 04:24:05 +0200 Subject: [PATCH 87/95] feat: update Dummy project used to verify SPM Resolves: none. --- .../SPMDummy.xcodeproj/project.pbxproj | 22 +++++++++++++++---- .../xcshareddata/swiftpm/Package.resolved | 9 ++++++++ .../workflows/SPMDummy/SPMDummy/MainApp.swift | 19 ++++++++++++++++ .../SPMDummy/SPMDummy/MainView.swift | 16 ++++++++++++++ .../SPMDummy/SPMDummy/ViewController.swift | 2 +- .github/workflows/spm-lint.yml | 4 +++- Package.swift | 2 +- 7 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/SPMDummy/SPMDummy/MainApp.swift create mode 100644 .github/workflows/SPMDummy/SPMDummy/MainView.swift diff --git a/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.pbxproj b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.pbxproj index aadf94c..471c1dc 100644 --- a/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.pbxproj +++ b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ C60771BC2A98525600268543 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C60771BA2A98525600268543 /* Main.storyboard */; platformFilter = ios; }; C60771BE2A98525A00268543 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C60771BD2A98525A00268543 /* Assets.xcassets */; }; C60771C12A98525A00268543 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C60771BF2A98525A00268543 /* LaunchScreen.storyboard */; platformFilter = ios; }; + C66F79FE2B4E356F00FDA1AC /* MainApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66F79FD2B4E356F00FDA1AC /* MainApp.swift */; }; + C66F7A002B4E357D00FDA1AC /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C66F79FF2B4E357D00FDA1AC /* MainView.swift */; }; C6C5417E2A98CDCD00BDA70B /* RxNetworkKit in Frameworks */ = {isa = PBXBuildFile; productRef = C6C5417D2A98CDCD00BDA70B /* RxNetworkKit */; }; /* End PBXBuildFile section */ @@ -26,6 +28,8 @@ C60771C02A98525A00268543 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; C60771C22A98525A00268543 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C60771CA2A98531500268543 /* RxNetworkKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = RxNetworkKit; path = ../../..; sourceTree = ""; }; + C66F79FD2B4E356F00FDA1AC /* MainApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainApp.swift; sourceTree = ""; }; + C66F79FF2B4E357D00FDA1AC /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -68,6 +72,8 @@ C60771BD2A98525A00268543 /* Assets.xcassets */, C60771BF2A98525A00268543 /* LaunchScreen.storyboard */, C60771C22A98525A00268543 /* Info.plist */, + C66F79FD2B4E356F00FDA1AC /* MainApp.swift */, + C66F79FF2B4E357D00FDA1AC /* MainView.swift */, ); path = SPMDummy; sourceTree = ""; @@ -162,7 +168,9 @@ buildActionMask = 2147483647; files = ( C60771B92A98525600268543 /* ViewController.swift in Sources */, + C66F79FE2B4E356F00FDA1AC /* MainApp.swift in Sources */, C60771B52A98525600268543 /* AppDelegate.swift in Sources */, + C66F7A002B4E357D00FDA1AC /* MainView.swift in Sources */, C60771B72A98525600268543 /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -318,19 +326,22 @@ INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.las.SPMDummy; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 14.0; }; name = Debug; }; @@ -349,19 +360,22 @@ INFOPLIST_KEY_UIMainStoryboardFile = Main; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.las.SPMDummy; PRODUCT_NAME = "$(TARGET_NAME)"; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,3"; + TVOS_DEPLOYMENT_TARGET = 14.0; }; name = Release; }; diff --git a/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3d62ac3..8324ff6 100644 --- a/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/.github/workflows/SPMDummy/SPMDummy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,6 +1,15 @@ { "object": { "pins": [ + { + "package": "CoreHTTP", + "repositoryURL": "https://github.com/loay-ashraf/CoreHTTP", + "state": { + "branch": null, + "revision": "17b030a4a10ac9449e6c107bce13f112280aae6b", + "version": "1.0.0" + } + }, { "package": "RxSwift", "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", diff --git a/.github/workflows/SPMDummy/SPMDummy/MainApp.swift b/.github/workflows/SPMDummy/SPMDummy/MainApp.swift new file mode 100644 index 0000000..892a32e --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy/MainApp.swift @@ -0,0 +1,19 @@ +// +// MainApp.swift +// SPMDummy +// +// Created by Loay Ashraf on 10/01/2024. +// + +#if os(watchOS) +import SwiftUI + +@main +struct MainApp: App { + var body: some Scene { + WindowGroup { + MainView() + } + } +} +#endif diff --git a/.github/workflows/SPMDummy/SPMDummy/MainView.swift b/.github/workflows/SPMDummy/SPMDummy/MainView.swift new file mode 100644 index 0000000..66facf4 --- /dev/null +++ b/.github/workflows/SPMDummy/SPMDummy/MainView.swift @@ -0,0 +1,16 @@ +// +// MainView.swift +// SPMDummy +// +// Created by Loay Ashraf on 10/01/2024. +// + +#if os(watchOS) +import SwiftUI + +struct MainView: View { + var body: some View { + EmptyView() + } +} +#endif diff --git a/.github/workflows/SPMDummy/SPMDummy/ViewController.swift b/.github/workflows/SPMDummy/SPMDummy/ViewController.swift index 5dda1ee..f0a8c6f 100644 --- a/.github/workflows/SPMDummy/SPMDummy/ViewController.swift +++ b/.github/workflows/SPMDummy/SPMDummy/ViewController.swift @@ -5,7 +5,7 @@ // Created by Loay Ashraf on 25/08/2023. // -#if os(iOS) +#if os(iOS) || os(tvOS) import UIKit class ViewController: UIViewController { diff --git a/.github/workflows/spm-lint.yml b/.github/workflows/spm-lint.yml index 6f4dc84..a784295 100644 --- a/.github/workflows/spm-lint.yml +++ b/.github/workflows/spm-lint.yml @@ -17,7 +17,9 @@ jobs: matrix: destination: ['generic/platform=iOS Simulator', 'generic/platform=iOS', - 'generic/platform=macOS'] + 'generic/platform=macOS', + 'generic/platform=tvOS Simulator', + 'generic/platform=tvOS'] if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: diff --git a/Package.swift b/Package.swift index 4f434c3..30ed564 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,7 @@ import PackageDescription let package = Package( name: "RxNetworkKit", platforms: [ - .iOS(.v14), .macOS(.v11), .watchOS(.v7) + .iOS(.v14), .macOS(.v11), .tvOS(.v14), .watchOS(.v7) ], products: [ .library(name: "RxNetworkKit", targets: ["RxNetworkKit"]), From 4e1721447569aa41d73f9c5637584860e4a3b4ae Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 10 Jan 2024 12:51:41 +0200 Subject: [PATCH 88/95] fix: update README.md + add missing initializers Resolves: none. --- README.md | 34 +++++++++++-------- .../Parameters/HTTPUploadRequestFile.swift | 4 +-- .../HTTPUploadRequestFormData.swift | 11 ++++++ 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1e6acad..cce9192 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,14 @@ It makes use of RxSwift's traits at request level to acheive a high level of spe ### Simple API Call: ``` -// Create 'Network Manager' instance. -let manager = NetworkManager(configuration: .default, requestInterceptor: self, eventMonitor: self) -// Create request router object. +// Create `Session` instance. +let session = Session(configuration: .default, eventMonitor: self) +// Create 'RESTClient' instance. +let restClient = RESTClient(session: session, requestInterceptor: self) +// Create `RequestRouter` instance. let router = Router.default // Make request observable sequence using request router. -let single: Single = manager.request(router) +let single: Single = restClient.request(router) // Subscrible to sequence and observe events. single .observe(on: MainScheduler.instance) @@ -54,12 +56,14 @@ single ### Download Request: ``` -// Create 'Network Manager' instance. -let manager = NetworkManager(configuration: .default, requestInterceptor: self, eventMonitor: self) -// Create download request router object. +// Create `Session` instance. +let session = Session(configuration: .default, eventMonitor: self) +// Create 'HTTPClient' instance. +let httpClient = HTTPClient(session: session, requestInterceptor: self) +// Create `HTTPDownloadRequestRouter` instance. let router = DownloadRouter.default // Make download request observable sequence using request router. -let downloadObservable: Observable = manager.download(router) +let downloadObservable: Observable = httpClient.download(router) // Subscrible to sequence and observe events. downloadObservable .observe(on: MainScheduler.instance) @@ -83,15 +87,17 @@ downloadObservable ### Upload Request: ``` -// Create 'Network Manager' instance. -let manager = NetworkManager(configuration: .default, requestInterceptor: self, eventMonitor: self) -// Create upload request router object. +// Create `Session` instance. +let session = Session(configuration: .default, eventMonitor: self) +// Create 'HTTPClient' instance. +let httpClient = HTTPClient(session: session, requestInterceptor: self) +// Create `HTTPUploadRequestRouter` instance. let router = UploadRouter.default -// Make 'UploadFile' object. +// Make 'HTTPUploadRequestFile' instance. let fileData = Data() guard let file = UploadFile(forKey: UUID().uuidString, withName: "testFile.txt", withData: fileData) else { return } -// Make upload request observable sequence using request router and uploa file object. -let uploadObservable: Observable> = manager.upload(router, file) +// Make upload request observable sequence using request router and upload file object. +let uploadObservable: Observable> = httpClient.upload(router, file) // Subscrible to sequence and observe events. uploadObservable .observe(on: MainScheduler.instance) diff --git a/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFile.swift b/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFile.swift index 10d84c6..ac7384f 100644 --- a/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFile.swift +++ b/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFile.swift @@ -27,7 +27,7 @@ public struct HTTPUploadRequestFile { /// - key: file key or id. /// - name: file name. /// - data: `Data` object for file. - init?(forKey key: String, withName name: String, withData data: Data) { + public init?(forKey key: String, withName name: String, withData data: Data) { self.key = key self.name = name self.url = nil @@ -41,7 +41,7 @@ public struct HTTPUploadRequestFile { /// - Parameters: /// - key: file key or id. /// - url: local `URL` for the file. - init?(forKey key: String, withURL url: URL) { + public init?(forKey key: String, withURL url: URL) { let name = url.lastPathComponent self.key = key self.name = name diff --git a/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFormData.swift b/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFormData.swift index e4f0a7e..ab3afe4 100644 --- a/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFormData.swift +++ b/Source/HTTP/Types/Request/Parameters/HTTPUploadRequestFormData.swift @@ -15,4 +15,15 @@ public struct HTTPUploadRequestFormData { /// files to be included in the form HTTP body. let files: [HTTPUploadRequestFile] + + /// Creates `HTTPUploadRequestFormData` instance. + /// + /// - Parameters: + /// - parameters: `[String: String]` parameters (text data fields) to be included in the form HTTP body. + /// - files: `[HTTPUploadRequestFile]` files to be included in the form HTTP body. + public init(parameters: [String: String], files: [HTTPUploadRequestFile]) { + self.parameters = parameters + self.files = files + } + } From 8f4f12091889c4d0e61c5fcb73097cfcd367d67b Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 10 Jan 2024 12:55:10 +0200 Subject: [PATCH 89/95] Update RxNetworkKit.md --- Docs.docc/Pages/RxNetworkKit.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Docs.docc/Pages/RxNetworkKit.md b/Docs.docc/Pages/RxNetworkKit.md index 8e269f6..4751a72 100644 --- a/Docs.docc/Pages/RxNetworkKit.md +++ b/Docs.docc/Pages/RxNetworkKit.md @@ -27,14 +27,14 @@ It makes use of RxSwift's traits at request level to acheive a high level of spe - ``HTTPScheme`` - ``HTTPMethod`` - ``HTTPStatusCode`` -- ``HTTPBodyError`` -- ``DefaultHTTPBodyError`` ### Error - ``HTTPError`` - ``HTTPAPIError`` - ``DefaultHTTPAPIError`` +- ``HTTPBodyError`` +- ``DefaultHTTPBodyError`` - ``HTTPClientError`` - ``HTTPServerError`` From c435325aa4463f16b8db6e1a6eaaee6b3ae5e797 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 10 Jan 2024 12:55:45 +0200 Subject: [PATCH 90/95] Update spm-lint.yml --- .github/workflows/spm-lint.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/spm-lint.yml b/.github/workflows/spm-lint.yml index a784295..94222b6 100644 --- a/.github/workflows/spm-lint.yml +++ b/.github/workflows/spm-lint.yml @@ -18,8 +18,7 @@ jobs: destination: ['generic/platform=iOS Simulator', 'generic/platform=iOS', 'generic/platform=macOS', - 'generic/platform=tvOS Simulator', - 'generic/platform=tvOS'] + 'generic/platform=tvOS Simulator'] if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: From fd616bd16797e9086ed4661c0051677bd0146ef0 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 10 Jan 2024 13:31:02 +0200 Subject: [PATCH 91/95] fix: update ci file + remove typo Resolves: none. --- .github/workflows/spm-lint.yml | 3 +-- README.md | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/spm-lint.yml b/.github/workflows/spm-lint.yml index 94222b6..6f4dc84 100644 --- a/.github/workflows/spm-lint.yml +++ b/.github/workflows/spm-lint.yml @@ -17,8 +17,7 @@ jobs: matrix: destination: ['generic/platform=iOS Simulator', 'generic/platform=iOS', - 'generic/platform=macOS', - 'generic/platform=tvOS Simulator'] + 'generic/platform=macOS'] if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: diff --git a/README.md b/README.md index cce9192..64d72bc 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ It makes use of RxSwift's traits at request level to acheive a high level of spe let session = Session(configuration: .default, eventMonitor: self) // Create 'RESTClient' instance. let restClient = RESTClient(session: session, requestInterceptor: self) -// Create `RequestRouter` instance. +// Create `HTTPRequestRouter` instance. let router = Router.default // Make request observable sequence using request router. let single: Single = restClient.request(router) @@ -133,7 +133,7 @@ Once you have your Swift package set up, adding RxNetworkKit as a dependency is ```swift dependencies: [ - .package(url: "https://github.com/loay-ashraf/RxNetworkKit.git", .upToNextMajor(from: "0.0.1")) + .package(url: "https://github.com/loay-ashraf/RxNetworkKit.git", .upToNextMajor(from: "1.0.0")) ] ``` From ab41447cf478709f202ab795a8fcf2f6d789bf23 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 25 Jan 2024 15:04:51 +0200 Subject: [PATCH 92/95] Feature/add request logging (#67) * feat: add HTTPRequestLogger and SessionConfiguration Resolves: none. * fix: update examples Resolves: none. * feat: remove un-needed files + update RequestRouter Resolves: none * Update Package.resolved --- .../Controller/ViewController.swift | 3 +- .../Controller/ViewController.swift | 3 +- .../Controller/ViewController.swift | 3 +- .../watchOS Example/View/MainView.swift | 3 +- RxNetworkKit.xcodeproj/project.pbxproj | 64 +++++++- .../xcshareddata/swiftpm/Package.resolved | 4 +- .../CoreExample.xcodeproj/project.pbxproj | 4 - .../Source/Network/RequestEventMonitor.swift | 26 ---- .../Source/Network/RequestRouter.swift | 4 - Source/HTTP/Extensions/Data+JSON.swift | 20 +++ .../Extensions/Observable+Decodable.swift | 1 + .../Reactive+URLSessionDownloadResponse.swift | 38 +---- .../Reactive+URLSessionUploadResponse.swift | 38 +---- .../Extensions/URLRequest+CURLCommand.swift | 40 +++++ .../Extensions/URLSession+DownloadTask.swift | 12 ++ .../Extensions/URLSession+LogRequests.swift | 15 ++ .../URLSession+OutgoingRequest.swift | 25 ++++ .../Extensions/URLSession+UploadTask.swift | 13 ++ .../Request/Logger/HTTPRequestLogger.swift | 140 ++++++++++++++++++ .../Router/HTTPDownloadRequestRouter.swift | 1 - .../Extensions/Reactive+RESTResponse.swift | 62 ++++++++ Source/REST/Types/Client/RESTClient.swift | 4 +- .../ProcessInfo+operatingSystemName.swift | 0 ...onfiguration+setAdditionalHTTPHeader.swift | 0 ...Configuration+setUserAgentHTTPHeader.swift | 2 +- Source/Session/Session.swift | 40 ----- Source/Session/Types/Session.swift | 31 ++++ .../Session/Types/SessionConfiguration.swift | 37 +++++ 28 files changed, 479 insertions(+), 154 deletions(-) delete mode 100644 Shared/CoreExample/Source/Network/RequestEventMonitor.swift create mode 100644 Source/HTTP/Extensions/Data+JSON.swift create mode 100644 Source/HTTP/Extensions/URLRequest+CURLCommand.swift create mode 100644 Source/HTTP/Extensions/URLSession+LogRequests.swift create mode 100644 Source/HTTP/Extensions/URLSession+OutgoingRequest.swift create mode 100644 Source/HTTP/Types/Request/Logger/HTTPRequestLogger.swift create mode 100644 Source/REST/Extensions/Reactive+RESTResponse.swift rename Source/{HTTP => Session}/Extensions/ProcessInfo+operatingSystemName.swift (100%) rename Source/{HTTP => Session}/Extensions/URLSessionConfiguration+setAdditionalHTTPHeader.swift (100%) rename Source/{HTTP => Session}/Extensions/URLSessionConfiguration+setUserAgentHTTPHeader.swift (90%) delete mode 100644 Source/Session/Session.swift create mode 100644 Source/Session/Types/Session.swift create mode 100644 Source/Session/Types/SessionConfiguration.swift diff --git a/Examples/iOS/iOS Example/Controller/ViewController.swift b/Examples/iOS/iOS Example/Controller/ViewController.swift index 5045a12..01fa751 100644 --- a/Examples/iOS/iOS Example/Controller/ViewController.swift +++ b/Examples/iOS/iOS Example/Controller/ViewController.swift @@ -42,8 +42,7 @@ class ViewController: UIViewController { /// Initializes ViewModel object. private func setupViewModel() { let requestInterceptor = RequestInterceptor() - let requestEventMointor = RequestEventMonitor() - let session = Session(configuration: .default, eventMonitor: requestEventMointor) + let session = Session(configuration: .default) let restClient = RESTClient(session: session, requestInterceptor: requestInterceptor) let httpClient = HTTPClient(session: session, requestInterceptor: requestInterceptor) viewModel = .init(restClient: restClient, httpClient: httpClient) diff --git a/Examples/macOS/macOS Example/Controller/ViewController.swift b/Examples/macOS/macOS Example/Controller/ViewController.swift index efb1e4f..43b1166 100644 --- a/Examples/macOS/macOS Example/Controller/ViewController.swift +++ b/Examples/macOS/macOS Example/Controller/ViewController.swift @@ -45,8 +45,7 @@ class ViewController: NSViewController { /// Initializes ViewModel object. private func setupViewModel() { let requestInterceptor = RequestInterceptor() - let requestEventMointor = RequestEventMonitor() - let session = Session(configuration: .default, eventMonitor: requestEventMointor) + let session = Session(configuration: .default) let restClient = RESTClient(session: session, requestInterceptor: requestInterceptor) let httpClient = HTTPClient(session: session, requestInterceptor: requestInterceptor) viewModel = .init(restClient: restClient, httpClient: httpClient) diff --git a/Examples/tvOS/tvOS Example/Controller/ViewController.swift b/Examples/tvOS/tvOS Example/Controller/ViewController.swift index da5f1e8..115b80a 100644 --- a/Examples/tvOS/tvOS Example/Controller/ViewController.swift +++ b/Examples/tvOS/tvOS Example/Controller/ViewController.swift @@ -39,8 +39,7 @@ class ViewController: UIViewController { /// Initializes ViewModel object. private func setupViewModel() { let requestInterceptor = RequestInterceptor() - let requestEventMointor = RequestEventMonitor() - let session = Session(configuration: .default, eventMonitor: requestEventMointor) + let session = Session(configuration: .default) let restClient = RESTClient(session: session, requestInterceptor: requestInterceptor) let httpClient = HTTPClient(session: session, requestInterceptor: requestInterceptor) viewModel = .init(restClient: restClient, httpClient: httpClient) diff --git a/Examples/watchOS/watchOS Example/View/MainView.swift b/Examples/watchOS/watchOS Example/View/MainView.swift index 24d77dc..1b7d425 100644 --- a/Examples/watchOS/watchOS Example/View/MainView.swift +++ b/Examples/watchOS/watchOS Example/View/MainView.swift @@ -16,8 +16,7 @@ struct MainView: View { /// Initializer init() { let requestInterceptor = RequestInterceptor() - let requestEventMointor = RequestEventMonitor() - let session = Session(configuration: .default, eventMonitor: requestEventMointor) + let session = Session(configuration: .default) let restClient = RESTClient(session: session, requestInterceptor: requestInterceptor) let httpClient = HTTPClient(session: session, requestInterceptor: requestInterceptor) viewModel = .init(restClient: restClient, httpClient: httpClient) diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index b0aa15c..1ce37b7 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -41,6 +41,13 @@ 0B77E0BD29D968DE0077FBC0 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BC29D968DE0077FBC0 /* RxSwift */; }; 0B77E0C029D969370077FBC0 /* RxSwiftExt in Frameworks */ = {isa = PBXBuildFile; productRef = 0B77E0BF29D969370077FBC0 /* RxSwiftExt */; }; C6049B162A95307800E5727E /* RxNetworkKit.h in Headers */ = {isa = PBXBuildFile; fileRef = C6049B152A95307800E5727E /* RxNetworkKit.h */; }; + C61A7E242B6276F800407C38 /* HTTPRequestLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C61A7E232B6276F800407C38 /* HTTPRequestLogger.swift */; }; + C61A7E262B62782D00407C38 /* Reactive+RESTResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = C61A7E252B62782D00407C38 /* Reactive+RESTResponse.swift */; }; + C61A7E2A2B62794900407C38 /* URLSession+OutgoingRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C61A7E292B62794900407C38 /* URLSession+OutgoingRequest.swift */; }; + C61A7E2C2B62798800407C38 /* URLRequest+CURLCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C61A7E2B2B62798800407C38 /* URLRequest+CURLCommand.swift */; }; + C61A7E2E2B6279C700407C38 /* Data+JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = C61A7E2D2B6279C700407C38 /* Data+JSON.swift */; }; + C61A7E302B627B3000407C38 /* SessionConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C61A7E2F2B627B3000407C38 /* SessionConfiguration.swift */; }; + C61A7E322B62885F00407C38 /* URLSession+LogRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C61A7E312B62885F00407C38 /* URLSession+LogRequests.swift */; }; C61CB5642AF2CFDD006A203A /* Session.swift in Sources */ = {isa = PBXBuildFile; fileRef = C61CB5632AF2CFDD006A203A /* Session.swift */; }; C623E7B42AD6262A00A20A0A /* CoreHTTP in Frameworks */ = {isa = PBXBuildFile; productRef = C623E7B32AD6262A00A20A0A /* CoreHTTP */; }; C6554A2B2AD5BBB60090DD3A /* RESTClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6554A2A2AD5BBB60090DD3A /* RESTClient.swift */; }; @@ -93,6 +100,13 @@ 0B77E08629D965D30077FBC0 /* HTTPDownloadRequestRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPDownloadRequestRouter.swift; sourceTree = ""; }; 0B77E08829D965D30077FBC0 /* HTTPUploadRequestRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPUploadRequestRouter.swift; sourceTree = ""; }; C6049B152A95307800E5727E /* RxNetworkKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RxNetworkKit.h; sourceTree = ""; }; + C61A7E232B6276F800407C38 /* HTTPRequestLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPRequestLogger.swift; sourceTree = ""; }; + C61A7E252B62782D00407C38 /* Reactive+RESTResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Reactive+RESTResponse.swift"; sourceTree = ""; }; + C61A7E292B62794900407C38 /* URLSession+OutgoingRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+OutgoingRequest.swift"; sourceTree = ""; }; + C61A7E2B2B62798800407C38 /* URLRequest+CURLCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+CURLCommand.swift"; sourceTree = ""; }; + C61A7E2D2B6279C700407C38 /* Data+JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+JSON.swift"; sourceTree = ""; }; + C61A7E2F2B627B3000407C38 /* SessionConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionConfiguration.swift; sourceTree = ""; }; + C61A7E312B62885F00407C38 /* URLSession+LogRequests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+LogRequests.swift"; sourceTree = ""; }; C61CB5632AF2CFDD006A203A /* Session.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Session.swift; sourceTree = ""; }; C6554A2A2AD5BBB60090DD3A /* RESTClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTClient.swift; sourceTree = ""; }; C6554A2C2AD5C1560090DD3A /* HTTPClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPClient.swift; sourceTree = ""; }; @@ -180,10 +194,38 @@ path = Reachability; sourceTree = ""; }; - C61CB5622AF2CF31006A203A /* Session */ = { + C61A7E222B6276D900407C38 /* Logger */ = { + isa = PBXGroup; + children = ( + C61A7E232B6276F800407C38 /* HTTPRequestLogger.swift */, + ); + path = Logger; + sourceTree = ""; + }; + C61A7E272B6278A900407C38 /* Types */ = { isa = PBXGroup; children = ( C61CB5632AF2CFDD006A203A /* Session.swift */, + C61A7E2F2B627B3000407C38 /* SessionConfiguration.swift */, + ); + path = Types; + sourceTree = ""; + }; + C61A7E282B6278B500407C38 /* Extensions */ = { + isa = PBXGroup; + children = ( + C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */, + C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */, + C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + C61CB5622AF2CF31006A203A /* Session */ = { + isa = PBXGroup; + children = ( + C61A7E282B6278B500407C38 /* Extensions */, + C61A7E272B6278A900407C38 /* Types */, ); path = Session; sourceTree = ""; @@ -220,6 +262,7 @@ isa = PBXGroup; children = ( C6554A342AD5C6830090DD3A /* Event */, + C61A7E222B6276D900407C38 /* Logger */, C6554A2E2AD5C4F30090DD3A /* Parameters */, C6554A312AD5C5940090DD3A /* Router */, ); @@ -247,9 +290,9 @@ isa = PBXGroup; children = ( 0B77E05529D965D30077FBC0 /* Data+AppendString.swift */, + C61A7E2D2B6279C700407C38 /* Data+JSON.swift */, 0B77E08029D965D30077FBC0 /* Observable+Decodable.swift */, 0B77E07F29D965D30077FBC0 /* Observable+Retry.swift */, - C6A9BEFE2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift */, 0B77E08429D965D30077FBC0 /* Reactive+Curl.swift */, 0B77E05029D965D30077FBC0 /* Reactive+URLSessionAdaptedDownloadResponse.swift */, 0B77E05629D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift */, @@ -258,10 +301,11 @@ C6BDFFEB2ACDF4100022F675 /* Reactive+WebSocketPing.swift */, C6BDFFE72ACDF3830022F675 /* Reactive+WebSocketReceive.swift */, C6BDFFE92ACDF3D90022F675 /* Reactive+WebSocketSend.swift */, + C61A7E2B2B62798800407C38 /* URLRequest+CURLCommand.swift */, 0B77E04F29D965D30077FBC0 /* URLSession+DownloadTask.swift */, + C61A7E312B62885F00407C38 /* URLSession+LogRequests.swift */, + C61A7E292B62794900407C38 /* URLSession+OutgoingRequest.swift */, 0B77E05429D965D30077FBC0 /* URLSession+UploadTask.swift */, - C6A9BEF92A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift */, - C6A9BEFC2A93FAF100459E32 /* URLSessionConfiguration+setUserAgentHTTPHeader.swift */, ); path = Extensions; sourceTree = ""; @@ -269,10 +313,11 @@ C6554A372AD5C7AD0090DD3A /* Extensions */ = { isa = PBXGroup; children = ( + 0B77E08229D965D30077FBC0 /* Completable+Retry.swift */, + C61A7E252B62782D00407C38 /* Reactive+RESTResponse.swift */, 0B77E07B29D965D30077FBC0 /* Single+CatchErrors.swift */, 0B77E07D29D965D30077FBC0 /* Single+Decodable.swift */, 0B77E07A29D965D30077FBC0 /* Single+Decode.swift */, - 0B77E08229D965D30077FBC0 /* Completable+Retry.swift */, 0B77E07929D965D30077FBC0 /* Single+Retry.swift */, 0B77E07C29D965D30077FBC0 /* Single+VerifyResponse.swift */, ); @@ -421,6 +466,7 @@ C6A9BEFF2A93FB1D00459E32 /* ProcessInfo+operatingSystemName.swift in Sources */, 0B77E08E29D965D30077FBC0 /* HTTPUploadRequestFile.swift in Sources */, 0B77E08D29D965D30077FBC0 /* HTTPUploadRequestFormData.swift in Sources */, + C61A7E262B62782D00407C38 /* Reactive+RESTResponse.swift in Sources */, 0B77E0AF29D965D30077FBC0 /* Single+Decodable.swift in Sources */, C69A78562ACF001400ECF092 /* Docs.docc in Sources */, 0B77E0A129D965D30077FBC0 /* NWInterfaceType+RawRepresentable.swift in Sources */, @@ -431,6 +477,8 @@ 0B77E09129D965D30077FBC0 /* Reactive+URLSessionAdaptedUploadResponse.swift in Sources */, C6A9BEFA2A93F16200459E32 /* URLSessionConfiguration+setAdditionalHTTPHeader.swift in Sources */, C6BDFFF32ACDF5100022F675 /* WebSocketMessage.swift in Sources */, + C61A7E242B6276F800407C38 /* HTTPRequestLogger.swift in Sources */, + C61A7E302B627B3000407C38 /* SessionConfiguration.swift in Sources */, C6B4B4C42AD47A2F009073ED /* WebSocketError.swift in Sources */, 0B77E0A429D965D30077FBC0 /* NetworkReachability.swift in Sources */, 0B77E0B229D965D30077FBC0 /* Completable+Retry.swift in Sources */, @@ -439,6 +487,7 @@ C6BDFFF52ACDF5250022F675 /* WebSocketCloseCode.swift in Sources */, 0B77E0B629D965D30077FBC0 /* HTTPUploadRequestRouter.swift in Sources */, 0B77E0AB29D965D30077FBC0 /* Single+Retry.swift in Sources */, + C61A7E2C2B62798800407C38 /* URLRequest+CURLCommand.swift in Sources */, 0B77E08F29D965D30077FBC0 /* URLSession+UploadTask.swift in Sources */, 0B77E08C29D965D30077FBC0 /* Reactive+URLSessionAdaptedDownloadResponse.swift in Sources */, 0B77E0A229D965D30077FBC0 /* NWInterfaceType+CaseIterable.swift in Sources */, @@ -446,12 +495,15 @@ C61CB5642AF2CFDD006A203A /* Session.swift in Sources */, 0B77E09329D965D30077FBC0 /* Reactive+URLSessionUploadResponse.swift in Sources */, 0B77E0AC29D965D30077FBC0 /* Single+Decode.swift in Sources */, + C61A7E2E2B6279C700407C38 /* Data+JSON.swift in Sources */, 0B77E0A329D965D30077FBC0 /* NetworkInterfaceType.swift in Sources */, 0B77E09229D965D30077FBC0 /* HTTPUploadRequestEvent.swift in Sources */, 0B77E09029D965D30077FBC0 /* Data+AppendString.swift in Sources */, C6BDFFE82ACDF3830022F675 /* Reactive+WebSocketReceive.swift in Sources */, + C61A7E322B62885F00407C38 /* URLSession+LogRequests.swift in Sources */, 0B77E08929D965D30077FBC0 /* Reactive+URLSessionDownloadResponse.swift in Sources */, C6554A2D2AD5C1560090DD3A /* HTTPClient.swift in Sources */, + C61A7E2A2B62794900407C38 /* URLSession+OutgoingRequest.swift in Sources */, C6BDFFEC2ACDF4100022F675 /* Reactive+WebSocketPing.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -708,7 +760,7 @@ repositoryURL = "https://github.com/loay-ashraf/CoreHTTP"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 1.0.0; + minimumVersion = 2.0.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved index e903d19..e39f16b 100644 --- a/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/RxNetworkKit.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/loay-ashraf/CoreHTTP", "state" : { - "revision" : "17b030a4a10ac9449e6c107bce13f112280aae6b", - "version" : "1.0.0" + "revision" : "d38c3063412e7121bbb5a1fcef3089d847865266", + "version" : "2.0.1" } }, { diff --git a/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj b/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj index c908e4d..9a77aec 100644 --- a/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj +++ b/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ C62E15252AD77BDF003CB8FA /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62E14DC2AD77747003CB8FA /* ViewModel.swift */; }; C62E15292AD77C39003CB8FA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = C62E15282AD77C39003CB8FA /* RxSwift */; }; C62E153E2AD78417003CB8FA /* RequestInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62E153D2AD78417003CB8FA /* RequestInterceptor.swift */; }; - C62E15402AD78483003CB8FA /* RequestEventMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C62E153F2AD78483003CB8FA /* RequestEventMonitor.swift */; }; C63EEB352AD7BE17003A64CA /* RxNetworkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C62E15042AD77AA4003CB8FA /* RxNetworkKit.framework */; }; C63EEB4A2AD7C4B6003A64CA /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = C63EEB492AD7C4B6003A64CA /* RxCocoa */; }; /* End PBXBuildFile section */ @@ -32,7 +31,6 @@ C62E15172AD77B8F003CB8FA /* CoreExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C62E15192AD77B8F003CB8FA /* CoreExample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreExample.h; sourceTree = ""; }; C62E153D2AD78417003CB8FA /* RequestInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestInterceptor.swift; sourceTree = ""; }; - C62E153F2AD78483003CB8FA /* RequestEventMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestEventMonitor.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -92,7 +90,6 @@ C62E14D52AD77729003CB8FA /* DownloadRequestRouter.swift */, C62E14D42AD77729003CB8FA /* RequestRouter.swift */, C62E153D2AD78417003CB8FA /* RequestInterceptor.swift */, - C62E153F2AD78483003CB8FA /* RequestEventMonitor.swift */, ); path = Network; sourceTree = ""; @@ -215,7 +212,6 @@ C62E153E2AD78417003CB8FA /* RequestInterceptor.swift in Sources */, C62E15232AD77BDA003CB8FA /* ViewLoadType.swift in Sources */, C62E15212AD77BD3003CB8FA /* RequestRouter.swift in Sources */, - C62E15402AD78483003CB8FA /* RequestEventMonitor.swift in Sources */, C62E15202AD77BCC003CB8FA /* Model.swift in Sources */, C62E15252AD77BDF003CB8FA /* ViewModel.swift in Sources */, ); diff --git a/Shared/CoreExample/Source/Network/RequestEventMonitor.swift b/Shared/CoreExample/Source/Network/RequestEventMonitor.swift deleted file mode 100644 index fa1de5d..0000000 --- a/Shared/CoreExample/Source/Network/RequestEventMonitor.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// RequestEventMonitor.swift -// CoreExample -// -// Created by Loay Ashraf on 12/10/2023. -// - -import RxNetworkKit - -public class RequestEventMonitor: NSObject, HTTPRequestEventMonitor { - public override init() { } - public func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) { - debugPrint("") - } - public func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { - debugPrint(session) - } - public func urlSession(_ session: URLSession, didCreateTask task: URLSessionTask) { - debugPrint("Task created!") - } - public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { - guard let error else { return } - debugPrint("Task did finish with error: \(error.localizedDescription)!") - } - public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { } -} diff --git a/Shared/CoreExample/Source/Network/RequestRouter.swift b/Shared/CoreExample/Source/Network/RequestRouter.swift index ecc9702..ffb8697 100644 --- a/Shared/CoreExample/Source/Network/RequestRouter.swift +++ b/Shared/CoreExample/Source/Network/RequestRouter.swift @@ -31,8 +31,4 @@ public enum RequestRouter: HTTPRequestRouter { public var body: [String : Any]? { nil } - public var url: URL? { - let urlString = scheme.rawValue + domain + "/" + path - return URL(string: urlString) - } } diff --git a/Source/HTTP/Extensions/Data+JSON.swift b/Source/HTTP/Extensions/Data+JSON.swift new file mode 100644 index 0000000..4f261ac --- /dev/null +++ b/Source/HTTP/Extensions/Data+JSON.swift @@ -0,0 +1,20 @@ +// +// Data+JSON.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 25/01/2024. +// + +import Foundation + +extension Data { + + /// JSON formatted string for this `Data` object. + var json: String? { + guard let object = try? JSONSerialization.jsonObject(with: self, options: []), + let data = try? JSONSerialization.data(withJSONObject: object, options: [.prettyPrinted, .withoutEscapingSlashes]) else { return nil } + let prettyPrintedString = String(decoding: data, as: UTF8.self) + return prettyPrintedString + } + +} diff --git a/Source/HTTP/Extensions/Observable+Decodable.swift b/Source/HTTP/Extensions/Observable+Decodable.swift index 27fdde0..09e8603 100644 --- a/Source/HTTP/Extensions/Observable+Decodable.swift +++ b/Source/HTTP/Extensions/Observable+Decodable.swift @@ -7,6 +7,7 @@ import Foundation import RxSwift +import RxCocoa extension Observable where Element == (response: HTTPURLResponse, data: Data) { /// Creates `Completable` observable + handles transport errors. diff --git a/Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift b/Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift index ce815fb..b8bf369 100644 --- a/Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift +++ b/Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift @@ -22,23 +22,12 @@ extension Reactive where Base: URLSession { var taskProgressObservation: NSKeyValueObservation? let taskProgressSubject = PublishSubject() let taskResponseSingle = Single<(response: HTTPURLResponse, data: Data)>.create { single in - // smart compiler should be able to optimize this out - let d: Date? - if URLSession.rx.shouldLogRequest(request) { - d = Date() - } else { - d = nil - } let task = self.base.fileDownloadTask(with: request) { data, response, error in - if URLSession.rx.shouldLogRequest(request) { - let interval = Date().timeIntervalSince(d ?? Date()) - print(convertURLRequestToCurlCommand(request)) -#if os(Linux) - print(convertResponseToString(response, error.flatMap { $0 as NSError }, interval)) -#else - print(convertResponseToString(response, error.map { $0 as NSError }, interval)) -#endif +#if DEBUG + if URLSession.logRequests { + HTTPRequestLogger.shared.log(response: (response, data, error), bodyPlaceholder: "[File Body]") } +#endif guard let response = response, let data = data else { single(.failure(error ?? RxCocoaURLError.unknown)) return @@ -74,23 +63,12 @@ extension Reactive where Base: URLSession { var taskProgressObservation: NSKeyValueObservation? let taskProgressSubject = PublishSubject() let taskResponseSingle = Single<(response: HTTPURLResponse, data: Data)>.create { single in - // smart compiler should be able to optimize this out - let d: Date? - if URLSession.rx.shouldLogRequest(request) { - d = Date() - } else { - d = nil - } let task = self.base.fileDownloadTask(with: request, saveTo: url) { data, response, error in - if URLSession.rx.shouldLogRequest(request) { - let interval = Date().timeIntervalSince(d ?? Date()) - print(convertURLRequestToCurlCommand(request)) -#if os(Linux) - print(convertResponseToString(response, error.flatMap { $0 as NSError }, interval)) -#else - print(convertResponseToString(response, error.map { $0 as NSError }, interval)) -#endif +#if DEBUG + if URLSession.logRequests { + HTTPRequestLogger.shared.log(response: (response, data, error), bodyPlaceholder: "[File Body]") } +#endif guard let response = response, let data = data else { single(.failure(error ?? RxCocoaURLError.unknown)) return diff --git a/Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift b/Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift index 85390a2..44497b9 100644 --- a/Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift +++ b/Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift @@ -23,23 +23,12 @@ extension Reactive where Base: URLSession { var taskProgressObservation: NSKeyValueObservation? let taskProgressSubject = PublishSubject() let taskResponseSingle = Single<(response: HTTPURLResponse, data: Data)>.create { single in - // smart compiler should be able to optimize this out - let d: Date? - if URLSession.rx.shouldLogRequest(request) { - d = Date() - } else { - d = nil - } let task = self.base.fileUploadTask(with: request, from: file) { data, response, error in - if URLSession.rx.shouldLogRequest(request) { - let interval = Date().timeIntervalSince(d ?? Date()) - print(convertURLRequestToCurlCommand(request)) -#if os(Linux) - print(convertResponseToString(response, error.flatMap { $0 as NSError }, interval)) -#else - print(convertResponseToString(response, error.map { $0 as NSError }, interval)) -#endif +#if DEBUG + if URLSession.logRequests { + HTTPRequestLogger.shared.log(response: (response, data, error)) } +#endif guard let response = response, let data = data else { single(.failure(error ?? RxCocoaURLError.unknown)) return @@ -75,23 +64,12 @@ extension Reactive where Base: URLSession { var taskProgressObservation: NSKeyValueObservation? let taskProgressSubject = PublishSubject() let taskResponseSingle = Single<(response: HTTPURLResponse, data: Data)>.create { single in - // smart compiler should be able to optimize this out - let d: Date? - if URLSession.rx.shouldLogRequest(request) { - d = Date() - } else { - d = nil - } let task = self.base.formDataUploadTask(with: request, from: formData) { data, response, error in - if URLSession.rx.shouldLogRequest(request) { - let interval = Date().timeIntervalSince(d ?? Date()) - print(convertURLRequestToCurlCommand(request)) -#if os(Linux) - print(convertResponseToString(response, error.flatMap { $0 as NSError }, interval)) -#else - print(convertResponseToString(response, error.map { $0 as NSError }, interval)) -#endif +#if DEBUG + if URLSession.logRequests { + HTTPRequestLogger.shared.log(response: (response, data, error)) } +#endif guard let response = response, let data = data else { single(.failure(error ?? RxCocoaURLError.unknown)) return diff --git a/Source/HTTP/Extensions/URLRequest+CURLCommand.swift b/Source/HTTP/Extensions/URLRequest+CURLCommand.swift new file mode 100644 index 0000000..8b8a0e8 --- /dev/null +++ b/Source/HTTP/Extensions/URLRequest+CURLCommand.swift @@ -0,0 +1,40 @@ +// +// URLRequest+CURLCommand.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 25/01/2024. +// + +import Foundation + +extension URLRequest { + + /// cURL command representation of this `URLRequest`. + var curlCommand: String { + guard let url = url else { return "" } + var baseCommand = #"curl "\#(url.absoluteString)""# + + if httpMethod == "HEAD" { + baseCommand += " --head" + } + + var command = [baseCommand] + + if let method = httpMethod, method != "GET" && method != "HEAD" { + command.append("-X \(method)") + } + + if let headers = allHTTPHeaderFields { + for (key, value) in headers where key != "Cookie" { + command.append("-H '\(key): \(value)'") + } + } + + if let data = httpBody, let body = String(data: data, encoding: .utf8) { + command.append("-d '\(body)'") + } + + return command.joined(separator: " \\\n\t") + } + +} diff --git a/Source/HTTP/Extensions/URLSession+DownloadTask.swift b/Source/HTTP/Extensions/URLSession+DownloadTask.swift index 571864a..ccb1bc3 100644 --- a/Source/HTTP/Extensions/URLSession+DownloadTask.swift +++ b/Source/HTTP/Extensions/URLSession+DownloadTask.swift @@ -16,6 +16,12 @@ extension URLSession { /// /// - Returns: download`URLSessionDataTask` created using given request. func fileDownloadTask(with request: URLRequest, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { +#if DEBUG + if URLSession.logRequests { + let outgoingRequest = outgoingRequest(for: request) + HTTPRequestLogger.shared.log(request: outgoingRequest) + } +#endif let task = dataTask(with: request, completionHandler: completionHandler) return task } @@ -28,6 +34,12 @@ extension URLSession { /// /// - Returns: download`URLSessionDataTask` created using given request and local url. func fileDownloadTask(with request: URLRequest, saveTo url: URL, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { +#if DEBUG + if URLSession.logRequests { + let outgoingRequest = outgoingRequest(for: request) + HTTPRequestLogger.shared.log(request: outgoingRequest) + } +#endif let task = dataTask(with: request) { data, response, error in do { if let data = data { diff --git a/Source/HTTP/Extensions/URLSession+LogRequests.swift b/Source/HTTP/Extensions/URLSession+LogRequests.swift new file mode 100644 index 0000000..dc5f4b0 --- /dev/null +++ b/Source/HTTP/Extensions/URLSession+LogRequests.swift @@ -0,0 +1,15 @@ +// +// URLSession+LogRequests.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 25/01/2024. +// + +import Foundation + +extension URLSession { + + /// `Bool` flag that indicates wether requests should be logged to the console + static var logRequests: Bool = false + +} diff --git a/Source/HTTP/Extensions/URLSession+OutgoingRequest.swift b/Source/HTTP/Extensions/URLSession+OutgoingRequest.swift new file mode 100644 index 0000000..37e54cf --- /dev/null +++ b/Source/HTTP/Extensions/URLSession+OutgoingRequest.swift @@ -0,0 +1,25 @@ +// +// URLRequest+OutgoingRequest.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 25/01/2024. +// + +import Foundation + +extension URLSession { + + /// Computes actual outgoing request for a given initial request. + /// + /// - Parameter initialRequest: Initial `URLRequest` object. + /// + /// - Returns: Actual outgoing `URLRequest` after applying additional HTTP headers. + func outgoingRequest(for initialRequest: URLRequest) -> URLRequest { + var finalRequest = initialRequest + let initialRequestHTTPHeaders = initialRequest.allHTTPHeaderFields ?? [:] + let configurationAdditionalHTTPHeaders = (configuration.httpAdditionalHeaders) as? [String: String] ?? [:] + finalRequest.allHTTPHeaderFields = initialRequestHTTPHeaders.merging(configurationAdditionalHTTPHeaders, uniquingKeysWith: { _, new in new }) + return finalRequest + } + +} diff --git a/Source/HTTP/Extensions/URLSession+UploadTask.swift b/Source/HTTP/Extensions/URLSession+UploadTask.swift index 7562371..fa2821e 100644 --- a/Source/HTTP/Extensions/URLSession+UploadTask.swift +++ b/Source/HTTP/Extensions/URLSession+UploadTask.swift @@ -19,6 +19,13 @@ extension URLSession { func fileUploadTask(with request: URLRequest, from file: HTTPUploadRequestFile, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { let fileData = extractFileData(file) let request = adaptUploadRequest(originalRequest: request, withContentType: file.mimeType.rawValue, withBody: fileData) +#if DEBUG + if URLSession.logRequests { + let outgoingRequest = outgoingRequest(for: request) + HTTPRequestLogger.shared.log(request: outgoingRequest, bodyPlaceholder: "[File Body]") + + } +#endif let task = dataTask(with: request, completionHandler: completionHandler) return task } @@ -34,6 +41,12 @@ extension URLSession { let boundary = generateFormBoundary() let dataBody = createFormDataBody(formData: formData, boundary: boundary) let request = adaptUploadRequest(originalRequest: request, withContentType: "multipart/form-data; boundary=\(boundary)", withBody: dataBody) +#if DEBUG + if URLSession.logRequests { + let outgoingRequest = outgoingRequest(for: request) + HTTPRequestLogger.shared.log(request: outgoingRequest) + } +#endif let task = dataTask(with: request, completionHandler: completionHandler) return task } diff --git a/Source/HTTP/Types/Request/Logger/HTTPRequestLogger.swift b/Source/HTTP/Types/Request/Logger/HTTPRequestLogger.swift new file mode 100644 index 0000000..4c63dab --- /dev/null +++ b/Source/HTTP/Types/Request/Logger/HTTPRequestLogger.swift @@ -0,0 +1,140 @@ +// +// HTTPRequestLogger.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 25/01/2024. +// + +import Foundation + +/// Object responsible for logging outgoing requests and incoming responses. +class HTTPRequestLogger { + + /// Shared `HTTPRequestLogger` instance. + static let shared: HTTPRequestLogger = .init() + + /// Private initializer to ensure only one instance is created. + private init() { } + + /// Prints outgoing request to console. + /// + /// - Parameters: + /// - request: `URLRequest` to be printed to console. + /// - bodyPlaceholder: `String?` placeholder to be printed in place of actual body. + func log(request: URLRequest, bodyPlaceholder: String? = nil) { + let logMessage = makeLogMessage(for: request, bodyPlaceholder: bodyPlaceholder) + print(logMessage) + } + + /// Prints incoming response to console. + /// + /// - Parameters: + /// - response: `(URLResponse?, Data?, Error?)` to be printed to console. + /// - bodyPlaceholder: `String?` placeholder to be printed in place of actual body. + func log(response: (URLResponse?, Data?, Error?), bodyPlaceholder: String? = nil) { + let logMessage = makeLogMessage(for: response, bodyPlaceholder: bodyPlaceholder) + print(logMessage) + } + + /// Make console message for outgoing request. + /// + /// - Parameters: + /// - request: `URLRequest` to be included in message. + /// - bodyPlaceholder: `String?` placeholder to be included in message in place of actual body. + /// + /// - Returns: `String` of outgoing request message. + private func makeLogMessage(for request: URLRequest, bodyPlaceholder: String?) -> String { + var logMessage: String = "" + + logMessage += "* * * * * * * * * * OUTGOING REQUEST * * * * * * * * * *\n" + + let urlAsString = request.url?.absoluteString ?? "" + let urlComponents = URLComponents(string: urlAsString) + let method = request.httpMethod != nil ? "\(request.httpMethod ?? "")" : "" + let path = "\(urlComponents?.path ?? "")" + let query = "\(urlComponents?.query ?? "")" + let host = "\(urlComponents?.host ?? "")" + var requestDetails = """ + \n\(urlAsString) \n + \(method) \(path)?\(query) HTTP/1.1 \n + HOST: \(host)\n + """ + for (key,value) in request.allHTTPHeaderFields ?? [:] { + requestDetails += "\(key): \(value) \n" + } + if let bodyPlaceholder = bodyPlaceholder { + requestDetails += "\n\(bodyPlaceholder)\n" + } else if let body = request.httpBody { + if let jsonString = body.json { + requestDetails += "\n\(jsonString)\n" + } else { + requestDetails += "\n\(String(decoding: body, as: UTF8.self))\n" + } + } + + var curlCommand = """ + \n- - - - - - - - - - - CURL COMMAND - - - - - - - - - - -\n + """ + curlCommand += "\n\(request.curlCommand)\n" + + logMessage += requestDetails + logMessage += curlCommand + logMessage += "\n* * * * * * * * * * * * * END * * * * * * * * * * * * *\n" + + return logMessage + } + + /// Make console message for incoming response. + /// + /// - Parameters: + /// - response: `(URLResponse?, Data?, Error?)` to be included in message. + /// - bodyPlaceholder: `String?` placeholder to be included in message in place of actual body. + /// + /// - Returns: `String` of incoming response message. + private func makeLogMessage(for response: (URLResponse?, Data?, Error?), bodyPlaceholder: String?) -> String { + var logMessage: String = "" + + logMessage += "* * * * * * * * * * INCOMING RESPONSE * * * * * * * * * *\n" + + let httpResponse = response.0 as? HTTPURLResponse + let responseBody = response.1 + let responseError = response.2 + + let urlString = httpResponse?.url?.absoluteString + let components = URLComponents(string: urlString ?? "") + let path = "\(components?.path ?? "")" + let query = "\(components?.query ?? "")" + var responseDetails = "" + if let urlString = urlString { + responseDetails += "\n\(urlString)" + responseDetails += "\n\n" + } + if let statusCode = httpResponse?.statusCode { + responseDetails += "HTTP \(statusCode) \(path)?\(query)\n" + } + if let host = components?.host { + responseDetails += "Host: \(host)\n" + } + for (key, value) in httpResponse?.allHeaderFields ?? [:] { + responseDetails += "\(key): \(value)\n" + } + if let bodyPlaceholder = bodyPlaceholder { + responseDetails += "\n\(bodyPlaceholder)\n" + } else if let body = responseBody { + if let jsonString = body.json { + responseDetails += "\n\(jsonString)\n" + } else { + responseDetails += "\n\(String(decoding: body, as: UTF8.self))\n" + } + } + if let responseError = responseError { + responseDetails += "\nError: \(responseError.localizedDescription)\n" + } + + logMessage += responseDetails + logMessage += "\n* * * * * * * * * * * * * END * * * * * * * * * * * * *\n" + + return logMessage + } + +} diff --git a/Source/HTTP/Types/Request/Router/HTTPDownloadRequestRouter.swift b/Source/HTTP/Types/Request/Router/HTTPDownloadRequestRouter.swift index 9bfd6e8..87716f2 100644 --- a/Source/HTTP/Types/Request/Router/HTTPDownloadRequestRouter.swift +++ b/Source/HTTP/Types/Request/Router/HTTPDownloadRequestRouter.swift @@ -7,7 +7,6 @@ import Foundation - /// Holds download request details. public protocol HTTPDownloadRequestRouter: HTTPRequestRouter { } diff --git a/Source/REST/Extensions/Reactive+RESTResponse.swift b/Source/REST/Extensions/Reactive+RESTResponse.swift new file mode 100644 index 0000000..b4800fb --- /dev/null +++ b/Source/REST/Extensions/Reactive+RESTResponse.swift @@ -0,0 +1,62 @@ +// +// Reactive+RESTResponse.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 25/01/2024. +// + +import Foundation +import RxSwift +import RxCocoa + +extension Reactive where Base: URLSession { + /** + Observable sequence of responses for URL request. + + Performing of request starts after observer is subscribed and not after invoking this method. + + **URL requests will be performed per subscribed observer.** + + Any error during fetching of the response will cause observed sequence to terminate with error. + + - parameter request: URL request. + - returns: Observable sequence of URL responses. + */ + func restResponse(request: URLRequest) -> Observable<(response: HTTPURLResponse, data: Data)> { + return Observable.create { observer in + +#if DEBUG + if URLSession.logRequests { + let outgoingRequest = base.outgoingRequest(for: request) + HTTPRequestLogger.shared.log(request: outgoingRequest) + } +#endif + + let task = self.base.dataTask(with: request) { data, response, error in +#if DEBUG + if URLSession.logRequests { + HTTPRequestLogger.shared.log(response: (response, data, error)) + } +#endif + + guard let response = response, let data = data else { + observer.on(.error(error ?? RxCocoaURLError.unknown)) + return + } + + guard let httpResponse = response as? HTTPURLResponse else { + observer.on(.error(RxCocoaURLError.nonHTTPResponse(response: response))) + return + } + + observer.on(.next((httpResponse, data))) + observer.on(.completed) + } + + task.resume() + + return Disposables.create(with: task.cancel) + } + } + +} diff --git a/Source/REST/Types/Client/RESTClient.swift b/Source/REST/Types/Client/RESTClient.swift index 6ecbdeb..7997ede 100644 --- a/Source/REST/Types/Client/RESTClient.swift +++ b/Source/REST/Types/Client/RESTClient.swift @@ -51,7 +51,7 @@ public class RESTClient { } let observable = urlSession .rx - .response(request: adaptedRequest) + .restResponse(request: adaptedRequest) .decodable(E.self, apiErrorType: AE.self) .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable @@ -76,7 +76,7 @@ public class RESTClient { } let observable = urlSession .rx - .response(request: adaptedRequest) + .restResponse(request: adaptedRequest) .decodable(T.self, httpErrorType: E.self, apiErrorType: AE.self) .retry(retryMaxAttempts, delay: retryPolicy, shouldRetry: shouldRetry) return observable diff --git a/Source/HTTP/Extensions/ProcessInfo+operatingSystemName.swift b/Source/Session/Extensions/ProcessInfo+operatingSystemName.swift similarity index 100% rename from Source/HTTP/Extensions/ProcessInfo+operatingSystemName.swift rename to Source/Session/Extensions/ProcessInfo+operatingSystemName.swift diff --git a/Source/HTTP/Extensions/URLSessionConfiguration+setAdditionalHTTPHeader.swift b/Source/Session/Extensions/URLSessionConfiguration+setAdditionalHTTPHeader.swift similarity index 100% rename from Source/HTTP/Extensions/URLSessionConfiguration+setAdditionalHTTPHeader.swift rename to Source/Session/Extensions/URLSessionConfiguration+setAdditionalHTTPHeader.swift diff --git a/Source/HTTP/Extensions/URLSessionConfiguration+setUserAgentHTTPHeader.swift b/Source/Session/Extensions/URLSessionConfiguration+setUserAgentHTTPHeader.swift similarity index 90% rename from Source/HTTP/Extensions/URLSessionConfiguration+setUserAgentHTTPHeader.swift rename to Source/Session/Extensions/URLSessionConfiguration+setUserAgentHTTPHeader.swift index 05b5908..03c5ba8 100644 --- a/Source/HTTP/Extensions/URLSessionConfiguration+setUserAgentHTTPHeader.swift +++ b/Source/Session/Extensions/URLSessionConfiguration+setUserAgentHTTPHeader.swift @@ -16,6 +16,6 @@ extension URLSessionConfiguration { let frameworkBundleVersion = frameworkBundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String let osName = ProcessInfo.processInfo.operatingSystemName let osVersion = ProcessInfo.processInfo.operatingSystemVersionString - setAdditionalHTTPHeader("User-Agent", value: "RxNetworkKit/\(frameworkBundleVersion) (\(osName) \(osVersion)-\(mainBundleIndentifier))") + setAdditionalHTTPHeader("User-Agent", value: "RxNetworkKit/\(frameworkBundleVersion) (\(osName) \(osVersion)) (\(mainBundleIndentifier))") } } diff --git a/Source/Session/Session.swift b/Source/Session/Session.swift deleted file mode 100644 index 4156b5a..0000000 --- a/Source/Session/Session.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// Session.swift -// RxNetworkKit -// -// Created by Loay Ashraf on 01/11/2023. -// - -import Foundation - -/// Wrapper object for `URLSession` and `HTTPRequestEventMonitor` that can be shared between multiple clients. -public class Session { - - /// Principal `URLSession`object used to create request tasks. - let urlSession: URLSession - /// Principal `HTTPRequestEventMonitor` object used to monitor request tasks. - let eventMonitor: HTTPRequestEventMonitor - /// `OperationQueue` object used by event monitor. - private let eventMonitorQueue: OperationQueue - - /// Creates a `Session` instance. - /// - /// - Parameters: - /// - configuration: `URLSessionConfiguration` object used to create `URLSession` instance. - /// - eventMonitor: `HTTPRequestEventMonitor` object for monitoring events for session. - public init(configuration: URLSessionConfiguration, eventMonitor: HTTPRequestEventMonitor) { - // Apply User-Agent header as a part of HTTP aditional headers. - configuration.setUserAgentHTTPHeader() - // Initialize session's properties. - self.eventMonitor = eventMonitor - self.eventMonitorQueue = .init() - eventMonitorQueue.name = "RxNetworkKit/Session(\(UUID().uuidString))/Event Monitor Queue" - eventMonitorQueue.qualityOfService = .utility - eventMonitorQueue.maxConcurrentOperationCount = 20 - self.urlSession = .init(configuration: configuration, - delegate: eventMonitor, - delegateQueue: eventMonitorQueue) - URLSession.rx.shouldLogRequest = { _ in false } - } - -} diff --git a/Source/Session/Types/Session.swift b/Source/Session/Types/Session.swift new file mode 100644 index 0000000..8396574 --- /dev/null +++ b/Source/Session/Types/Session.swift @@ -0,0 +1,31 @@ +// +// Session.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 01/11/2023. +// + +import Foundation + +/// Wrapper object for `URLSession` that can be shared between multiple clients. +public class Session { + + /// Principal `URLSession`object used to create request tasks. + let urlSession: URLSession + + /// Creates a `Session` instance. + /// + /// - Parameters: + /// - configuration: `SessionConfiguration` object used to configure and create `URLSession` instance. + public init(configuration: SessionConfiguration) { + let urlSessionConfiguration = configuration.urlSessionConfiguration + if configuration.setUserAgentHeader { + // Apply User-Agent header as a part of HTTP aditional headers. + urlSessionConfiguration.setUserAgentHTTPHeader() + } + URLSession.logRequests = configuration.logRequests + // Initialize `URLSession`. + urlSession = .init(configuration: urlSessionConfiguration) + } + +} diff --git a/Source/Session/Types/SessionConfiguration.swift b/Source/Session/Types/SessionConfiguration.swift new file mode 100644 index 0000000..c926083 --- /dev/null +++ b/Source/Session/Types/SessionConfiguration.swift @@ -0,0 +1,37 @@ +// +// SessionConfiguration.swift +// RxNetworkKit +// +// Created by Loay Ashraf on 25/01/2024. +// + +import Foundation + +/// Wrapper object for `URLSessionConfiguration` and additional configuration parameters. +public class SessionConfiguration { + + /// Default `SessionConfiguration` object. + public static var `default`: SessionConfiguration { + .init(urlSessionConfiguration: .default, setUserAgentHeader: true, logRequests: true) + } + + /// `URLSessionConfiguration`object used to create `URLSession` object. + let urlSessionConfiguration: URLSessionConfiguration + /// `Bool` flag that indicates wether a `URLSession` should add `User-Agent` header to outgoing requests. + let setUserAgentHeader: Bool + /// `Bool` flag that indicates wether a `URLSession` should print outgoing requests to the console. + let logRequests: Bool + + /// Description + /// + /// - Parameters: + /// - urlSessionConfiguration: `URLSessionConfiguration`object used to create `URLSession` object. + /// - setUserAgentHeader: `Bool` flag that indicates wether a `URLSession` should add `User-Agent` header to outgoing requests. + /// - logRequests: `Bool` flag that indicates wether a `URLSession` should print outgoing requests to the console. + public init(urlSessionConfiguration: URLSessionConfiguration, setUserAgentHeader: Bool, logRequests: Bool) { + self.urlSessionConfiguration = urlSessionConfiguration + self.setUserAgentHeader = setUserAgentHeader + self.logRequests = logRequests + } + +} From 1c6c99acc4d3b74aeb8c23fba802011935e74bb9 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 25 Jan 2024 15:28:40 +0200 Subject: [PATCH 93/95] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 64d72bc..87e490a 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ It makes use of RxSwift's traits at request level to acheive a high level of spe ``` // Create `Session` instance. -let session = Session(configuration: .default, eventMonitor: self) +let session = Session(configuration: .default) // Create 'RESTClient' instance. let restClient = RESTClient(session: session, requestInterceptor: self) // Create `HTTPRequestRouter` instance. @@ -57,7 +57,7 @@ single ``` // Create `Session` instance. -let session = Session(configuration: .default, eventMonitor: self) +let session = Session(configuration: .default) // Create 'HTTPClient' instance. let httpClient = HTTPClient(session: session, requestInterceptor: self) // Create `HTTPDownloadRequestRouter` instance. @@ -88,7 +88,7 @@ downloadObservable ``` // Create `Session` instance. -let session = Session(configuration: .default, eventMonitor: self) +let session = Session(configuration: .default) // Create 'HTTPClient' instance. let httpClient = HTTPClient(session: session, requestInterceptor: self) // Create `HTTPUploadRequestRouter` instance. @@ -133,7 +133,7 @@ Once you have your Swift package set up, adding RxNetworkKit as a dependency is ```swift dependencies: [ - .package(url: "https://github.com/loay-ashraf/RxNetworkKit.git", .upToNextMajor(from: "1.0.0")) + .package(url: "https://github.com/loay-ashraf/RxNetworkKit.git", .upToNextMajor(from: "2.0.0")) ] ``` From caa553ce75a1c8e14f920e8ffc6671c40cd2c725 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 25 Jan 2024 15:46:45 +0200 Subject: [PATCH 94/95] feat: update documentation Resolves: none. --- Docs.docc/Pages/RxNetworkKit.md | 5 +---- Source/Session/Types/SessionConfiguration.swift | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Docs.docc/Pages/RxNetworkKit.md b/Docs.docc/Pages/RxNetworkKit.md index 4751a72..db3f29e 100644 --- a/Docs.docc/Pages/RxNetworkKit.md +++ b/Docs.docc/Pages/RxNetworkKit.md @@ -19,6 +19,7 @@ It makes use of RxSwift's traits at request level to acheive a high level of spe ### Foundation - ``Session`` +- ``SessionConfiguration`` - ``RESTClient`` - ``HTTPClient`` @@ -45,10 +46,6 @@ It makes use of RxSwift's traits at request level to acheive a high level of spe - ``HTTPRequestRetrier`` - ``HTTPRequestRetryPolicy`` -### Event Monitor - -- ``HTTPRequestEventMonitor`` - ### Network Reachability - ``NetworkReachability`` diff --git a/Source/Session/Types/SessionConfiguration.swift b/Source/Session/Types/SessionConfiguration.swift index c926083..22b2629 100644 --- a/Source/Session/Types/SessionConfiguration.swift +++ b/Source/Session/Types/SessionConfiguration.swift @@ -15,17 +15,17 @@ public class SessionConfiguration { .init(urlSessionConfiguration: .default, setUserAgentHeader: true, logRequests: true) } - /// `URLSessionConfiguration`object used to create `URLSession` object. + /// `URLSessionConfiguration` object used to create `URLSession` object. let urlSessionConfiguration: URLSessionConfiguration /// `Bool` flag that indicates wether a `URLSession` should add `User-Agent` header to outgoing requests. let setUserAgentHeader: Bool /// `Bool` flag that indicates wether a `URLSession` should print outgoing requests to the console. let logRequests: Bool - /// Description + /// Creates a `SessionConfiguration` instance. /// /// - Parameters: - /// - urlSessionConfiguration: `URLSessionConfiguration`object used to create `URLSession` object. + /// - urlSessionConfiguration: `URLSessionConfiguration` object used to create `URLSession` object. /// - setUserAgentHeader: `Bool` flag that indicates wether a `URLSession` should add `User-Agent` header to outgoing requests. /// - logRequests: `Bool` flag that indicates wether a `URLSession` should print outgoing requests to the console. public init(urlSessionConfiguration: URLSessionConfiguration, setUserAgentHeader: Bool, logRequests: Bool) { From 3968c38328b1c79f27665d7e62e27ddd4dbe42bf Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 25 Jan 2024 15:49:29 +0200 Subject: [PATCH 95/95] feat: bump versions to 2.0.0 Resolves: none. --- Examples/iOS/iOS Example.xcodeproj/project.pbxproj | 4 ++-- Examples/macOS/macOS Example.xcodeproj/project.pbxproj | 4 ++-- Examples/tvOS/tvOS Example.xcodeproj/project.pbxproj | 4 ++-- Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj | 4 ++-- RxNetworkKit.xcodeproj/project.pbxproj | 4 ++-- Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj index 5df55ce..87897ab 100644 --- a/Examples/iOS/iOS Example.xcodeproj/project.pbxproj +++ b/Examples/iOS/iOS Example.xcodeproj/project.pbxproj @@ -330,7 +330,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitExample; PRODUCT_NAME = "iOS Example"; @@ -367,7 +367,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitExample; PRODUCT_NAME = "iOS Example"; diff --git a/Examples/macOS/macOS Example.xcodeproj/project.pbxproj b/Examples/macOS/macOS Example.xcodeproj/project.pbxproj index 562449c..078be98 100644 --- a/Examples/macOS/macOS Example.xcodeproj/project.pbxproj +++ b/Examples/macOS/macOS Example.xcodeproj/project.pbxproj @@ -361,7 +361,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitMacOSExample; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -394,7 +394,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; OTHER_CODE_SIGN_FLAGS = "--deep"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKitMacOSExample; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Examples/tvOS/tvOS Example.xcodeproj/project.pbxproj b/Examples/tvOS/tvOS Example.xcodeproj/project.pbxproj index a38162c..8abf7ea 100644 --- a/Examples/tvOS/tvOS Example.xcodeproj/project.pbxproj +++ b/Examples/tvOS/tvOS Example.xcodeproj/project.pbxproj @@ -372,7 +372,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; PRODUCT_BUNDLE_IDENTIFIER = com.las.tvOSExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -398,7 +398,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; PRODUCT_BUNDLE_IDENTIFIER = com.las.tvOSExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj b/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj index 5d80ece..63f4889 100644 --- a/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj +++ b/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj @@ -406,7 +406,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; PRODUCT_BUNDLE_IDENTIFIER = com.las.watchOSExample.watchkitapp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; @@ -436,7 +436,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; PRODUCT_BUNDLE_IDENTIFIER = com.las.watchOSExample.watchkitapp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = watchos; diff --git a/RxNetworkKit.xcodeproj/project.pbxproj b/RxNetworkKit.xcodeproj/project.pbxproj index 1ce37b7..ca07bfb 100644 --- a/RxNetworkKit.xcodeproj/project.pbxproj +++ b/RxNetworkKit.xcodeproj/project.pbxproj @@ -657,7 +657,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKit; @@ -698,7 +698,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.las.RxNetworkKit; diff --git a/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj b/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj index 9a77aec..f00fef8 100644 --- a/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj +++ b/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj @@ -366,7 +366,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.las.CoreExample; @@ -411,7 +411,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 2.0.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.las.CoreExample;