From bfdb3198cbdf9b8bc91a09c71f670b9ba3e6be78 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Thu, 5 Oct 2023 21:03:27 +0300 Subject: [PATCH] fix corrupt CI workflow file (#53) * feat: add decoding capability in case of failure caused due to HTTP status code Resolves: none. * feat: override User-Agent HTTP header in session configuration (#26) Resolves: none. * 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. * 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. * fix: remove RxDataSources import statement (#31) Resolves: none. * 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 * Update CI Workflows * update CI workflows * Update CI Workflow * 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. * feat: add WebSocket capability to NetworkManager (#46) Resolves: none. * fix a typo * update dependencies versions Resolves: none. * update Package.swift + podspec file * change xcode version used in CI/CD to 14.3.1 Resolves: none. * Update pod-lib-lint.yml * Update pod-lib-lint.yml * Update pod-lib-lint.yml * Update build.yml * Update build.yml * update workflow files * 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 * Update pod-lib-lint.yml * Update pod-lib-lint.yml * Update pod-lib-lint.yml * Update pod-trunk-push.yml * Update spm-lint.yml * Update build-macos.yml * update Xcode version to 15.0.0 for CI workflows * Update pod-lib-lint.yml * Update pod-lib-lint.yml * Update pod-lib-lint.yml * break: drop support for Cocoapods (cannot work with Xcode 15) Resolves: none. * [49][DocC][Update Documentation] (#50) * feat: update overview for classes, structs, enums, typaliases and protocols Resolves: none. * feat: add docs catalog Resolves: none. * update CI workflows to build docs Resolves: none. * update CI workflows 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 * fix: update corrupt ci workflow file Resolves: none. --- ...uild-deploy-docs => build-deploy-docs.yml} | 0 .github/workflows/build-ios.yml | 26 +++++++++ .github/workflows/build-macos.yml | 2 +- .github/workflows/pod-lib-lint.yml | 38 ------------- .github/workflows/pod-trunk-push.yml | 38 ------------- Docs.docc/Pages/NetworkManager.md | 37 +++++++++++++ Docs.docc/Pages/RxNetworkKit.md | 54 +++++++++++++++++++ RxNetworkKit.xcodeproj/project.pbxproj | 4 ++ RxNetworkKitX.podspec | 20 ------- .../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 + 38 files changed, 268 insertions(+), 108 deletions(-) rename .github/workflows/{build-deploy-docs => build-deploy-docs.yml} (100%) delete mode 100644 .github/workflows/pod-lib-lint.yml delete mode 100644 .github/workflows/pod-trunk-push.yml create mode 100644 Docs.docc/Pages/NetworkManager.md create mode 100644 Docs.docc/Pages/RxNetworkKit.md delete mode 100644 RxNetworkKitX.podspec 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 diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml index c411fd3..af192f8 100644 --- a/.github/workflows/build-ios.yml +++ b/.github/workflows/build-ios.yml @@ -4,6 +4,32 @@ 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 runs-on: macos-13 diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 2e086b6..b9ccbb3 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -21,7 +21,7 @@ jobs: scheme: RxNetworkKit destination: generic/platform=macOS run: | - xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" | xcpretty && exit ${PIPESTATUS[0]} + xcodebuild clean build -workspace "${workspace}" -scheme "${scheme}" -destination "${destination}" docbuild | xcpretty && exit ${PIPESTATUS[0]} example-macos: name: Build Example For macOS diff --git a/.github/workflows/pod-lib-lint.yml b/.github/workflows/pod-lib-lint.yml deleted file mode 100644 index 1e4a715..0000000 --- a/.github/workflows/pod-lib-lint.yml +++ /dev/null @@ -1,38 +0,0 @@ -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 - 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/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/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 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