From 05d7d8dc13524e47bf43563302ca36812ecf1778 Mon Sep 17 00:00:00 2001 From: Loay Ashraf Date: Wed, 10 Jan 2024 04:39:23 +0200 Subject: [PATCH] v1.0.0 (#64) * 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. * Delete build-deploy-docs1.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * update CI workflows * Update build.yml * Update build-ios.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * Update build-deploy-docs.yml * 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 * Update build.yml * 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. * Update build.yml * Update build.yml * feat: bump Xcode version to 15.1.0 for workflow files Resolves: none. * 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. * fix: silence warnings Resolves: none. * feat: add watchOS platform to package manifest + sort files Resolves: none. * 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. * Update README.md * 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/build-deploy-docs.yml | 2 +- .github/workflows/build-ios.yml | 12 +- .github/workflows/build-macos.yml | 6 +- .github/workflows/build-tvos.yml | 86 +++ .github/workflows/build-watchos.yml | 85 +++ .github/workflows/build.yml | 9 + .github/workflows/spm-lint.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 | 28 +- .../iOS/iOS Example.xcodeproj/project.pbxproj | 175 ++---- .../ViewController+NetworkEventMonitor.swift | 26 - ...Controller+NetworkRequestInterceptor.swift | 24 - .../Controller/ViewController.swift | 17 +- Examples/iOS/iOS Example/Model/Model.swift | 21 - .../macOS Example.xcodeproj/project.pbxproj | 189 +++--- .../xcschemes/macOS Example.xcscheme | 77 +++ .../ViewController+NetworkEventMonitor.swift | 26 - ...Controller+NetworkRequestInterceptor.swift | 24 - .../Controller/ViewController.swift | 15 +- .../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 - .../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 | 544 ++++++++++++++++++ .../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 ++ Package.resolved | 17 +- Package.swift | 5 +- README.md | 20 +- RxNetworkKit.xcodeproj/project.pbxproj | 357 +++++------- .../contents.xcworkspacedata | 11 +- .../xcshareddata/swiftpm/Package.resolved | 9 + .../CoreExample.xcodeproj/project.pbxproj | 485 ++++++++++++++++ .../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 | 4 +- .../Reactive+URLSessionUploadResponse.swift | 10 +- .../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 | 40 ++ 144 files changed, 3823 insertions(+), 1704 deletions(-) create mode 100644 .github/workflows/SPMDummy/SPMDummy/MainApp.swift create mode 100644 .github/workflows/SPMDummy/SPMDummy/MainView.swift create mode 100644 .github/workflows/build-tvos.yml create mode 100644 .github/workflows/build-watchos.yml 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 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 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 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 (97%) rename Source/{Custom Requests/Upload => HTTP/Extensions}/Reactive+URLSessionUploadResponse.swift (89%) 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/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/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 635c4ad..5d98642 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: '15.1.0' - 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: '15.1.0' - 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: '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: '15.0.0' + 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 b9ccbb3..a35ab7d 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: '15.1.0' - 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: '15.1.0' - name: Build Example env: workspace: RxNetworkKit.xcworkspace 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-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 9d65597..d4ff127 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,6 +26,15 @@ 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 + build-deploy-docs: name: Build and Deploy Docs uses: ./.github/workflows/build-deploy-docs.yml + if: github.ref == 'refs/heads/main' diff --git a/.github/workflows/spm-lint.yml b/.github/workflows/spm-lint.yml index 0b3fb98..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: @@ -28,7 +30,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 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..8e269f6 100644 --- a/Docs.docc/Pages/RxNetworkKit.md +++ b/Docs.docc/Pages/RxNetworkKit.md @@ -18,34 +18,36 @@ It makes use of RxSwift's traits at request level to acheive a high level of spe ### Foundation -- ``NetworkManager`` +- ``Session`` +- ``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..5df55ce 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; @@ -412,7 +330,8 @@ "$(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"; 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; @@ -451,7 +367,8 @@ "$(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"; 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..5045a12 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 { @@ -133,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() { @@ -199,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/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..562449c 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; @@ -439,9 +361,11 @@ "@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)"; + 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; @@ -469,9 +394,11 @@ "@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)"; + 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..efb1e4f 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 { @@ -130,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/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/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 new file mode 100644 index 0000000..5d80ece --- /dev/null +++ b/Examples/watchOS/watchOS Example.xcodeproj/project.pbxproj @@ -0,0 +1,544 @@ +// !$*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 = ( + C66F79BC2B4DCAD000FDA1AC /* App */, + C66F79BD2B4DCEE100FDA1AC /* View */, + ); + path = "watchOS Example"; + sourceTree = ""; + }; + C66F79AD2B4DC8B600FDA1AC /* Frameworks */ = { + isa = PBXGroup; + children = ( + C66F79B22B4DC8BD00FDA1AC /* RxNetworkKit.framework */, + C66F79AE2B4DC8B600FDA1AC /* CoreExample.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + C66F79BC2B4DCAD000FDA1AC /* App */ = { + isa = PBXGroup; + children = ( + C60BAE632B4DC64A00C2338B /* Assets.xcassets */, + C60BAE622B4DC64A00C2338B /* ExampleApp.swift */, + C60BAE652B4DC64A00C2338B /* Preview Assets.xcassets */, + ); + path = App; + sourceTree = ""; + }; + C66F79BD2B4DCEE100FDA1AC /* View */ = { + isa = PBXGroup; + children = ( + C60BAE662B4DC64A00C2338B /* MainView.swift */, + C66F79C72B4DFA7000FDA1AC /* RemoteImage.swift */, + C66F79C52B4DFA3300FDA1AC /* RxBindingMediator.swift */, + C66F79C32B4DF9FA00FDA1AC /* RxImageDownloadBindingMediator.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; + 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; + }; + 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; + 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; + }; + 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/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..30ed564 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), .tvOS(.v14), .watchOS(.v7) ], products: [ .library(name: "RxNetworkKit", targets: ["RxNetworkKit"]), @@ -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/README.md b/README.md index 769155c..1e6acad 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # 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%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) -![Cocoapods](https://img.shields.io/badge/Cocoapods-compatible-red) +![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) [![LinkedIn](https://img.shields.io/badge/LinkedIn-loay--ashraf-blue)](https://linkedin.com/in/loay-ashraf) @@ -114,19 +115,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+ / tvOS 14.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 +131,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 936b8d2..b0aa15c 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"; @@ -680,18 +605,20 @@ "@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 = "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"; + TARGETED_DEVICE_FAMILY = "1,2,3,4"; + TVOS_DEPLOYMENT_TARGET = 14.0; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Debug; }; @@ -708,6 +635,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"; @@ -718,18 +646,20 @@ "@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 = "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"; + TARGETED_DEVICE_FAMILY = "1,2,3,4"; + TVOS_DEPLOYMENT_TARGET = 14.0; + WATCHOS_DEPLOYMENT_TARGET = 7.0; }; name = Release; }; @@ -773,6 +703,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 +734,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..c7114cb 100644 --- a/RxNetworkKit.xcworkspace/contents.xcworkspacedata +++ b/RxNetworkKit.xcworkspace/contents.xcworkspacedata @@ -2,7 +2,10 @@ + location = "group:RxNetworkKit.xcodeproj"> + + @@ -10,4 +13,10 @@ + + + + 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..c908e4d --- /dev/null +++ b/Shared/CoreExample/CoreExample.xcodeproj/project.pbxproj @@ -0,0 +1,485 @@ +// !$*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 = "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,3,4"; + TVOS_DEPLOYMENT_TARGET = 14.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 7.0; + }; + 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 = "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,3,4"; + TVOS_DEPLOYMENT_TARGET = 14.0; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + WATCHOS_DEPLOYMENT_TARGET = 7.0; + }; + 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 97% rename from Source/Custom Requests/Download/Reactive+URLSessionDownloadResponse.swift rename to Source/HTTP/Extensions/Reactive+URLSessionDownloadResponse.swift index c6fa24c..ce815fb 100644 --- a/Source/Custom Requests/Download/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) } @@ -65,7 +66,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)>) { @@ -106,6 +107,7 @@ extension Reactive where Base: URLSession { taskProgressSubject.onCompleted() } } + _ = taskProgressObservation task.resume() return Disposables.create(with: task.cancel) } diff --git a/Source/Custom Requests/Upload/Reactive+URLSessionUploadResponse.swift b/Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift similarity index 89% rename from Source/Custom Requests/Upload/Reactive+URLSessionUploadResponse.swift rename to Source/HTTP/Extensions/Reactive+URLSessionUploadResponse.swift index f8a5bb0..85390a2 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() @@ -56,6 +56,7 @@ extension Reactive where Base: URLSession { taskProgressSubject.onCompleted() } } + _ = taskProgressObservation task.resume() return Disposables.create(with: task.cancel) } @@ -66,10 +67,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() @@ -107,6 +108,7 @@ extension Reactive where Base: URLSession { taskProgressSubject.onCompleted() } } + _ = taskProgressObservation task.resume() return Disposables.create(with: task.cancel) } 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..4156b5a --- /dev/null +++ b/Source/Session/Session.swift @@ -0,0 +1,40 @@ +// +// 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 } + } + +}