From 3d055b010d008d228c759df88a26106ddb64810b Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Sat, 11 May 2024 18:18:45 +0400 Subject: [PATCH 1/4] Update `IAuthenticator` (#2) * Update `IAuthenticator` * Update `CHANGELOG` * Update docs * Update `danger.yml` * Update Unit Tests --- .github/workflows/danger.yml | 4 ++-- CHANGELOG.md | 6 ++++++ .../Core/Authentification/AuthenticationInterceptor.swift | 2 +- .../NetworkLayer.docc/Articles/Authentication.md | 4 ++-- .../Classes/Core/Authenticator/IAuthenticator.swift | 2 +- .../Classes/Helpers/Mocks/AuthenticatorMock.swift | 2 +- .../Tests/UnitTests/RequestParametersEncoderTests.swift | 4 ++-- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index e6c6e9a..3ba7736 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -15,7 +15,7 @@ jobs: - name: ruby setup uses: ruby/setup-ruby@v1 with: - ruby-version: 2.7 + ruby-version: 3.1.4 bundler-cache: true - name: Checkout code uses: actions/checkout@v2 @@ -28,4 +28,4 @@ jobs: DANGER_GITHUB_API_TOKEN: ${{ secrets.DANGER_GITHUB_API_TOKEN }} - run: bundle exec danger --verbose \ No newline at end of file + run: bundle exec danger --verbose diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c4648..80138b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. +## [Unreleased] + +## Fixed +- Fix the `inout` parameter in the `IAuthenticator` protocol + - Fixed in Pull Request [#2](https://github.com/space-code/network-layer/pull/2). + #### 1.x Releases - `1.0.x` Releases - [1.0.0](#100) diff --git a/Sources/NetworkLayer/Classes/Core/Authentification/AuthenticationInterceptor.swift b/Sources/NetworkLayer/Classes/Core/Authentification/AuthenticationInterceptor.swift index b017286..8a09425 100644 --- a/Sources/NetworkLayer/Classes/Core/Authentification/AuthenticationInterceptor.swift +++ b/Sources/NetworkLayer/Classes/Core/Authentification/AuthenticationInterceptor.swift @@ -46,7 +46,7 @@ public final class AuthenticationInterceptor: IAu if credential.requiresRefresh { try await refresh(credential, for: session) } else { - try await authenticator.apply(credential, to: request) + try await authenticator.apply(credential, to: &request) } } diff --git a/Sources/NetworkLayer/NetworkLayer.docc/Articles/Authentication.md b/Sources/NetworkLayer/NetworkLayer.docc/Articles/Authentication.md index c44f16c..ce2dc46 100644 --- a/Sources/NetworkLayer/NetworkLayer.docc/Articles/Authentication.md +++ b/Sources/NetworkLayer/NetworkLayer.docc/Articles/Authentication.md @@ -34,8 +34,8 @@ struct Authenticator: IAuthenticator { /// - Parameters: /// - credential: The `Credential`. /// - urlRequest: The `URLRequest`. - func apply(_ credential: Credential, to urlRequest: URLRequest) async throws { - request.addValue("Bearer ", forHTTPHeaderField: "Authorization") + func apply(_ credential: Credential, to urlRequest: inout URLRequest) async throws { + urlRequest.addValue("Bearer ", forHTTPHeaderField: "Authorization") } /// Refreshes the `Credential`. diff --git a/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticator.swift b/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticator.swift index a67cd64..2f791c5 100644 --- a/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticator.swift +++ b/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticator.swift @@ -14,7 +14,7 @@ public protocol IAuthenticator { /// - Parameters: /// - credential: The `Credential`. /// - urlRequest: The `URLRequest`. - func apply(_ credential: Credential, to urlRequest: URLRequest) async throws + func apply(_ credential: Credential, to urlRequest: inout URLRequest) async throws /// Refreshes the `Credential`. /// diff --git a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthenticatorMock.swift b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthenticatorMock.swift index 4b1d42b..492469f 100644 --- a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthenticatorMock.swift +++ b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthenticatorMock.swift @@ -14,7 +14,7 @@ final class AuthenticatorMock: IAuthenticator { var invokedApplyParameters: (credential: Credential, urlRequest: URLRequest)? var invokedApplyParametersList = [(credential: Credential, urlRequest: URLRequest)]() - func apply(_ credential: Credential, to urlRequest: URLRequest) async throws { + func apply(_ credential: Credential, to urlRequest: inout URLRequest) async throws { invokedApply = true invokedApplyCount += 1 invokedApplyParameters = (credential, urlRequest) diff --git a/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestParametersEncoderTests.swift b/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestParametersEncoderTests.swift index 9cb4a31..32995e8 100644 --- a/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestParametersEncoderTests.swift +++ b/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestParametersEncoderTests.swift @@ -41,7 +41,8 @@ final class RequestParametersEncoderTests: XCTestCase { func test_thatRequestParametersEncoderThrowsAnError_whenURLIsNotValid() { // given - var requestMock = URLRequest.fake(string: .wrongURL) + var requestMock = URLRequest.fake(string: .domainName) + requestMock.url = nil // when var receivedError: NSError? @@ -60,7 +61,6 @@ final class RequestParametersEncoderTests: XCTestCase { private extension String { static let domainName = "https://google.com" - static let wrongURL = "http://example.com:-80" } private extension Dictionary where Self.Key == String, Self.Value == String { From 37c6dd1faa73314a0ed0e65682d85f696d503a21 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Tue, 24 Dec 2024 16:16:23 +0100 Subject: [PATCH 2/4] Bump the Swift version to 6.0 (#3) * Update `ci.yml` * Fix typos * Add `Package@swift-5.8.swift` * Fix typos * Update `GitHub Actions` * Bump `Typhoon` version * Bump the Swift version to 6.0 * Update `CHANGELOG.md` --- .github/workflows/ci.yml | 237 +++++++++++++++--- .swiftlint.yml | 5 +- CHANGELOG.md | 6 +- Package.resolved | 8 +- Package.swift | 8 +- Package@swift-5.10.swift | 51 ++++ Package@swift-5.7.swift | 6 +- Package@swift-5.8.swift | 50 ++++ Package@swift-5.9.swift | 51 ++++ README.md | 4 +- .../AuthenticationInterceptor.swift | 4 +- .../RequestBodyEncoder.swift | 2 +- .../RequestBuilder/RequestBuilder.swift | 2 +- .../DataRequestHandler.swift | 39 +-- .../Models/SafeRequestProcessorDelegate.swift | 19 ++ .../RequestProcessor/RequestProcessor.swift | 25 +- .../Classes/DI/NetworkLayerAssembly.swift | 4 +- .../IAuthenticationCredential.swift | 2 +- .../IAuthenticationInterceptor.swift | 2 +- .../Core/Authenticator/IAuthenticator.swift | 4 +- .../Classes/Core/Models/Configuration.swift | 2 +- .../Classes/Core/Models/IRequest.swift | 8 +- .../Classes/Core/Models/RequestBody.swift | 2 +- .../Classes/Core/Models/Response.swift | 6 + .../Core/Services/IRequestBuilder.swift | 2 +- .../Core/Services/IRequestProcessor.swift | 2 +- .../Helpers/RequestProcessor+Mock.swift | 2 +- .../Helpers/Mocks/AuthenticatorMock.swift | 4 +- .../AuthentificatorInterceptorMock.swift | 2 +- .../Mocks/DataRequestHandlerMock.swift | 2 +- .../Helpers/Mocks/RequestBuilderMock.swift | 2 +- .../Classes/Helpers/Mocks/RequestMock.swift | 2 +- .../Mocks/URLSessionDelegateMock.swift | 2 +- .../Stubs/AuthenticationCredentialStub.swift | 2 +- .../Classes/Helpers/Stubs/RequestStub.swift | 4 +- .../Classes/Helpers/Stubs/StubResponse.swift | 2 +- .../RequestProcessorAuthenticationTests.swift | 4 +- .../RequestProcessorRequestTests.swift | 28 ++- .../UnitTests/RequestBodyEncoderTests.swift | 8 +- .../Tests/UnitTests/RequestBuilderTests.swift | 4 +- .../RequestParametersEncoderTests.swift | 2 +- .../UnitTests/RequestProcessorTests.swift | 2 +- 42 files changed, 505 insertions(+), 118 deletions(-) create mode 100644 Package@swift-5.10.swift create mode 100644 Package@swift-5.8.swift create mode 100644 Package@swift-5.9.swift create mode 100644 Sources/NetworkLayer/Classes/Core/Services/RequestProcessor/Models/SafeRequestProcessorDelegate.swift diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 619319c..d56943c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,51 +12,216 @@ on: - "Package.swift" - "Source/**" - "Tests/**" + jobs: - SwiftLint: - runs-on: ubuntu-latest + macOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - xcode: "Xcode_16.0" + runsOn: macOS-14 + name: "macOS 14, Xcode 16.0, Swift 6.0" + - xcode: "Xcode_15.4" + runsOn: macOS-14 + name: "macOS 14, Xcode 15.4, Swift 5.10" + - xcode: "Xcode_15.0" + runsOn: macos-13 + name: "macOS 13, Xcode 15.0, Swift 5.9.0" + - xcode: "Xcode_14.3.1" + runsOn: macos-13 + name: "macOS 13, Xcode 14.3.1, Swift 5.8.0" + steps: + - uses: actions/checkout@v4 + - name: ${{ matrix.name }} + run: xcodebuild test -scheme "NetworkLayer" -destination "platform=macOS" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 + - name: Upload test coverage reports to Codecov + uses: space-code/oss-common-actions/.github/actions/upload_test_coverage_report@main + with: + scheme_name: NetworkLayer + filename: ${{ matrix.name }} + token: ${{ secrets.CODECOV_TOKEN }} + + iOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - destination: "OS=18.1,name=iPhone 16 Pro" + name: "iOS 18.1" + xcode: "Xcode_16.1" + runsOn: macOS-14 + - destination: "OS=18.0,name=iPhone 16 Pro" + name: "iOS 18.0" + xcode: "Xcode_16.0" + runsOn: macOS-14 + - destination: "OS=17.5,name=iPhone 15 Pro" + name: "iOS 17.5" + xcode: "Xcode_15.4" + runsOn: macOS-14 + - destination: "OS=17.0.1,name=iPhone 14 Pro" + name: "iOS 17.0.1" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=16.4,name=iPhone 14 Pro" + name: "iOS 16.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - uses: actions/checkout@v4 + - name: ${{ matrix.name }} + run: xcodebuild test -scheme "NetworkLayer" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 + - name: Upload test coverage reports to Codecov + uses: space-code/oss-common-actions/.github/actions/upload_test_coverage_report@main + with: + scheme_name: NetworkLayer + filename: ${{ matrix.name }} + token: ${{ secrets.CODECOV_TOKEN }} + + tvOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - destination: "OS=18.1,name=Apple TV" + name: "tvOS 18.1" + xcode: "Xcode_16.1" + runsOn: macOS-14 + - destination: "OS=18.0,name=Apple TV" + name: "tvOS 18.0" + xcode: "Xcode_16.0" + runsOn: macOS-14 + - destination: "OS=17.5,name=Apple TV" + name: "tvOS 17.5" + xcode: "Xcode_15.4" + runsOn: macOS-14 + - destination: "OS=17.0,name=Apple TV" + name: "tvOS 17.0" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=16.4,name=Apple TV" + name: "tvOS 16.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 steps: - - uses: actions/checkout@v3 - - name: GitHub Action for SwiftLint - uses: norio-nomura/action-swiftlint@3.2.1 + - uses: actions/checkout@v4 + - name: ${{ matrix.name }} + run: xcodebuild test -scheme "NetworkLayer" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 + - name: Upload test coverage report to Codecov + uses: space-code/oss-common-actions/.github/actions/upload_test_coverage_report@main with: - args: --strict - env: - DIFF_BASE: ${{ github.base_ref }} - Latest: - name: Test Latest (iOS, macOS, tvOS, watchOS) - runs-on: macOS-12 + scheme_name: NetworkLayer + filename: ${{ matrix.name }} + token: ${{ secrets.CODECOV_TOKEN }} + + watchOS: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} env: - DEVELOPER_DIR: "/Applications/Xcode_14.1.app/Contents/Developer" - timeout-minutes: 10 + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 strategy: fail-fast: false matrix: include: - - destination: "OS=16.1,name=iPhone 14 Pro" - name: "iOS" - scheme: "NetworkLayer" - sdk: iphonesimulator - - destination: "OS=16.1,name=Apple TV" - name: "tvOS" - scheme: "NetworkLayer" - sdk: appletvsimulator - - destination: "OS=9.1,name=Apple Watch Series 8 (45mm)" - name: "watchOS" - scheme: "NetworkLayer" - sdk: watchsimulator - - destination: "platform=macOS" - name: "macOS" - scheme: "NetworkLayer" - sdk: macosx + - destination: "OS=11.1,name=Apple Watch Series 10 (46mm)" + name: "watchOS 11.1" + xcode: "Xcode_16.1" + runsOn: macOS-14 + - destination: "OS=11.0,name=Apple Watch Series 10 (46mm)" + name: "watchOS 11.0" + xcode: "Xcode_16.0" + runsOn: macOS-14 + - destination: "OS=10.5,name=Apple Watch Series 9 (45mm)" + name: "watchOS 10.5" + xcode: "Xcode_15.4" + runsOn: macOS-14 + - destination: "OS=10.0,name=Apple Watch Series 9 (45mm)" + name: "watchOS 10.0" + xcode: "Xcode_15.0" + runsOn: macos-13 + - destination: "OS=9.4,name=Apple Watch Series 8 (45mm)" + name: "watchOS 9.4" + xcode: "Xcode_14.3.1" + runsOn: macos-13 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: ${{ matrix.name }} - run: xcodebuild test -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "./${{ matrix.sdk }}.xcresult" - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3.1.0 + run: xcodebuild test -scheme "NetworkLayer" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES -resultBundlePath "test_output/${{ matrix.name }}.xcresult" || exit 1 + - name: Upload test coverage reports to Codecov + uses: space-code/oss-common-actions/.github/actions/upload_test_coverage_report@main with: + scheme_name: NetworkLayer + filename: ${{ matrix.name }} token: ${{ secrets.CODECOV_TOKEN }} - xcode: true - xcode_archive_path: "./${{ matrix.sdk }}.xcresult" - \ No newline at end of file + + spm: + name: ${{ matrix.name }} + runs-on: ${{ matrix.runsOn }} + env: + DEVELOPER_DIR: "/Applications/${{ matrix.xcode }}.app/Contents/Developer" + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - name: "macOS 14, SPM 6.0.2 Test" + xcode: "Xcode_16.1" + runsOn: macOS-14 + - name: "macOS 14, SPM 6.0.0 Test" + xcode: "Xcode_16.0" + runsOn: macOS-14 + - name: "macOS 14, SPM 5.9.0 Test" + xcode: "Xcode_15.0" + runsOn: macos-14 + - name: "macOS 13, SPM 5.8.1 Test" + xcode: "Xcode_14.3.1" + runsOn: macos-13 + steps: + - uses: actions/checkout@v4 + - name: ${{ matrix.name }} + run: swift build -c release --target NetworkLayer + + merge-test-reports: + needs: [iOS, macOS, watchOS, tvOS] + runs-on: macos-13 + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: test_output + - run: xcrun xcresulttool merge test_output/**/*.xcresult --output-path test_output/final/final.xcresult + - name: Upload Merged Artifact + uses: actions/upload-artifact@v4 + with: + name: MergedResult + path: test_output/final + + discover-typos: + name: Discover Typos + runs-on: macOS-13 + env: + DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer + steps: + - uses: actions/checkout@v4 + - name: Discover typos + run: | + export PATH="$PATH:/Library/Frameworks/Python.framework/Versions/3.11/bin" + python3 -m pip install --upgrade pip + python3 -m pip install codespell + codespell --ignore-words-list="hart,inout,msdos,sur" --skip="./.build/*,./.git/*" diff --git a/.swiftlint.yml b/.swiftlint.yml index 297f875..ccb6b1f 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -2,6 +2,9 @@ excluded: - Tests - Package.swift - Package@swift-5.7.swift + - Package@swift-5.8.swift + - Package@swift-5.9.swift + - Package@swift-5.10.swift - .build # Rules @@ -130,4 +133,4 @@ nesting: type_name: max_length: warning: 40 - error: 50 \ No newline at end of file + error: 50 diff --git a/CHANGELOG.md b/CHANGELOG.md index 80138b0..967c6a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,11 @@ All notable changes to this project will be documented in this file. ## [Unreleased] -## Fixed +#### Added +- Bump the Swift version to 6.0. + - Added in Pull Request [#3](https://github.com/space-code/network-layer/pull/3). + +#### Fixed - Fix the `inout` parameter in the `IAuthenticator` protocol - Fixed in Pull Request [#2](https://github.com/space-code/network-layer/pull/2). diff --git a/Package.resolved b/Package.resolved index 418af9a..9c93daf 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/space-code/atomic", "state" : { - "revision" : "53fae2fc8216bb5c27c87b245f893176d0d290eb", - "version" : "1.0.0" + "revision" : "6304c805e9d97fe1239bcde561234d857164cb10", + "version" : "1.1.0" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/space-code/typhoon", "state" : { - "revision" : "c0e2970812f4ec837fc61afd72a7ca1f0aa39306", - "version" : "1.0.0" + "revision" : "5865671564c5b443a9a28d2f1dbb69bccbb5931c", + "version" : "1.2.1" } } ], diff --git a/Package.swift b/Package.swift index c9f72ca..382a9fb 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.9 +// swift-tools-version: 6.0 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -17,9 +17,9 @@ let package = Package( .library(name: "NetworkLayerInterfaces", targets: ["NetworkLayerInterfaces"]), ], dependencies: [ - .package(url: "https://github.com/space-code/atomic", .upToNextMajor(from: "1.0.0")), - .package(url: "https://github.com/space-code/typhoon", .upToNextMajor(from: "1.0.0")), - .package(url: "https://github.com/WeTransfer/Mocker", .upToNextMajor(from: "3.0.1")), + .package(url: "https://github.com/space-code/atomic", exact: "1.1.0"), + .package(url: "https://github.com/space-code/typhoon", exact: "1.2.1"), + .package(url: "https://github.com/WeTransfer/Mocker", exact: "3.0.1"), ], targets: [ .target( diff --git a/Package@swift-5.10.swift b/Package@swift-5.10.swift new file mode 100644 index 0000000..5c251ce --- /dev/null +++ b/Package@swift-5.10.swift @@ -0,0 +1,51 @@ +// swift-tools-version: 5.10 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "NetworkLayer", + platforms: [ + .macOS(.v10_15), + .iOS(.v13), + .watchOS(.v7), + .tvOS(.v13), + .visionOS(.v1), + ], + products: [ + .library(name: "NetworkLayer", targets: ["NetworkLayer"]), + .library(name: "NetworkLayerInterfaces", targets: ["NetworkLayerInterfaces"]), + ], + dependencies: [ + .package(url: "https://github.com/space-code/atomic", exact: "1.1.0"), + .package(url: "https://github.com/space-code/typhoon", exact: "1.2.1"), + .package(url: "https://github.com/WeTransfer/Mocker", exact: "3.0.1"), + ], + targets: [ + .target( + name: "NetworkLayer", + dependencies: [ + "NetworkLayerInterfaces", + .product(name: "Atomic", package: "atomic"), + .product(name: "Typhoon", package: "typhoon"), + ] + ), + .target( + name: "NetworkLayerInterfaces", + dependencies: [ + .product(name: "Typhoon", package: "typhoon"), + ] + ), + .testTarget( + name: "NetworkLayerTests", + dependencies: [ + "NetworkLayer", + .product(name: "Mocker", package: "Mocker"), + .product(name: "Typhoon", package: "typhoon"), + ], + resources: [ + .process("Resources"), + ] + ), + ] +) diff --git a/Package@swift-5.7.swift b/Package@swift-5.7.swift index 0a132cd..f462754 100644 --- a/Package@swift-5.7.swift +++ b/Package@swift-5.7.swift @@ -16,9 +16,9 @@ let package = Package( .library(name: "NetworkLayerInterfaces", targets: ["NetworkLayerInterfaces"]), ], dependencies: [ - .package(url: "https://github.com/space-code/atomic", .upToNextMajor(from: "1.0.0")), - .package(url: "https://github.com/space-code/typhoon", .upToNextMajor(from: "1.0.0")), - .package(url: "https://github.com/WeTransfer/Mocker", .upToNextMajor(from: "3.0.1")), + .package(url: "https://github.com/space-code/atomic", exact: "1.1.0"), + .package(url: "https://github.com/space-code/typhoon", exact: "1.2.1"), + .package(url: "https://github.com/WeTransfer/Mocker", exact: "3.0.1"), ], targets: [ .target( diff --git a/Package@swift-5.8.swift b/Package@swift-5.8.swift new file mode 100644 index 0000000..a0ad70b --- /dev/null +++ b/Package@swift-5.8.swift @@ -0,0 +1,50 @@ +// swift-tools-version: 5.8 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "NetworkLayer", + platforms: [ + .macOS(.v10_15), + .iOS(.v13), + .watchOS(.v7), + .tvOS(.v13), + ], + products: [ + .library(name: "NetworkLayer", targets: ["NetworkLayer"]), + .library(name: "NetworkLayerInterfaces", targets: ["NetworkLayerInterfaces"]), + ], + dependencies: [ + .package(url: "https://github.com/space-code/atomic", exact: "1.1.0"), + .package(url: "https://github.com/space-code/typhoon", exact: "1.2.1"), + .package(url: "https://github.com/WeTransfer/Mocker", exact: "3.0.1"), + ], + targets: [ + .target( + name: "NetworkLayer", + dependencies: [ + "NetworkLayerInterfaces", + .product(name: "Atomic", package: "atomic"), + .product(name: "Typhoon", package: "typhoon"), + ] + ), + .target( + name: "NetworkLayerInterfaces", + dependencies: [ + .product(name: "Typhoon", package: "typhoon"), + ] + ), + .testTarget( + name: "NetworkLayerTests", + dependencies: [ + "NetworkLayer", + .product(name: "Mocker", package: "Mocker"), + .product(name: "Typhoon", package: "typhoon"), + ], + resources: [ + .process("Resources"), + ] + ), + ] +) diff --git a/Package@swift-5.9.swift b/Package@swift-5.9.swift new file mode 100644 index 0000000..43d2dc8 --- /dev/null +++ b/Package@swift-5.9.swift @@ -0,0 +1,51 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "NetworkLayer", + platforms: [ + .macOS(.v10_15), + .iOS(.v13), + .watchOS(.v7), + .tvOS(.v13), + .visionOS(.v1), + ], + products: [ + .library(name: "NetworkLayer", targets: ["NetworkLayer"]), + .library(name: "NetworkLayerInterfaces", targets: ["NetworkLayerInterfaces"]), + ], + dependencies: [ + .package(url: "https://github.com/space-code/atomic", exact: "1.1.0"), + .package(url: "https://github.com/space-code/typhoon", exact: "1.2.1"), + .package(url: "https://github.com/WeTransfer/Mocker", exact: "3.0.1"), + ], + targets: [ + .target( + name: "NetworkLayer", + dependencies: [ + "NetworkLayerInterfaces", + .product(name: "Atomic", package: "atomic"), + .product(name: "Typhoon", package: "typhoon"), + ] + ), + .target( + name: "NetworkLayerInterfaces", + dependencies: [ + .product(name: "Typhoon", package: "typhoon"), + ] + ), + .testTarget( + name: "NetworkLayerTests", + dependencies: [ + "NetworkLayer", + .product(name: "Mocker", package: "Mocker"), + .product(name: "Typhoon", package: "typhoon"), + ], + resources: [ + .process("Resources"), + ] + ), + ] +) diff --git a/README.md b/README.md index 6c45a18..ca6b8fa 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@

License -Swift Compability -Platform Compability +Swift Compatibility +Platform Compatibility CI diff --git a/Sources/NetworkLayer/Classes/Core/Authentification/AuthenticationInterceptor.swift b/Sources/NetworkLayer/Classes/Core/Authentification/AuthenticationInterceptor.swift index 8a09425..e06a944 100644 --- a/Sources/NetworkLayer/Classes/Core/Authentification/AuthenticationInterceptor.swift +++ b/Sources/NetworkLayer/Classes/Core/Authentification/AuthenticationInterceptor.swift @@ -1,6 +1,6 @@ // // network-layer -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Atomic @@ -9,7 +9,7 @@ import NetworkLayerInterfaces /// A custom AuthenticationInterceptor implementation that works with a specific type /// of Authenticator conforming to the IAuthenticator protocol. -public final class AuthenticationInterceptor: IAuthenticationInterceptor { +public final class AuthenticationInterceptor: IAuthenticationInterceptor, @unchecked Sendable { // MARK: Types public typealias Credential = Authenticator.Credential diff --git a/Sources/NetworkLayer/Classes/Core/Builders/RequestBuilder/RequestBodyEncoder/RequestBodyEncoder.swift b/Sources/NetworkLayer/Classes/Core/Builders/RequestBuilder/RequestBodyEncoder/RequestBodyEncoder.swift index db481d4..59d70ca 100644 --- a/Sources/NetworkLayer/Classes/Core/Builders/RequestBuilder/RequestBodyEncoder/RequestBodyEncoder.swift +++ b/Sources/NetworkLayer/Classes/Core/Builders/RequestBuilder/RequestBodyEncoder/RequestBodyEncoder.swift @@ -25,7 +25,7 @@ struct RequestBodyEncoder: IRequestBodyEncoder { request.httpBody = data case let .encodable(encodable): request.httpBody = try jsonEncoder.encode(encodable) - case let .dictonary(dictionary): + case let .dictionary(dictionary): request.httpBody = try JSONSerialization.data(withJSONObject: dictionary) } } diff --git a/Sources/NetworkLayer/Classes/Core/Builders/RequestBuilder/RequestBuilder.swift b/Sources/NetworkLayer/Classes/Core/Builders/RequestBuilder/RequestBuilder.swift index 48f581f..09fe07e 100644 --- a/Sources/NetworkLayer/Classes/Core/Builders/RequestBuilder/RequestBuilder.swift +++ b/Sources/NetworkLayer/Classes/Core/Builders/RequestBuilder/RequestBuilder.swift @@ -6,7 +6,7 @@ import Foundation import NetworkLayerInterfaces -final class RequestBuilder: IRequestBuilder { +final class RequestBuilder: IRequestBuilder, @unchecked Sendable { // MARK: Properties private let parametersEncoder: IRequestParametersEncoder diff --git a/Sources/NetworkLayer/Classes/Core/Services/DataRequestHandler/DataRequestHandler.swift b/Sources/NetworkLayer/Classes/Core/Services/DataRequestHandler/DataRequestHandler.swift index 64f3400..b6fe0b9 100644 --- a/Sources/NetworkLayer/Classes/Core/Services/DataRequestHandler/DataRequestHandler.swift +++ b/Sources/NetworkLayer/Classes/Core/Services/DataRequestHandler/DataRequestHandler.swift @@ -1,6 +1,6 @@ // // network-layer -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Atomic @@ -10,13 +10,13 @@ import NetworkLayerInterfaces // MARK: - DataRequestHandler /// Manages data request handlers for URLSessionTasks. -final class DataRequestHandler: NSObject { +final class DataRequestHandler: NSObject, @unchecked Sendable { // MARK: Properties - private typealias HandlerDictonary = [URLSessionTask: DataTaskHandler] + private typealias HandlerDictionary = [URLSessionTask: DataTaskHandler] - /// The dictonary that stores handlers. - @Atomic private var handlers: HandlerDictonary = [:] + /// The dictionary that stores handlers. + @Atomic private var handlers: HandlerDictionary = [:] /// A protocol that defines methods that URL session instances call on their /// delegates to handle task-level events specific to data and upload tasks. private var userDataDelegate: URLSessionDataDelegate? @@ -40,7 +40,7 @@ extension DataRequestHandler: IDataRequestHandler { try await withTaskCancellationHandler(operation: { try await withUnsafeThrowingContinuation { continuation in let dataTaskHandler = DataTaskHandler(delegate: delegate) - dataTaskHandler.completion = continuation.resume(with:) + dataTaskHandler.completion = { continuation.resume(with: $0) } handlers[task] = dataTaskHandler task.resume() } @@ -71,15 +71,14 @@ extension DataRequestHandler { _ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, - completionHandler: @escaping (CachedURLResponse?) -> Void + completionHandler: @escaping @Sendable (CachedURLResponse?) -> Void ) { userDataDelegate?.urlSession?( session, dataTask: dataTask, willCacheResponse: proposedResponse, completionHandler: completionHandler - ) - completionHandler(proposedResponse) + ) ?? completionHandler(proposedResponse) } } @@ -114,7 +113,7 @@ extension DataRequestHandler { task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, - completionHandler: @escaping (URLRequest?) -> Void + completionHandler: @escaping @Sendable (URLRequest?) -> Void ) { userDataDelegate?.urlSession?( session, @@ -122,8 +121,7 @@ extension DataRequestHandler { willPerformHTTPRedirection: response, newRequest: request, completionHandler: completionHandler - ) - completionHandler(request) + ) ?? completionHandler(request) } func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) { @@ -133,9 +131,13 @@ extension DataRequestHandler { func urlSession( _ session: URLSession, didReceive challenge: URLAuthenticationChallenge, - completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void + completionHandler: @escaping @Sendable (URLSession.AuthChallengeDisposition, URLCredential?) -> Void ) { - userDataDelegate?.urlSession?(session, didReceive: challenge, completionHandler: completionHandler) + userDataDelegate?.urlSession?( + session, + didReceive: challenge, + completionHandler: completionHandler + ) completionHandler(.performDefaultHandling, nil) } @@ -143,9 +145,14 @@ extension DataRequestHandler { _ session: URLSession, task: URLSessionTask, willBeginDelayedRequest request: URLRequest, - completionHandler: @escaping (URLSession.DelayedRequestDisposition, URLRequest?) -> Void + completionHandler: @escaping @Sendable (URLSession.DelayedRequestDisposition, URLRequest?) -> Void ) { - userDataDelegate?.urlSession?(session, task: task, willBeginDelayedRequest: request, completionHandler: completionHandler) + userDataDelegate?.urlSession?( + session, + task: task, + willBeginDelayedRequest: request, + completionHandler: completionHandler + ) completionHandler(.continueLoading, nil) } diff --git a/Sources/NetworkLayer/Classes/Core/Services/RequestProcessor/Models/SafeRequestProcessorDelegate.swift b/Sources/NetworkLayer/Classes/Core/Services/RequestProcessor/Models/SafeRequestProcessorDelegate.swift new file mode 100644 index 0000000..bd0a1f2 --- /dev/null +++ b/Sources/NetworkLayer/Classes/Core/Services/RequestProcessor/Models/SafeRequestProcessorDelegate.swift @@ -0,0 +1,19 @@ +// +// network-layer +// Copyright © 2024 Space Code. All rights reserved. +// + +import NetworkLayerInterfaces + +final class SafeRequestProcessorDelegate: @unchecked Sendable { + private weak var delegate: RequestProcessorDelegate? + + init(delegate: RequestProcessorDelegate? = nil) { + self.delegate = delegate + } + + var wrappedValue: RequestProcessorDelegate? { + get { delegate } + set { delegate = newValue } + } +} diff --git a/Sources/NetworkLayer/Classes/Core/Services/RequestProcessor/RequestProcessor.swift b/Sources/NetworkLayer/Classes/Core/Services/RequestProcessor/RequestProcessor.swift index 8754162..1375262 100644 --- a/Sources/NetworkLayer/Classes/Core/Services/RequestProcessor/RequestProcessor.swift +++ b/Sources/NetworkLayer/Classes/Core/Services/RequestProcessor/RequestProcessor.swift @@ -26,7 +26,7 @@ actor RequestProcessor { /// The authenticator interceptor. private let interceptor: IAuthenticationInterceptor? /// The delegate. - private weak var delegate: RequestProcessorDelegate? + private var delegate: SafeRequestProcessorDelegate? // MARK: Initialization @@ -42,7 +42,7 @@ actor RequestProcessor { requestBuilder: IRequestBuilder, dataRequestHandler: any IDataRequestHandler, retryPolicyService: IRetryPolicyService, - delegate: RequestProcessorDelegate?, + delegate: SafeRequestProcessorDelegate?, interceptor: IAuthenticationInterceptor? ) { self.configuration = configuration @@ -83,8 +83,8 @@ actor RequestProcessor { try await adapt(request, urlRequest: &urlRequest, session: session) - return try await performRequest(strategy: strategy) { - try await self.delegate?.requestProcessor(self, willSendRequest: urlRequest) + return try await performRequest(strategy: strategy) { [urlRequest] in + try await self.delegate?.wrappedValue?.requestProcessor(self, willSendRequest: urlRequest) let task = session.dataTask(with: urlRequest) @@ -103,7 +103,7 @@ actor RequestProcessor { } } - try self.validate(response) + try await self.validate(response) return response } catch { @@ -153,9 +153,9 @@ actor RequestProcessor { /// - send: The closure that sends the request. /// /// - Returns: The response from the network request. - private func performRequest( + private func performRequest( strategy: RetryPolicyStrategy? = nil, - _ send: () async throws -> T + _ send: @Sendable () async throws -> T ) async throws -> T { do { return try await send() @@ -166,18 +166,23 @@ actor RequestProcessor { private func validate(_ response: Response) throws { guard let urlResponse = response.response as? HTTPURLResponse else { return } - try delegate?.requestProcessor(self, validateResponse: urlResponse, data: response.data, task: response.task) + try delegate?.wrappedValue?.requestProcessor( + self, + validateResponse: urlResponse, + data: response.data, + task: response.task + ) } } // MARK: IRequestProcessor extension RequestProcessor: IRequestProcessor { - func send( + func send( _ request: T, strategy: RetryPolicyStrategy? = nil, delegate: URLSessionDelegate? = nil, - configure: ((inout URLRequest) throws -> Void)? = nil + configure: (@Sendable (inout URLRequest) throws -> Void)? = nil ) async throws -> Response { let response = try await performRequest(request, strategy: strategy, delegate: delegate, configure: configure) return try response.map { data in try self.configuration.jsonDecoder.decode(M.self, from: data) } diff --git a/Sources/NetworkLayer/Classes/DI/NetworkLayerAssembly.swift b/Sources/NetworkLayer/Classes/DI/NetworkLayerAssembly.swift index 18e0bc2..00b5d39 100644 --- a/Sources/NetworkLayer/Classes/DI/NetworkLayerAssembly.swift +++ b/Sources/NetworkLayer/Classes/DI/NetworkLayerAssembly.swift @@ -15,7 +15,7 @@ public final class NetworkLayerAssembly: INetworkLayerAssembly { /// The retry policy service. private let retryPolicyStrategy: RetryPolicyStrategy? /// The request processor delegate. - private let delegate: RequestProcessorDelegate? + private var delegate: SafeRequestProcessorDelegate? /// The authenticator interceptor. private let interceptor: IAuthenticationInterceptor? /// The json encoder. @@ -37,7 +37,7 @@ public final class NetworkLayerAssembly: INetworkLayerAssembly { ) { self.configure = configure self.retryPolicyStrategy = retryPolicyStrategy - self.delegate = delegate + self.delegate = SafeRequestProcessorDelegate(delegate: delegate) self.interceptor = interceptor self.jsonEncoder = jsonEncoder } diff --git a/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticationCredential.swift b/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticationCredential.swift index 192e55c..6911bbc 100644 --- a/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticationCredential.swift +++ b/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticationCredential.swift @@ -6,7 +6,7 @@ import Foundation /// A type defines an authentication credential interface. -public protocol IAuthenticationCredential { +public protocol IAuthenticationCredential: Sendable { /// Determines whether the authentication credential requires a refresh. var requiresRefresh: Bool { get } } diff --git a/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticationInterceptor.swift b/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticationInterceptor.swift index 1ac679b..159d7be 100644 --- a/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticationInterceptor.swift +++ b/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticationInterceptor.swift @@ -6,7 +6,7 @@ import Foundation /// A type defines the authenticator interceptor interface. -public protocol IAuthenticationInterceptor { +public protocol IAuthenticationInterceptor: Sendable { /// Adapts the request with credentials. /// /// - Parameters: diff --git a/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticator.swift b/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticator.swift index 2f791c5..33f6f0b 100644 --- a/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticator.swift +++ b/Sources/NetworkLayerInterfaces/Classes/Core/Authenticator/IAuthenticator.swift @@ -1,12 +1,12 @@ // // network-layer -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation /// A protocol defining the interface for an authenticator type. -public protocol IAuthenticator { +public protocol IAuthenticator: Sendable { associatedtype Credential: IAuthenticationCredential /// Applies the `Credential` to the `URLRequest`. diff --git a/Sources/NetworkLayerInterfaces/Classes/Core/Models/Configuration.swift b/Sources/NetworkLayerInterfaces/Classes/Core/Models/Configuration.swift index 1d929ac..f8a27e4 100644 --- a/Sources/NetworkLayerInterfaces/Classes/Core/Models/Configuration.swift +++ b/Sources/NetworkLayerInterfaces/Classes/Core/Models/Configuration.swift @@ -6,7 +6,7 @@ import Foundation /// A type that represents a configuration for the network layer. -public struct Configuration { +public struct Configuration: @unchecked Sendable { // MARK: Properties /// A configuration object that defines behavior and policies for a URL session. diff --git a/Sources/NetworkLayerInterfaces/Classes/Core/Models/IRequest.swift b/Sources/NetworkLayerInterfaces/Classes/Core/Models/IRequest.swift index 91caaae..a109d00 100644 --- a/Sources/NetworkLayerInterfaces/Classes/Core/Models/IRequest.swift +++ b/Sources/NetworkLayerInterfaces/Classes/Core/Models/IRequest.swift @@ -1,6 +1,6 @@ // // network-layer -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation @@ -8,7 +8,7 @@ import Foundation // MARK: - IRequest /// A type to which all requests must conform. -public protocol IRequest { +public protocol IRequest: Sendable { /// The base `URL` for the resource. var domainName: String { get } @@ -30,7 +30,7 @@ public protocol IRequest { /// The HTTP method. var httpMethod: HTTPMethod { get } - /// A dictonary that contains the request's body. + /// A dictionary that contains the request's body. var httpBody: RequestBody? { get } /// An alias for the cache policy. @@ -58,7 +58,7 @@ public extension IRequest { 60 } - /// A dictonary that contains the request's body. + /// A dictionary that contains the request's body. var httpBody: RequestBody? { nil } diff --git a/Sources/NetworkLayerInterfaces/Classes/Core/Models/RequestBody.swift b/Sources/NetworkLayerInterfaces/Classes/Core/Models/RequestBody.swift index f884d5c..3fbd4b9 100644 --- a/Sources/NetworkLayerInterfaces/Classes/Core/Models/RequestBody.swift +++ b/Sources/NetworkLayerInterfaces/Classes/Core/Models/RequestBody.swift @@ -8,5 +8,5 @@ import Foundation public enum RequestBody { case data(Data) case encodable(Encodable) - case dictonary([String: Any]) + case dictionary([String: Any]) } diff --git a/Sources/NetworkLayerInterfaces/Classes/Core/Models/Response.swift b/Sources/NetworkLayerInterfaces/Classes/Core/Models/Response.swift index 1eaeba7..d7b49aa 100644 --- a/Sources/NetworkLayerInterfaces/Classes/Core/Models/Response.swift +++ b/Sources/NetworkLayerInterfaces/Classes/Core/Models/Response.swift @@ -5,6 +5,8 @@ import Foundation +// MARK: - Response + /// A generic struct representing an HTTP response. public struct Response { /// The data associated with the response. @@ -32,3 +34,7 @@ public struct Response { self.task = task } } + +// MARK: Sendable + +extension Response: @unchecked Sendable where T: Sendable {} diff --git a/Sources/NetworkLayerInterfaces/Classes/Core/Services/IRequestBuilder.swift b/Sources/NetworkLayerInterfaces/Classes/Core/Services/IRequestBuilder.swift index f47e8bf..dc3fb74 100644 --- a/Sources/NetworkLayerInterfaces/Classes/Core/Services/IRequestBuilder.swift +++ b/Sources/NetworkLayerInterfaces/Classes/Core/Services/IRequestBuilder.swift @@ -6,7 +6,7 @@ import Foundation /// A type that creates a `URLRequest`. -public protocol IRequestBuilder { +public protocol IRequestBuilder: Sendable { /// Creates a new `URLRequest` using `IRequest.` /// /// - Parameter request: The request object that defines the request details. diff --git a/Sources/NetworkLayerInterfaces/Classes/Core/Services/IRequestProcessor.swift b/Sources/NetworkLayerInterfaces/Classes/Core/Services/IRequestProcessor.swift index c3a6561..1800e40 100644 --- a/Sources/NetworkLayerInterfaces/Classes/Core/Services/IRequestProcessor.swift +++ b/Sources/NetworkLayerInterfaces/Classes/Core/Services/IRequestProcessor.swift @@ -22,7 +22,7 @@ public protocol IRequestProcessor { _ request: T, strategy: RetryPolicyStrategy?, delegate: URLSessionDelegate?, - configure: ((inout URLRequest) throws -> Void)? + configure: (@Sendable (inout URLRequest) throws -> Void)? ) async throws -> Response } diff --git a/Tests/NetworkLayerTests/Classes/Helpers/Helpers/RequestProcessor+Mock.swift b/Tests/NetworkLayerTests/Classes/Helpers/Helpers/RequestProcessor+Mock.swift index 5385dae..39a82d0 100644 --- a/Tests/NetworkLayerTests/Classes/Helpers/Helpers/RequestProcessor+Mock.swift +++ b/Tests/NetworkLayerTests/Classes/Helpers/Helpers/RequestProcessor+Mock.swift @@ -27,7 +27,7 @@ extension RequestProcessor { ), dataRequestHandler: DataRequestHandler(), retryPolicyService: RetryPolicyService(strategy: .constant(retry: 1, duration: .seconds(0))), - delegate: requestProcessorDelegate, + delegate: SafeRequestProcessorDelegate(delegate: requestProcessorDelegate), interceptor: interceptor ) } diff --git a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthenticatorMock.swift b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthenticatorMock.swift index 492469f..d01a119 100644 --- a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthenticatorMock.swift +++ b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthenticatorMock.swift @@ -1,12 +1,12 @@ // // network-layer -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation import NetworkLayerInterfaces -final class AuthenticatorMock: IAuthenticator { +final class AuthenticatorMock: IAuthenticator, @unchecked Sendable { typealias Credential = AuthenticationCredentialStub var invokedApply = false diff --git a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthentificatorInterceptorMock.swift b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthentificatorInterceptorMock.swift index 4313e21..7c7e051 100644 --- a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthentificatorInterceptorMock.swift +++ b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/AuthentificatorInterceptorMock.swift @@ -6,7 +6,7 @@ import Foundation import NetworkLayerInterfaces -final class AuthentificatorInterceptorMock: IAuthenticationInterceptor { +final class AuthentificatorInterceptorMock: IAuthenticationInterceptor, @unchecked Sendable { var invokedAdapt = false var invokedAdaptCount = 0 var invokedAdaptParameters: (request: URLRequest, session: URLSession)? diff --git a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/DataRequestHandlerMock.swift b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/DataRequestHandlerMock.swift index 8889a5c..3b662b1 100644 --- a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/DataRequestHandlerMock.swift +++ b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/DataRequestHandlerMock.swift @@ -6,7 +6,7 @@ import Foundation import NetworkLayerInterfaces -final class DataRequestHandlerMock: NSObject, IDataRequestHandler { +final class DataRequestHandlerMock: NSObject, IDataRequestHandler, @unchecked Sendable { var invokedUrlSessionGetDelegate = false var invokedUrlSessionGetDelegateCount = 0 var invokedUrlSessionSetDelegate = false diff --git a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/RequestBuilderMock.swift b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/RequestBuilderMock.swift index bb845c2..ecfa24a 100644 --- a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/RequestBuilderMock.swift +++ b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/RequestBuilderMock.swift @@ -6,7 +6,7 @@ import Foundation import NetworkLayerInterfaces -final class RequestBuilderMock: IRequestBuilder { +final class RequestBuilderMock: IRequestBuilder, @unchecked Sendable { var invokedBuild = false var invokedBuildCount = 0 var invokedBuildParameters: (request: IRequest, Void)? diff --git a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/RequestMock.swift b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/RequestMock.swift index 36cc932..e37ede1 100644 --- a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/RequestMock.swift +++ b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/RequestMock.swift @@ -6,7 +6,7 @@ import Foundation import NetworkLayerInterfaces -final class RequestMock: IRequest { +final class RequestMock: IRequest, @unchecked Sendable { var invokedDomainNameGetter = false var invokedDomainNameGetterCount = 0 var stubbedDomainName: String! = "" diff --git a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/URLSessionDelegateMock.swift b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/URLSessionDelegateMock.swift index 98e2694..ae06122 100644 --- a/Tests/NetworkLayerTests/Classes/Helpers/Mocks/URLSessionDelegateMock.swift +++ b/Tests/NetworkLayerTests/Classes/Helpers/Mocks/URLSessionDelegateMock.swift @@ -5,7 +5,7 @@ import Foundation -final class URLSessionDelegateMock: NSObject, URLSessionDataDelegate { +final class URLSessionDelegateMock: NSObject, URLSessionDataDelegate, @unchecked Sendable { var invokedUrlSessionDidBecomeInvalidWithError = false var invokedUrlSessionDidBecomeInvalidWithErrorCount = 0 var invokedUrlSessionDidBecomeInvalidWithErrorParameters: (session: URLSession, error: Error?)? diff --git a/Tests/NetworkLayerTests/Classes/Helpers/Stubs/AuthenticationCredentialStub.swift b/Tests/NetworkLayerTests/Classes/Helpers/Stubs/AuthenticationCredentialStub.swift index 9e9b4f3..8e09656 100644 --- a/Tests/NetworkLayerTests/Classes/Helpers/Stubs/AuthenticationCredentialStub.swift +++ b/Tests/NetworkLayerTests/Classes/Helpers/Stubs/AuthenticationCredentialStub.swift @@ -6,7 +6,7 @@ import Foundation import NetworkLayerInterfaces -final class AuthenticationCredentialStub: IAuthenticationCredential { +final class AuthenticationCredentialStub: IAuthenticationCredential, @unchecked Sendable { var stubbedRequiresRefresh: Bool! = false var requiresRefresh: Bool { diff --git a/Tests/NetworkLayerTests/Classes/Helpers/Stubs/RequestStub.swift b/Tests/NetworkLayerTests/Classes/Helpers/Stubs/RequestStub.swift index 148c675..fb1678f 100644 --- a/Tests/NetworkLayerTests/Classes/Helpers/Stubs/RequestStub.swift +++ b/Tests/NetworkLayerTests/Classes/Helpers/Stubs/RequestStub.swift @@ -6,7 +6,7 @@ import Foundation import NetworkLayerInterfaces -final class RequestStub: IRequest { +final class RequestStub: IRequest, @unchecked Sendable { var stubbedDomainName: String! = "" var domainName: String { @@ -49,7 +49,7 @@ final class RequestStub: IRequest { stubbedHttpMethod } - var stubbedHttpBody: RequestBody? = nil + var stubbedHttpBody: RequestBody? var httpBody: RequestBody? { stubbedHttpBody diff --git a/Tests/NetworkLayerTests/Classes/Helpers/Stubs/StubResponse.swift b/Tests/NetworkLayerTests/Classes/Helpers/Stubs/StubResponse.swift index 97f4471..2c6ad4a 100644 --- a/Tests/NetworkLayerTests/Classes/Helpers/Stubs/StubResponse.swift +++ b/Tests/NetworkLayerTests/Classes/Helpers/Stubs/StubResponse.swift @@ -6,7 +6,7 @@ import Foundation import Mocker -struct StubResponse { +struct StubResponse: @unchecked Sendable { let name: String let fileURL: URL let httpMethod: Mock.HTTPMethod diff --git a/Tests/NetworkLayerTests/Classes/Tests/IntegrationTests/RequestProcessorAuthenticationTests.swift b/Tests/NetworkLayerTests/Classes/Tests/IntegrationTests/RequestProcessorAuthenticationTests.swift index 51821df..81b4560 100644 --- a/Tests/NetworkLayerTests/Classes/Tests/IntegrationTests/RequestProcessorAuthenticationTests.swift +++ b/Tests/NetworkLayerTests/Classes/Tests/IntegrationTests/RequestProcessorAuthenticationTests.swift @@ -74,7 +74,7 @@ final class RequestProcessorAuthenicationTests: XCTestCase { // MARK: Private private func test_failAuthentication(adaptError: Error?, refreshError: Error?, expectedError: Error) async throws { - class FailInterceptor: IAuthenticationInterceptor { + class FailInterceptor: IAuthenticationInterceptor, @unchecked Sendable { let adaptError: Error? let refreshError: Error? @@ -125,7 +125,7 @@ final class RequestProcessorAuthenicationTests: XCTestCase { // MARK: - AuthInterceptor -private final class AuthInterceptor: IAuthenticationInterceptor { +private final class AuthInterceptor: IAuthenticationInterceptor, @unchecked Sendable { var token: Token! private var attempts = 0 diff --git a/Tests/NetworkLayerTests/Classes/Tests/IntegrationTests/RequestProcessorRequestTests.swift b/Tests/NetworkLayerTests/Classes/Tests/IntegrationTests/RequestProcessorRequestTests.swift index 8548539..8bd3a90 100644 --- a/Tests/NetworkLayerTests/Classes/Tests/IntegrationTests/RequestProcessorRequestTests.swift +++ b/Tests/NetworkLayerTests/Classes/Tests/IntegrationTests/RequestProcessorRequestTests.swift @@ -46,12 +46,35 @@ final class RequestProcessorRequestTests: XCTestCase { } } + func test_thatRequestProcessorConfigureARequest() async throws { + // given + DynamicStubs.register(stubs: [.updateProfile]) + + let endpointURL = URL(string: "\(String.domain)/\(String.updateProfile)") + let delegateMock = RequestProcessorDelegateMock() + + let sut = RequestProcessor.mock(requestProcessorDelegate: delegateMock) + let request = makeRequest(.user) + + // when + let user: Response = try await sut.send(request) { urlRequest in + urlRequest.url = endpointURL + } + + // then + XCTAssertEqual(user.data.id, 1) + XCTAssertNotNil(user.data.avatarUrl) + XCTAssertEqual(delegateMock.invokedRequestProcessorParameters?.request.httpMethod, HTTPMethod.get.rawValue) + XCTAssertEqual(delegateMock.invokedRequestProcessorParameters?.request.url, endpointURL) + } + // MARK: Private private func makeRequest(_ path: String) -> IRequest { let request = RequestStub() - request.stubbedDomainName = "https://github.com" + request.stubbedDomainName = .domain request.stubbedPath = path +// request.httpMethod = .get return request } } @@ -75,8 +98,11 @@ private final class GitHubDelegate: RequestProcessorDelegate { private extension StubResponse { static let user = StubResponse(name: .user, fileURL: MockedData.userJSON, httpMethod: .get) + static let updateProfile = StubResponse(name: .updateProfile, fileURL: MockedData.userJSON, httpMethod: .get) } private extension String { static let user = "user" + static let updateProfile = "updateProfile" + static let domain = "https://github.com" } diff --git a/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestBodyEncoderTests.swift b/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestBodyEncoderTests.swift index ad3c44c..4d66378 100644 --- a/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestBodyEncoderTests.swift +++ b/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestBodyEncoderTests.swift @@ -39,16 +39,16 @@ final class RequestBodyEncoderTests: XCTestCase { XCTAssertEqual(requestFake.httpBody, data) } - func test_thatRequestBodyEncoderEncodesBodyIntoRequest_whenTypeIsDictonary() throws { + func test_thatRequestBodyEncoderEncodesBodyIntoRequest_whenTypeIsDictionary() throws { // given var requestFake = URLRequest.fake() - let dictonary = ["test": "test"] + let dictionary = ["test": "test"] // when - try sut.encode(body: .dictonary(dictonary), to: &requestFake) + try sut.encode(body: .dictionary(dictionary), to: &requestFake) // then - let data = try JSONSerialization.data(withJSONObject: dictonary) + let data = try JSONSerialization.data(withJSONObject: dictionary) XCTAssertEqual(requestFake.httpBody, data) } diff --git a/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestBuilderTests.swift b/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestBuilderTests.swift index ce7c383..43dd0e0 100644 --- a/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestBuilderTests.swift +++ b/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestBuilderTests.swift @@ -59,7 +59,7 @@ final class RequestBuilderTests: XCTestCase { requestStub.stubbedDomainName = .domainName requestStub.stubbedHeaders = .contentType requestStub.stubbedHttpMethod = .post - requestStub.stubbedHttpBody = .dictonary(.item) + requestStub.stubbedHttpBody = .dictionary(.item) requestStub.stubbedParameters = .contentType // when @@ -72,7 +72,7 @@ final class RequestBuilderTests: XCTestCase { XCTAssertEqual(request?.httpMethod, "POST") XCTAssertEqual(parametersEncoderMock.invokedEncodeParameters?.parameters, .contentType) - if case let .dictonary(dict) = requestBodyEncoderMock.invokedEncodeParameters?.body { + if case let .dictionary(dict) = requestBodyEncoderMock.invokedEncodeParameters?.body { XCTAssertTrue(NSDictionary(dictionary: dict).isEqual(to: Dictionary.item)) } else { XCTFail("body should be equal to a dictionary") diff --git a/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestParametersEncoderTests.swift b/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestParametersEncoderTests.swift index 32995e8..d99165a 100644 --- a/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestParametersEncoderTests.swift +++ b/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestParametersEncoderTests.swift @@ -1,6 +1,6 @@ // // network-layer -// Copyright © 2023 Space Code. All rights reserved. +// Copyright © 2024 Space Code. All rights reserved. // import Foundation diff --git a/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestProcessorTests.swift b/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestProcessorTests.swift index a46d5a8..9357595 100644 --- a/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestProcessorTests.swift +++ b/Tests/NetworkLayerTests/Classes/Tests/UnitTests/RequestProcessorTests.swift @@ -43,7 +43,7 @@ final class RequestProcessorTests: XCTestCase { requestBuilder: requestBuilderMock, dataRequestHandler: dataRequestHandler, retryPolicyService: retryPolicyMock, - delegate: delegateMock, + delegate: SafeRequestProcessorDelegate(delegate: delegateMock), interceptor: interceptorMock ) } From dd1532ddfd3966d82e20f18f4fc12dbe18ffa638 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 25 Dec 2024 08:21:56 +0100 Subject: [PATCH 3/4] Update `CHANGELOG.md` (#4) --- CHANGELOG.md | 10 ++++++---- Package.resolved | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 967c6a9..5c9cf2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. -## [Unreleased] +#### 1.x Releases +- `1.1.x` Release Candidates - [1.1.0-rc.1](#110-rc1) +- `1.0.x` Releases - [1.0.0](#100) + +## [1.1.0-rc.1](https://github.com/space-code/network-layer/releases/tag/1.1.0-rc.1) +Released on 2024-12-25. #### Added - Bump the Swift version to 6.0. @@ -11,9 +16,6 @@ All notable changes to this project will be documented in this file. - Fix the `inout` parameter in the `IAuthenticator` protocol - Fixed in Pull Request [#2](https://github.com/space-code/network-layer/pull/2). -#### 1.x Releases -- `1.0.x` Releases - [1.0.0](#100) - ## [1.0.0](https://github.com/space-code/network-layer/releases/tag/1.0.0) Released on 2023-12-04. diff --git a/Package.resolved b/Package.resolved index 9c93daf..4e47abc 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "c8abfa3cdea573bdb7b39a88434ead5158c5f6b6a0344824ff93c750d4259758", "pins" : [ { "identity" : "atomic", @@ -28,5 +29,5 @@ } } ], - "version" : 2 + "version" : 3 } From bbb342f0a01ce8f124ef32f597566ea55cb7b542 Mon Sep 17 00:00:00 2001 From: Nikita Vasilev Date: Wed, 25 Dec 2024 08:34:45 +0100 Subject: [PATCH 4/4] Remove `Package.resolved` (#6) * Remove `Package.resolved` * Update `CHANGELOG.md` --- .gitignore | 1 + CHANGELOG.md | 2 ++ Package.resolved | 33 --------------------------------- 3 files changed, 3 insertions(+), 33 deletions(-) delete mode 100644 Package.resolved diff --git a/.gitignore b/.gitignore index 3b29812..5922fda 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc +Package.resolved diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c9cf2c..79cae31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ Released on 2024-12-25. #### Fixed - Fix the `inout` parameter in the `IAuthenticator` protocol - Fixed in Pull Request [#2](https://github.com/space-code/network-layer/pull/2). +- Fix the package builiding. + - Fixed in Pull Request [#6](https://github.com/space-code/network-layer/pull-6). ## [1.0.0](https://github.com/space-code/network-layer/releases/tag/1.0.0) Released on 2023-12-04. diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index 4e47abc..0000000 --- a/Package.resolved +++ /dev/null @@ -1,33 +0,0 @@ -{ - "originHash" : "c8abfa3cdea573bdb7b39a88434ead5158c5f6b6a0344824ff93c750d4259758", - "pins" : [ - { - "identity" : "atomic", - "kind" : "remoteSourceControl", - "location" : "https://github.com/space-code/atomic", - "state" : { - "revision" : "6304c805e9d97fe1239bcde561234d857164cb10", - "version" : "1.1.0" - } - }, - { - "identity" : "mocker", - "kind" : "remoteSourceControl", - "location" : "https://github.com/WeTransfer/Mocker", - "state" : { - "revision" : "4384e015cae4916a6828252467a4437173c7ae17", - "version" : "3.0.1" - } - }, - { - "identity" : "typhoon", - "kind" : "remoteSourceControl", - "location" : "https://github.com/space-code/typhoon", - "state" : { - "revision" : "5865671564c5b443a9a28d2f1dbb69bccbb5931c", - "version" : "1.2.1" - } - } - ], - "version" : 3 -}