diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7afe662..ace189b 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -3,7 +3,7 @@ name: Build
jobs:
test:
name: Build
- runs-on: macOS-latest
+ runs-on: macos-15
steps:
- name: Checkout
uses: actions/checkout@v2
diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/Package.swift b/Package.swift
index edc0cc5..01a251d 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.5
+// swift-tools-version:5.9
//
// Package.swift
//
@@ -30,10 +30,12 @@ import PackageDescription
let package = Package(
name: "DBNetworkStack",
platforms: [
- .iOS(.v9),
- .tvOS(.v9),
- .watchOS(.v2),
- .macOS(.v10_10)
+ .iOS(.v13),
+ .tvOS(.v13),
+ .watchOS(.v6),
+ .macOS(.v10_15),
+ .visionOS(.v1),
+ .macCatalyst(.v14)
],
products: [
.library(
@@ -44,7 +46,11 @@ let package = Package(
.target(
name: "DBNetworkStack",
dependencies: [],
- path: "Source"),
+ path: "Source",
+ swiftSettings: [
+ .enableExperimentalFeature("StrictConcurrency")
+ ]
+ ),
.testTarget(
name: "DBNetworkStackTests",
dependencies: ["DBNetworkStack"],
diff --git a/Source/ContainerNetworkTask.swift b/Source/ContainerNetworkTask.swift
deleted file mode 100644
index 59b4eef..0000000
--- a/Source/ContainerNetworkTask.swift
+++ /dev/null
@@ -1,72 +0,0 @@
-//
-// Copyright (C) 2018 DB Systel GmbH.
-// DB Systel GmbH; Jürgen-Ponto-Platz 1; D-60329 Frankfurt am Main; Germany; http://www.dbsystel.de/
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the "Software"),
-// to deal in the Software without restriction, including without limitation
-// the rights to use, copy, modify, merge, publish, distribute, sublicense,
-// and/or sell copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-//
-
-import Foundation
-
-/// A task which contains another task which can be updated in fligh.
-/// Use this task to compose a chain of requests during the original request.
-/// An oAuth Flow would be a good example for this.
-///
-/// - Note: Take look at `RetryNetworkService` to see how to use it in detail.
-public final class ContainerNetworkTask: NetworkTask {
-
- // MARK: - Init
-
- /// Creates a `ContainerNetworkTask` instance.
- public init() { }
-
- // MARK: - Override
-
- // MARK: - Protocol NetworkTask
-
- /**
- Resumes a task.
- */
- public func resume() {
- underlyingTask?.resume()
- }
-
- /**
- Cancels the underlying task.
- */
- public func cancel() {
- isCanceled = true
- underlyingTask?.cancel()
- }
-
- /**
- Suspends a task.
- */
- public func suspend() {
- underlyingTask?.suspend()
- }
-
- // MARK: - Public
-
- /// The underlying task
- public var underlyingTask: NetworkTask?
-
- /// Indicates if the request has been canceled.
- /// When composing multiple requests this flag must be respected.
- public private(set) var isCanceled = false
-}
diff --git a/Source/HTTPMethod.swift b/Source/HTTPMethod.swift
index dedec34..d5a3c53 100644
--- a/Source/HTTPMethod.swift
+++ b/Source/HTTPMethod.swift
@@ -29,7 +29,7 @@ import Foundation
See [IETF document](https://tools.ietf.org/html/rfc7231#section-4.3)
*/
-public enum HTTPMethod: String {
+public enum HTTPMethod: String, Sendable {
case GET
case POST
case PUT
diff --git a/Source/NetworkAccess.swift b/Source/NetworkAccess.swift
index 7f8df17..539a033 100644
--- a/Source/NetworkAccess.swift
+++ b/Source/NetworkAccess.swift
@@ -24,7 +24,7 @@
import Foundation
/// `NetworkAccess` provides access to the network.
-public protocol NetworkAccess {
+public protocol NetworkAccess: Sendable {
/// Fetches a request asynchrony from remote location.
///
@@ -32,6 +32,6 @@ public protocol NetworkAccess {
/// - request: The request one wants to fetch.
/// - callback: Callback which gets called when the request finishes.
/// - Returns: the running network task
- func load(request: URLRequest, callback: @escaping (Data?, HTTPURLResponse?, Error?) -> Void) -> NetworkTask
+ func load(request: URLRequest) async throws -> (Data, URLResponse)
}
diff --git a/Source/NetworkError.swift b/Source/NetworkError.swift
index eb48cb8..11e5d91 100644
--- a/Source/NetworkError.swift
+++ b/Source/NetworkError.swift
@@ -24,7 +24,7 @@
import Foundation
/// `NetworkError` provides a collection of error types which can occur during execution.
-public enum NetworkError: Error {
+public enum NetworkError: Error, Sendable {
/// The error is unkonw
case unknownError
/// The request was cancelled before it finished
@@ -36,15 +36,11 @@ public enum NetworkError: Error {
/// Error on the server (HTTP Error 500...511)
case serverError(response: HTTPURLResponse?, data: Data?)
/// Parsing the body into expected type failed.
- case serializationError(error: Error, data: Data?)
+ case serializationError(error: Error, response: HTTPURLResponse, data: Data?)
/// Complete request failed.
case requestError(error: Error)
- public init?(response: HTTPURLResponse?, data: Data?) {
- guard let response = response else {
- return nil
- }
-
+ public init?(response: HTTPURLResponse, data: Data) {
switch response.statusCode {
case 200..<300: return nil
case 401:
@@ -79,22 +75,30 @@ extension NetworkError: CustomDebugStringConvertible {
case .cancelled:
return "Request cancelled"
case .unauthorized(let response, let data):
- return "Authorization error: \(response), response: ".appendingContentsOf(data: data)
+ return "Authorization error, response headers: \(response), response body: ".appendingContentsOf(data: data)
case .clientError(let response, let data):
if let response = response {
- return "Client error: \((response)), response: ".appendingContentsOf(data: data)
+ return "Client error, response headers: \((response)), response body: ".appendingContentsOf(data: data)
}
- return "Client error, response: ".appendingContentsOf(data: data)
- case .serializationError(let description, let data):
- return "Serialization error: \(description), response: ".appendingContentsOf(data: data)
+ return "Client error, response headers: nil, response body: ".appendingContentsOf(data: data)
+ case .serializationError(let error, let response, let data):
+ return "Serialization error: \(error), response headers: \(response), response body: ".appendingContentsOf(data: data)
case .requestError(let error):
return "Request error: \(error)"
case .serverError(let response, let data):
- if let response = response {
- return "Server error: \(String(describing: response)), response: ".appendingContentsOf(data: data)
+ if let response {
+ return "Server error, response headers: \(String(describing: response)), response body: ".appendingContentsOf(data: data)
} else {
- return "Server error: nil, response: ".appendingContentsOf(data: data)
+ return "Server error: nil, response body: ".appendingContentsOf(data: data)
}
}
}
}
+
+extension NetworkError: NetworkErrorConvertible {
+
+ public init(networkError: NetworkError) {
+ self = networkError
+ }
+
+}
diff --git a/Source/NetworkErrorConvertible.swift b/Source/NetworkErrorConvertible.swift
new file mode 100644
index 0000000..3c8a115
--- /dev/null
+++ b/Source/NetworkErrorConvertible.swift
@@ -0,0 +1,14 @@
+//
+// File.swift
+//
+//
+// Created by Lukas Schmidt on 13.10.23.
+//
+
+import Foundation
+
+public protocol NetworkErrorConvertible: Error {
+
+ init(networkError: NetworkError)
+
+}
diff --git a/Source/NetworkResponseProcessor.swift b/Source/NetworkResponseProcessor.swift
deleted file mode 100644
index 998be4a..0000000
--- a/Source/NetworkResponseProcessor.swift
+++ /dev/null
@@ -1,95 +0,0 @@
-//
-// Copyright (C) 2017 DB Systel GmbH.
-// DB Systel GmbH; Jürgen-Ponto-Platz 1; D-60329 Frankfurt am Main; Germany; http://www.dbsystel.de/
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the "Software"),
-// to deal in the Software without restriction, including without limitation
-// the rights to use, copy, modify, merge, publish, distribute, sublicense,
-// and/or sell copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-//
-
-import Foundation
-import Dispatch
-
-final class NetworkResponseProcessor {
- /**
- Processes the results of an HTTPRequest and parses the result the matching Model type of the given resource.
-
- Great error handling should be implemented here as well.
-
- - parameter response: response from the server. Could be nil
- - parameter resource: The resource matching the response.
- - parameter data: Returned data. Could be nil.
- - parameter error: the return error. Could be nil.
-
- - returns: the parsed model object.
- */
- func process(response: HTTPURLResponse?, resource: Resource, data: Data?, error: Error?) throws -> Result {
- if let error = error {
- if case URLError.cancelled = error {
- throw NetworkError.cancelled
- }
-
- throw NetworkError.requestError(error: error)
- }
- if let responseError = NetworkError(response: response, data: data) {
- throw responseError
- }
- guard let data = data else {
- throw NetworkError.serverError(response: response, data: nil)
- }
- do {
- return try resource.parse(data)
- } catch let error {
- throw NetworkError.serializationError(error: error, data: data)
- }
- }
-
- /// This parseses a `HTTPURLResponse` with a given resource into the result type of the resource or errors.
- /// The result will be return via a blocks onCompletion/onError.
- ///
- /// - Parameters:
- /// - queue: The `DispatchQueue` to execute the completion and error block on.
- /// - response: the HTTPURLResponse one wants to parse.
- /// - resource: the resource.
- /// - data: the payload of the response.
- /// - error: optional error from net network.
- /// - onCompletion: completion block which gets called on the given `queue`.
- /// - onError: error block which gets called on the given `queue`.
- func processAsyncResponse(queue: DispatchQueue, response: HTTPURLResponse?, resource: Resource, data: Data?,
- error: Error?, onCompletion: @escaping (Result, HTTPURLResponse) -> Void, onError: @escaping (NetworkError) -> Void) {
- do {
- let parsed = try process(
- response: response,
- resource: resource,
- data: data,
- error: error
- )
- queue.async {
- if let response = response {
- onCompletion(parsed, response)
- } else {
- onError(NetworkError.unknownError)
- }
- }
- } catch let genericError {
- let dbNetworkError: NetworkError! = genericError as? NetworkError
- queue.async {
- return onError(dbNetworkError)
- }
- }
- }
-}
diff --git a/Source/NetworkService+Async.swift b/Source/NetworkService+Async.swift
deleted file mode 100644
index df9ad99..0000000
--- a/Source/NetworkService+Async.swift
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-// File.swift
-//
-//
-// Created by Lukas Schmidt on 19.12.21.
-//
-
-import Foundation
-
-@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
-public extension NetworkService {
-
- /**
- Fetches a resource asynchronously from remote location. Execution of the requests starts immediately.
-
- **Example**:
- ```swift
- let networkService: NetworkService = //
- let resource: Resource = //
-
- let (result, response) = try await networkService.request(resource)
- ```
-
- - parameter resource: The resource you want to fetch.
-
- - returns: a touple containing the parsed result and the HTTP response
- - Throws: A `NetworkError`
- */
- @discardableResult
- func request(_ resource: Resource) async throws -> (Result, HTTPURLResponse) {
- var task: NetworkTask?
- let cancel = { task?.cancel() }
- return try await withTaskCancellationHandler(operation: {
- try Task.checkCancellation()
- return try await withCheckedThrowingContinuation({ coninuation in
- task = request(resource: resource, onCompletionWithResponse: {
- coninuation.resume(with: $0)
- })
- })
- }, onCancel: {
- cancel()
- })
- }
-
- /**
- Fetches a resource asynchronously from remote location. Execution of the requests starts immediately.
-
- **Example**:
- ```swift
- let networkService: NetworkService = //
- let resource: ResourceWithError = //
-
- let (result, response) = try await networkService.request(resource)
- ```
-
- - parameter resource: The resource you want to fetch.
-
- - returns: a touple containing the parsed result and the HTTP response
- - Throws: Custom Error provided by ResourceWithError
- */
- @discardableResult
- func request(_ resource: ResourceWithError) async throws -> (Result, HTTPURLResponse) {
- var task: NetworkTask?
- let cancel = { task?.cancel() }
- return try await withTaskCancellationHandler(operation: {
- try Task.checkCancellation()
- return try await withCheckedThrowingContinuation({ coninuation in
- task = request(resource: resource, onCompletionWithResponse: {
- coninuation.resume(with: $0)
- })
- })
- }, onCancel: {
- cancel()
- })
- }
-
-}
diff --git a/Source/NetworkService+ResourceWithError.swift b/Source/NetworkService+ResourceWithError.swift
index 0842392..45ebb95 100644
--- a/Source/NetworkService+ResourceWithError.swift
+++ b/Source/NetworkService+ResourceWithError.swift
@@ -51,16 +51,12 @@ extension NetworkService {
- returns: a running network task
*/
@discardableResult
- public func request(
- queue: DispatchQueue,
- resource: ResourceWithError,
- onCompletionWithResponse: @escaping (Result, HTTPURLResponse) -> Void,
- onError: @escaping (E) -> Void
- ) -> NetworkTask {
- let resourceWithoutError = Resource(request: resource.request, parse: resource.parse)
- return request(queue: queue, resource: resourceWithoutError, onCompletionWithResponse: onCompletionWithResponse) { networkError in
- onError(resource.mapError(networkError))
- }
+ public func requestResultWithResponse(
+ for resource: Resource
+ ) async -> Result<(Success, HTTPURLResponse), E> {
+ let resourceWithoutError = Resource(request: resource.request, parse: resource.parse)
+ return await self.requestResultWithResponse(for: resourceWithoutError)
+ .mapError(resource.mapError)
}
/**
@@ -88,17 +84,13 @@ extension NetworkService {
- returns: a running network task
*/
@discardableResult
- public func request(
- _ resource: ResourceWithError,
- onCompletion: @escaping (Result) -> Void,
- onError: @escaping (E) -> Void
- ) -> NetworkTask {
- return request(
- queue: .main,
- resource: resource,
- onCompletionWithResponse: { model, _ in onCompletion(model) },
- onError: onError
- )
+ public func requestResult(
+ for resource: Resource
+ ) async -> Result {
+ let resourceWithoutError = Resource(request: resource.request, parse: resource.parse)
+ return await requestResultWithResponse(for: resourceWithoutError)
+ .mapError(resource.mapError)
+ .map({ $0.0 })
}
/**
@@ -127,86 +119,19 @@ extension NetworkService {
- returns: a running network task
*/
@discardableResult
- func request(
- queue: DispatchQueue = .main,
- resource: ResourceWithError,
- onCompletionWithResponse: @escaping (Swift.Result<(Result, HTTPURLResponse), E>) -> Void
- ) -> NetworkTask {
- return request(
- queue: queue,
- resource: resource,
- onCompletionWithResponse: { result, response in
- onCompletionWithResponse(.success((result, response)))
- }, onError: { error in
- onCompletionWithResponse(.failure(error))
- }
- )
+ public func request(
+ _ resource: Resource
+ ) async throws(E) -> Success {
+ let resourceWithoutError = Resource(request: resource.request, parse: resource.parse)
+ return try await requestResultWithResponse(for: resourceWithoutError)
+ .mapError(resource.mapError)
+ .map({ $0.0 })
+ .get()
}
- /**
- Fetches a resource asynchronously from remote location. Execution of the requests starts immediately.
- Execution happens on no specific queue. It dependes on the network access which queue is used.
- Once execution is finished either the completion block or the error block gets called.
- These blocks are called on the main queue.
-
- **Example**:
- ```swift
- let networkService: NetworkService = //
- let resource: ResourceWithError = //
-
- networkService.request(resource, onCompletion: { htmlText in
- print(htmlText)
- }, onError: { error in
- // Handle errors
- })
- ```
-
- - parameter resource: The resource you want to fetch.
- - parameter onComplition: Callback which gets called when fetching and transforming into model succeeds.
- - parameter onError: Callback which gets called with an custom error.
-
- - returns: a running network task
- */
@discardableResult
- public func request(
- _ resource: ResourceWithError,
- onCompletion: @escaping (Swift.Result) -> Void
- ) -> NetworkTask {
- return request(
- queue: .main,
- resource: resource,
- onCompletionWithResponse: { model, _ in onCompletion(.success(model)) },
- onError: { onCompletion(.failure($0))}
- )
+ func requestWithResponse(for resource: Resource) async throws(E) -> (Success, HTTPURLResponse) {
+ return try await requestResultWithResponse(for: resource).get()
}
- /**
- Fetches a resource asynchronously from remote location. Execution of the requests starts immediately.
- Execution happens on no specific queue. It dependes on the network access which queue is used.
- Once execution is finished either the completion block or the error block gets called.
- These blocks are called on the main queue.
-
- **Example**:
- ```swift
- let networkService: NetworkService = //
- let resource: Resource = //
-
- networkService.request(resource, onCompletionWithResponse: { htmlText, httpResponse in
- print(htmlText, httpResponse)
- }, onError: { error in
- // Handle errors
- })
- ```
-
- - parameter resource: The resource you want to fetch.
- - parameter onCompletion: Callback which gets called when fetching and transforming into model succeeds.
- - parameter onError: Callback which gets called when fetching or transforming fails.
-
- - returns: a running network task
- */
- @discardableResult
- func request(_ resource: ResourceWithError, onCompletionWithResponse: @escaping (Result, HTTPURLResponse) -> Void,
- onError: @escaping (E) -> Void) -> NetworkTask {
- return request(queue: .main, resource: resource, onCompletionWithResponse: onCompletionWithResponse, onError: onError)
- }
}
diff --git a/Source/NetworkService+Result.swift b/Source/NetworkService+Result.swift
deleted file mode 100644
index f361c33..0000000
--- a/Source/NetworkService+Result.swift
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// NetworkService+Result.swift
-// DBNetworkStack
-//
-// Created by Lukas Schmidt on 03.01.19.
-// Copyright © 2019 DBSystel. All rights reserved.
-//
-
-import Foundation
-
-public extension NetworkService {
-
- /**
- Fetches a resource asynchronously from remote location. Execution of the requests starts immediately.
- Execution happens on no specific queue. It dependes on the network access which queue is used.
- Once execution is finished the completion block gets called.
- You decide on which queue completion gets executed. Defaults to `main`.
-
- **Example**:
- ```swift
- let networkService: NetworkService = //
- let resource: Resource = //
-
- networkService.request(resource: resource, onCompletionWithResponse: { result in
- print(result)
- })
- ```
-
- - parameter queue: The `DispatchQueue` to execute the completion block on. Defaults to `main`.
- - parameter resource: The resource you want to fetch.
- - parameter onCompletionWithResponse: Callback which gets called when request completes.
-
- - returns: a running network task
- */
- @discardableResult
- func request(queue: DispatchQueue = .main,
- resource: Resource,
- onCompletionWithResponse: @escaping (Swift.Result<(Result, HTTPURLResponse), NetworkError>) -> Void) -> NetworkTask {
- return request(queue: queue,
- resource: resource,
- onCompletionWithResponse: { result, response in
- onCompletionWithResponse(.success((result, response)))
- }, onError: { error in
- onCompletionWithResponse(.failure(error))
- })
- }
-
- /**
- Fetches a resource asynchronously from remote location. Execution of the requests starts immediately.
- Execution happens on no specific queue. It dependes on the network access which queue is used.
- Once execution is finished the completion block gets called.
- Completion gets executed on `main` queue.
-
- **Example**:
- ```swift
- let networkService: NetworkService = //
- let resource: Resource = //
-
- networkService.request(resource, onCompletion: { result in
- print(result)
- })
- ```
-
- - parameter resource: The resource you want to fetch.
- - parameter onComplition: Callback which gets called when request completes.
-
- - returns: a running network task
- */
- @discardableResult
- func request(_ resource: Resource,
- onCompletion: @escaping (Swift.Result) -> Void) -> NetworkTask {
- return request(resource: resource,
- onCompletionWithResponse: { result in
- switch result {
- case .success(let response):
- onCompletion(.success(response.0))
- case .failure(let error):
- onCompletion(.failure(error))
- }
- })
- }
-}
diff --git a/Source/NetworkService.swift b/Source/NetworkService.swift
index 3c3d379..1067f5f 100644
--- a/Source/NetworkService.swift
+++ b/Source/NetworkService.swift
@@ -30,7 +30,7 @@ import Dispatch
- seealso: `BasicNetworkService`
- seealso: `NetworkServiceMock`
*/
-public protocol NetworkService {
+public protocol NetworkService: Sendable {
/**
Fetches a resource asynchronously from remote location. Execution of the requests starts immediately.
Execution happens on no specific queue. It dependes on the network access which queue is used.
@@ -42,26 +42,19 @@ public protocol NetworkService {
let networkService: NetworkService = //
let resource: Resource = //
- networkService.request(queue: .main, resource: resource, onCompletionWithResponse: { htmlText, response in
- print(htmlText, response)
- }, onError: { error in
- // Handle errors
- })
+ let result = await networkService.requestResultWithResponse(for: resource)
```
- - parameter queue: The `DispatchQueue` to execute the completion and error block on.
- parameter resource: The resource you want to fetch.
- - parameter onCompletionWithResponse: Callback which gets called when fetching and transforming into model succeeds.
- - parameter onError: Callback which gets called when fetching or transforming fails.
-
- - returns: a running network task
+
+ - returns: a result containing either the success or a network error
*/
@discardableResult
- func request(queue: DispatchQueue, resource: Resource, onCompletionWithResponse: @escaping (Result, HTTPURLResponse) -> Void,
- onError: @escaping (NetworkError) -> Void) -> NetworkTask
+ func requestResultWithResponse(for resource: Resource) async -> Result<(Success, HTTPURLResponse), NetworkError>
}
public extension NetworkService {
+
/**
Fetches a resource asynchronously from remote location. Execution of the requests starts immediately.
Execution happens on no specific queue. It dependes on the network access which queue is used.
@@ -72,53 +65,44 @@ public extension NetworkService {
```swift
let networkService: NetworkService = //
let resource: Resource = //
-
- networkService.request(resource, onCompletion: { htmlText in
- print(htmlText)
- }, onError: { error in
- // Handle errors
- })
+
+ let result = await networkService.requestResult(for: resource)
```
-
+
- parameter resource: The resource you want to fetch.
- - parameter onComplition: Callback which gets called when fetching and transforming into model succeeds.
- - parameter onError: Callback which gets called when fetching or transforming fails.
-
- - returns: a running network task
+
+ - returns: a result containing either the success or a network error
*/
@discardableResult
- func request(_ resource: Resource, onCompletion: @escaping (Result) -> Void,
- onError: @escaping (NetworkError) -> Void) -> NetworkTask {
- return request(queue: .main, resource: resource, onCompletionWithResponse: { model, _ in onCompletion(model) }, onError: onError)
+ func requestResult(for resource: Resource) async -> Result {
+ return await requestResultWithResponse(for: resource).map({ $0.0 })
}
-
+
/**
Fetches a resource asynchronously from remote location. Execution of the requests starts immediately.
Execution happens on no specific queue. It dependes on the network access which queue is used.
Once execution is finished either the completion block or the error block gets called.
These blocks are called on the main queue.
-
+
**Example**:
```swift
let networkService: NetworkService = //
let resource: Resource = //
-
- networkService.request(resource, onCompletionWithResponse: { htmlText, httpResponse in
- print(htmlText, httpResponse)
- }, onError: { error in
- // Handle errors
- })
+
+ let result = await networkService.request(resource)
```
-
+
- parameter resource: The resource you want to fetch.
- - parameter onCompletion: Callback which gets called when fetching and transforming into model succeeds.
- - parameter onError: Callback which gets called when fetching or transforming fails.
-
- - returns: a running network task
+
+ - returns: a result containing either the success or a network error
*/
@discardableResult
- func request(_ resource: Resource, onCompletionWithResponse: @escaping (Result, HTTPURLResponse) -> Void,
- onError: @escaping (NetworkError) -> Void) -> NetworkTask {
- return request(queue: .main, resource: resource, onCompletionWithResponse: onCompletionWithResponse, onError: onError)
+ func request(_ resource: Resource) async throws(NetworkError) -> Success {
+ return try await requestResultWithResponse(for: resource).get().0
+ }
+
+ @discardableResult
+ func requestWithResponse(for resource: Resource) async throws(NetworkError) -> (Success, HTTPURLResponse) {
+ return try await requestResultWithResponse(for: resource).get()
}
}
diff --git a/Source/NetworkServiceMock.swift b/Source/NetworkServiceMock.swift
deleted file mode 100644
index 11fe5ba..0000000
--- a/Source/NetworkServiceMock.swift
+++ /dev/null
@@ -1,205 +0,0 @@
-//
-// Copyright (C) 2017 DB Systel GmbH.
-// DB Systel GmbH; Jürgen-Ponto-Platz 1; D-60329 Frankfurt am Main; Germany; http://www.dbsystel.de/
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the "Software"),
-// to deal in the Software without restriction, including without limitation
-// the rights to use, copy, modify, merge, publish, distribute, sublicense,
-// and/or sell copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-//
-
-import Foundation
-import Dispatch
-
-struct NetworkServiceMockCallback {
- let onErrorCallback: (NetworkError) -> Void
- let onTypedSuccess: (Any, HTTPURLResponse) throws -> Void
-
- init(resource: Resource, onCompletionWithResponse: @escaping (Result, HTTPURLResponse) -> Void, onError: @escaping (NetworkError) -> Void) {
- onTypedSuccess = { anyResult, response in
- guard let typedResult = anyResult as? Result else {
- throw NetworkServiceMock.Error.typeMismatch
- }
- onCompletionWithResponse(typedResult, response)
- }
- onErrorCallback = { error in
- onError(error)
- }
- }
-}
-
-/**
- Mocks a `NetworkService`.
- You can configure expected results or errors to have a fully functional mock.
-
- **Example**:
- ```swift
- //Given
- let networkServiceMock = NetworkServiceMock()
- let resource: Resource = //
-
- //When
- networkService.request(
- resource,
- onCompletion: { string in /*...*/ },
- onError: { error in /*...*/ }
- )
- networkService.returnSuccess(with: "Sucess")
-
- //Then
- //Test your expectations
-
- ```
-
- It is possible to start multiple requests at a time.
- All requests and responses (or errors) are processed
- in order they have been called. So, everything is serial.
-
- **Example**:
- ```swift
- //Given
- let networkServiceMock = NetworkServiceMock()
- let resource: Resource = //
-
- //When
- networkService.request(
- resource,
- onCompletion: { string in /* Success */ },
- onError: { error in /*...*/ }
- )
- networkService.request(
- resource,
- onCompletion: { string in /*...*/ },
- onError: { error in /*. cancel error .*/ }
- )
-
- networkService.returnSuccess(with: "Sucess")
- networkService.returnError(with: .cancelled)
-
- //Then
- //Test your expectations
-
- ```
-
- - seealso: `NetworkService`
- */
-public final class NetworkServiceMock: NetworkService {
-
- public enum Error: Swift.Error, CustomDebugStringConvertible {
- case missingRequest
- case typeMismatch
-
- public var debugDescription: String {
- switch self {
- case .missingRequest:
- return "Could not return because no request"
- case .typeMismatch:
- return "Return type does not match requested type"
- }
- }
- }
-
- /// Count of all started requests
- public var requestCount: Int {
- return lastRequests.count
- }
-
- /// Last executed request
- public var lastRequest: URLRequest? {
- return lastRequests.last
- }
-
- public var pendingRequestCount: Int {
- return callbacks.count
- }
-
- /// All executed requests.
- public private(set) var lastRequests: [URLRequest] = []
-
- /// Set this to hava a custom networktask returned by the mock
- public var nextNetworkTask: NetworkTask?
-
- private var callbacks: [NetworkServiceMockCallback] = []
-
- /// Creates an instace of `NetworkServiceMock`
- public init() {}
-
- /**
- Fetches a resource asynchronously from remote location. Execution of the requests starts immediately.
- Execution happens on no specific queue. It dependes on the network access which queue is used.
- Once execution is finished either the completion block or the error block gets called.
- You decide on which queue these blocks get executed.
-
- **Example**:
- ```swift
- let networkService: NetworkService = //
- let resource: Resource = //
-
- networkService.request(queue: .main, resource: resource, onCompletionWithResponse: { htmlText, response in
- print(htmlText, response)
- }, onError: { error in
- // Handle errors
- })
- ```
-
- - parameter queue: The `DispatchQueue` to execute the completion and error block on.
- - parameter resource: The resource you want to fetch.
- - parameter onCompletionWithResponse: Callback which gets called when fetching and transforming into model succeeds.
- - parameter onError: Callback which gets called when fetching or transforming fails.
-
- - returns: a running network task
- */
- @discardableResult
- public func request(queue: DispatchQueue, resource: Resource, onCompletionWithResponse: @escaping (Result, HTTPURLResponse) -> Void,
- onError: @escaping (NetworkError) -> Void) -> NetworkTask {
- lastRequests.append(resource.request)
- callbacks.append(NetworkServiceMockCallback(
- resource: resource,
- onCompletionWithResponse: onCompletionWithResponse,
- onError: onError
- ))
-
- return nextNetworkTask ?? NetworkTaskMock()
- }
-
- /// Will return an error to the current waiting request.
- ///
- /// - Parameters:
- /// - error: the error which gets passed to the caller
- ///
- /// - Throws: An error of type `NetworkServiceMock.Error`
- public func returnError(with error: NetworkError) throws {
- guard !callbacks.isEmpty else {
- throw Error.missingRequest
- }
- callbacks.removeFirst().onErrorCallback(error)
- }
-
- /// Will return a successful request, by using the given type `T` as serialized result of a request.
- ///
- /// - Parameters:
- /// - data: the mock response from the server. `Data()` by default
- /// - httpResponse: the mock `HTTPURLResponse` from the server. `HTTPURLResponse()` by default
- ///
- /// - Throws: An error of type `NetworkServiceMock.Error`
- public func returnSuccess(with serializedResponse: T, httpResponse: HTTPURLResponse = HTTPURLResponse()) throws {
- guard !callbacks.isEmpty else {
- throw Error.missingRequest
- }
- try callbacks.removeFirst().onTypedSuccess(serializedResponse, httpResponse)
- }
-
-}
diff --git a/Source/BasicNetworkService.swift b/Source/NetworkServices/BasicNetworkService.swift
similarity index 75%
rename from Source/BasicNetworkService.swift
rename to Source/NetworkServices/BasicNetworkService.swift
index 71ed881..c874653 100644
--- a/Source/BasicNetworkService.swift
+++ b/Source/NetworkServices/BasicNetworkService.swift
@@ -36,9 +36,8 @@ import Dispatch
- seealso: `NetworkService`
*/
public final class BasicNetworkService: NetworkService {
- let networkAccess: NetworkAccess
- let networkResponseProcessor: NetworkResponseProcessor
-
+ private let networkAccess: NetworkAccess
+
/**
Creates an `BasicNetworkService` instance with a given network access to execute requests on.
@@ -46,7 +45,6 @@ public final class BasicNetworkService: NetworkService {
*/
public init(networkAccess: NetworkAccess) {
self.networkAccess = networkAccess
- self.networkResponseProcessor = NetworkResponseProcessor()
}
/**
@@ -75,14 +73,29 @@ public final class BasicNetworkService: NetworkService {
- returns: a running network task
*/
@discardableResult
- public func request(queue: DispatchQueue, resource: Resource, onCompletionWithResponse: @escaping (Result, HTTPURLResponse) -> Void,
- onError: @escaping (NetworkError) -> Void) -> NetworkTask {
- let request = resource.request
- let dataTask = networkAccess.load(request: request, callback: { data, response, error in
- self.networkResponseProcessor.processAsyncResponse(queue: queue, response: response, resource: resource, data: data,
- error: error, onCompletion: onCompletionWithResponse, onError: onError)
- })
- return dataTask
+ public func requestResultWithResponse(for resource: Resource) async -> Result<(Success, HTTPURLResponse), NetworkError> {
+ do {
+ let (data, response) = try await networkAccess.load(request: resource.request)
+ guard let response = response as? HTTPURLResponse else {
+ return .failure(.unknownError)
+ }
+ if let responseError = NetworkError(response: response, data: data) {
+ return .failure(responseError)
+ }
+
+ do {
+ return .success((try resource.parse(data), response))
+ } catch let error {
+ return .failure(.serializationError(error: error, response: response, data: data))
+ }
+ } catch let error {
+ if case URLError.cancelled = error {
+ return .failure(.cancelled)
+ }
+
+ return .failure(.requestError(error: error))
+ }
+
}
}
diff --git a/Source/ModifyRequestNetworkService.swift b/Source/NetworkServices/ModifyRequestNetworkService.swift
similarity index 86%
rename from Source/ModifyRequestNetworkService.swift
rename to Source/NetworkServices/ModifyRequestNetworkService.swift
index 3d3da04..47b6763 100644
--- a/Source/ModifyRequestNetworkService.swift
+++ b/Source/NetworkServices/ModifyRequestNetworkService.swift
@@ -41,7 +41,7 @@ import Dispatch
*/
public final class ModifyRequestNetworkService: NetworkService {
- private let requestModifications: [(URLRequest) -> URLRequest]
+ private let requestModifications: [@Sendable (URLRequest) -> URLRequest]
private let networkService: NetworkService
/// Creates an insatcne of `ModifyRequestNetworkService`.
@@ -49,7 +49,7 @@ public final class ModifyRequestNetworkService: NetworkService {
/// - Parameters:
/// - networkService: a networkservice.
/// - requestModifications: array of modifications to modify requests.
- public init(networkService: NetworkService, requestModifications: [(URLRequest) -> URLRequest]) {
+ public init(networkService: NetworkService, requestModifications: [@Sendable (URLRequest) -> URLRequest]) {
self.networkService = networkService
self.requestModifications = requestModifications
}
@@ -80,12 +80,11 @@ public final class ModifyRequestNetworkService: NetworkService {
- returns: a running network task
*/
@discardableResult
- public func request(queue: DispatchQueue, resource: Resource, onCompletionWithResponse: @escaping (Result, HTTPURLResponse) -> Void,
- onError: @escaping (NetworkError) -> Void) -> NetworkTask {
+ public func requestResultWithResponse(for resource: Resource) async -> Result<(Success, HTTPURLResponse), NetworkError> {
let request = requestModifications.reduce(resource.request, { request, modify in
return modify(request)
})
- let newResource = Resource(request: request, parse: resource.parse)
- return networkService.request(queue: queue, resource: newResource, onCompletionWithResponse: onCompletionWithResponse, onError: onError)
+ let newResource = Resource(request: request, parse: resource.parse)
+ return await networkService.requestResultWithResponse(for: newResource)
}
}
diff --git a/Source/NetworkServices/NetworkServiceMock.swift b/Source/NetworkServices/NetworkServiceMock.swift
new file mode 100644
index 0000000..cce6ead
--- /dev/null
+++ b/Source/NetworkServices/NetworkServiceMock.swift
@@ -0,0 +1,260 @@
+//
+// Created by Lukas Schmidt on 27.01.22.
+//
+
+import Foundation
+
+
+/**
+ Mocks a `NetworkService`.
+ You can configure expected results or errors to have a fully functional mock.
+
+ **Example**:
+ ```swift
+ //Given
+ let networkServiceMock = NetworkServiceMock()
+ let resource: Resource = //
+
+ //When
+ networkService.request(
+ resource,
+ onCompletion: { string in /*...*/ },
+ onError: { error in /*...*/ }
+ )
+ networkService.returnSuccess(with: "Sucess")
+
+ //Then
+ //Test your expectations
+
+ ```
+
+ It is possible to start multiple requests at a time.
+ All requests and responses (or errors) are processed
+ in order they have been called. So, everything is serial.
+
+ **Example**:
+ ```swift
+ //Given
+ let networkServiceMock = NetworkServiceMock()
+ let resource: Resource = //
+
+ //When
+ networkService.request(
+ resource,
+ onCompletion: { string in /* Success */ },
+ onError: { error in /*...*/ }
+ )
+ networkService.request(
+ resource,
+ onCompletion: { string in /*...*/ },
+ onError: { error in /*. cancel error .*/ }
+ )
+
+ networkService.returnSuccess(with: "Sucess")
+ networkService.returnError(with: .cancelled)
+
+ //Then
+ //Test your expectations
+
+ ```
+
+ - seealso: `NetworkService`
+ */
+public final class NetworkServiceMock: NetworkService, @unchecked Sendable {
+
+ /// Count of all started requests
+ public var requestCount: Int {
+ return lastRequests.count
+ }
+
+ /// Last executed request
+ public var lastRequest: URLRequest? {
+ return lastRequests.last
+ }
+
+ /// All executed requests.
+ public private(set) var lastRequests: [URLRequest] = []
+
+ private var responses: [(String, Result<(Data, HTTPURLResponse), NetworkError>)]
+ private let encoder: JSONEncoder
+
+ /// Creates an instace of `NetworkServiceMock`
+ public init(
+ responses: [Result<(Data, HTTPURLResponse), NetworkError>] = [],
+ encoder: JSONEncoder = JSONEncoder()
+ ) {
+ self.encoder = encoder
+ self.responses = responses.map({ ("*", $0) })
+ }
+
+ /// Creates an instace of `NetworkServiceMock`
+ public init(
+ _ responses: repeat Result<(each T, HTTPURLResponse), NetworkError>,
+ encoder: JSONEncoder = JSONEncoder()
+ ) {
+ self.encoder = encoder
+ var encodedResponses: [Result<(Data, HTTPURLResponse), NetworkError>] = []
+ repeat encodedResponses.append((each responses).encode(encoder: encoder))
+ self.responses = encodedResponses.map({ ("*", $0) })
+ }
+
+ /// Creates an instace of `NetworkServiceMock`
+ public init(
+ _ responses: repeat Result,
+ encoder: JSONEncoder = JSONEncoder()
+ ) {
+ self.encoder = encoder
+ var encodedResponses: [Result<(Data, HTTPURLResponse), NetworkError>] = []
+ repeat encodedResponses.append((each responses).encode(encoder: encoder))
+ self.responses = encodedResponses.map({ ("*", $0) })
+ }
+
+ /// Creates an instace of `NetworkServiceMock`
+ public init(
+ mappings responses: [(String, Result<(Data, HTTPURLResponse), NetworkError>)],
+ encoder: JSONEncoder = JSONEncoder()
+ ) {
+ self.encoder = encoder
+ self.responses = responses
+ }
+
+ /// Creates an instace of `NetworkServiceMock`
+ public init(
+ mappings responses: repeat (String, Result<(each T, HTTPURLResponse), NetworkError>),
+ encoder: JSONEncoder = JSONEncoder()
+ ) {
+ self.encoder = encoder
+ var encodedResponses: [Result<(Data, HTTPURLResponse), NetworkError>] = []
+ var paths: [String] = []
+ repeat encodedResponses.append((each responses).1.encode(encoder: encoder))
+ repeat paths.append((each responses).0)
+ self.responses = encodedResponses.enumerated().map({ (paths[$0.offset], $0.element) })
+ }
+
+ /// Creates an instace of `NetworkServiceMock`
+ public init(
+ mappings responses: repeat (String, Result),
+ encoder: JSONEncoder = JSONEncoder()
+ ) {
+ self.encoder = encoder
+ var encodedResponses: [Result<(Data, HTTPURLResponse), NetworkError>] = []
+ var paths: [String] = []
+ repeat encodedResponses.append((each responses).1.encode(encoder: encoder))
+ repeat paths.append((each responses).0)
+ self.responses = encodedResponses.enumerated().map({ (paths[$0.offset], $0.element) })
+ }
+
+ /**
+ Fetches a resource asynchronously from remote location. Execution of the requests starts immediately.
+ Execution happens on no specific queue. It dependes on the network access which queue is used.
+ Once execution is finished either the completion block or the error block gets called.
+ You decide on which queue these blocks get executed.
+
+ **Example**:
+ ```swift
+ let networkService: NetworkService = //
+ let resource: Resource = //
+
+ networkService.request(queue: .main, resource: resource, onCompletionWithResponse: { htmlText, response in
+ print(htmlText, response)
+ }, onError: { error in
+ // Handle errors
+ })
+ ```
+
+ - parameter queue: The `DispatchQueue` to execute the completion and error block on.
+ - parameter resource: The resource you want to fetch.
+ - parameter onCompletionWithResponse: Callback which gets called when fetching and transforming into model succeeds.
+ - parameter onError: Callback which gets called when fetching or transforming fails.
+
+ */
+ @MainActor
+ public func requestResultWithResponse(for resource: Resource) async -> Result<(Success, HTTPURLResponse), NetworkError> {
+ lastRequests.append(resource.request)
+ if !responses.isEmpty {
+ let index = responses.firstIndex(where: {
+ return $0.0 == "*" || $0.0 == resource.request.url?.path
+ })
+ guard let index else {
+ return .failure(.serverError(response: nil, data: nil))
+ }
+ let scheduled = responses.remove(at: index).1
+ switch scheduled {
+ case .success((let data, let httpURLResponse)):
+ do {
+ let result = try resource.parse(data)
+ return .success((result, httpURLResponse))
+ } catch {
+ return .failure(.serializationError(error: error, response: httpURLResponse, data: data))
+ }
+ case .failure(let error):
+ return .failure(error)
+ }
+ } else {
+ return .failure(.serverError(response: nil, data: nil))
+ }
+ }
+
+ public func schedule(result: Result<(T, HTTPURLResponse), NetworkError>) {
+ let scheduled: Result<(Data, HTTPURLResponse), NetworkError>
+ switch result {
+ case .failure(let error):
+ scheduled = .failure(error)
+ case .success((let object, let httpUrlResponse)):
+ guard let data = try? encoder.encode(object) else {
+ fatalError("Not able to encode object")
+ }
+ scheduled = .success((data, httpUrlResponse))
+ }
+ responses.append(("*", scheduled))
+ }
+
+ public func schedule(success: Void) {
+ schedule(result: .success(("", HTTPURLResponse())))
+ }
+
+ public func schedule(success: (Void, HTTPURLResponse)) {
+ schedule(result: .success(("", success.1)))
+ }
+
+ public func schedule(success: T) {
+ schedule(result: .success((success, HTTPURLResponse())))
+ }
+
+ public func schedule(success: (T, HTTPURLResponse)) {
+ schedule(result: .success(success))
+ }
+
+ public func schedule(failure: NetworkError) {
+ responses.append(("*", .failure(failure)))
+ }
+}
+
+fileprivate extension Result {
+
+ func encode(
+ encoder: JSONEncoder
+ ) -> Result<(Data, HTTPURLResponse), NetworkError> where Success == (T, HTTPURLResponse), Failure == NetworkError {
+ return self.map({ ((try? encoder.encode($0.0)) ?? Data(), $0.1) })
+ }
+
+}
+
+fileprivate extension Result where Success: Encodable, Failure == NetworkError {
+
+ func encode(
+ encoder: JSONEncoder
+ ) -> Result<(Data, HTTPURLResponse), NetworkError> {
+ let defaultResponse: HTTPURLResponse! = HTTPURLResponse(
+ url: URL(staticString: "bahn.de"),
+ statusCode: 200,
+ httpVersion: "HTTP/1.1",
+ headerFields: nil
+ )
+ return self.map({ ((try? encoder.encode($0)) ?? Data(), defaultResponse) })
+ }
+
+}
+
+
+
diff --git a/Source/RetryNetworkService.swift b/Source/NetworkServices/RetryNetworkService.swift
similarity index 52%
rename from Source/RetryNetworkService.swift
rename to Source/NetworkServices/RetryNetworkService.swift
index f8fb301..7866dc7 100644
--- a/Source/RetryNetworkService.swift
+++ b/Source/NetworkServices/RetryNetworkService.swift
@@ -31,12 +31,12 @@ import Dispatch
- seealso: `NetworkService`
*/
public final class RetryNetworkService: NetworkService {
+
private let networkService: NetworkService
private let numberOfRetries: Int
private let idleTimeInterval: TimeInterval
- private let dispatchRetry: (_ deadline: DispatchTime, _ execute: @escaping () -> Void ) -> Void
- private let shouldRetry: (NetworkError) -> Bool
-
+ private let shouldRetry: @Sendable (NetworkError) -> Bool
+
/// Creates an instance of `RetryNetworkService`
///
/// - Parameters:
@@ -44,17 +44,16 @@ public final class RetryNetworkService: NetworkService {
/// - numberOfRetries: the number of retrys before final error
/// - idleTimeInterval: time between error and retry
/// - shouldRetry: closure which evaluated if error should be retry
- /// - dispatchRetry: closure where to dispatch the waiting
- public init(networkService: NetworkService, numberOfRetries: Int,
- idleTimeInterval: TimeInterval, shouldRetry: @escaping (NetworkError) -> Bool,
- dispatchRetry: @escaping (_ deadline: DispatchTime, _ execute: @escaping () -> Void ) -> Void = { deadline, execute in
- DispatchQueue.global(qos: .utility).asyncAfter(deadline: deadline, execute: execute)
- }) {
+ public init(
+ networkService: NetworkService,
+ numberOfRetries: Int,
+ idleTimeInterval: TimeInterval,
+ shouldRetry: @Sendable @escaping (NetworkError) -> Bool
+ ) {
self.networkService = networkService
self.numberOfRetries = numberOfRetries
self.idleTimeInterval = idleTimeInterval
self.shouldRetry = shouldRetry
- self.dispatchRetry = dispatchRetry
}
/**
@@ -82,59 +81,39 @@ public final class RetryNetworkService: NetworkService {
- returns: a running network task
*/
- @discardableResult
- public func request(queue: DispatchQueue, resource: Resource, onCompletionWithResponse: @escaping (Result, HTTPURLResponse) -> Void,
- onError: @escaping (NetworkError) -> Void) -> NetworkTask {
- let containerTask = ContainerNetworkTask()
- let retryOnError = customOnError(
- containerTask: containerTask,
- numberOfRetriesLeft: numberOfRetries,
- queue: queue,
- resource: resource,
- onCompletionWithResponse: onCompletionWithResponse,
- onError: onError
- )
- containerTask.underlyingTask = networkService.request(
- queue: queue,
- resource: resource,
- onCompletionWithResponse: onCompletionWithResponse,
- onError: retryOnError
- )
-
- return containerTask
+ public func requestResultWithResponse(for resource: Resource) async -> Result<(Success, HTTPURLResponse), NetworkError> {
+ let result = await networkService.requestResultWithResponse(for: resource)
+ switch result {
+ case .success:
+ return result
+ case .failure(let failure):
+ return await requestResultWithResponseOnError(error: failure, numberOfRetriesLeft: numberOfRetries, resource: resource)
+ }
}
- private func customOnError(containerTask: ContainerNetworkTask,
- numberOfRetriesLeft: Int,
- queue: DispatchQueue,
- resource: Resource,
- onCompletionWithResponse: @escaping (Result, HTTPURLResponse) -> Void,
- onError: @escaping (NetworkError) -> Void) -> (NetworkError) -> Void {
- return { error in
- if self.shouldRetry(error), numberOfRetriesLeft > 0 {
- guard !containerTask.isCanceled else {
- return
+ private func requestResultWithResponseOnError(
+ error: NetworkError,
+ numberOfRetriesLeft: Int,
+ resource: Resource
+ ) async -> Result<(Success, HTTPURLResponse), NetworkError> {
+ if self.shouldRetry(error), numberOfRetriesLeft > 0 {
+ let duration = UInt64(idleTimeInterval * 1_000_000_000)
+ try? await Task.sleep(nanoseconds: duration)
+ do {
+ try Task.checkCancellation()
+ let result = await networkService.requestResultWithResponse(for: resource)
+ switch result {
+ case .success:
+ return result
+ case .failure(let failure):
+ return await requestResultWithResponseOnError(error: failure, numberOfRetriesLeft: numberOfRetriesLeft - 1, resource: resource)
}
- self.dispatchRetry(.now() + self.idleTimeInterval, {
- let newOnError = self.customOnError(
- containerTask: containerTask,
- numberOfRetriesLeft: numberOfRetriesLeft - 1,
- queue: queue,
- resource: resource,
- onCompletionWithResponse: onCompletionWithResponse,
- onError: onError
- )
-
- containerTask.underlyingTask = self.networkService.request(
- queue: queue,
- resource: resource,
- onCompletionWithResponse: onCompletionWithResponse,
- onError: newOnError
- )
- })
- } else {
- onError(error)
+ } catch {
+ return .failure(.cancelled)
}
+
+ } else {
+ return .failure(error)
}
}
diff --git a/Source/NetworkTask.swift b/Source/NetworkTask.swift
deleted file mode 100644
index ef12d7b..0000000
--- a/Source/NetworkTask.swift
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-// Copyright (C) 2017 DB Systel GmbH.
-// DB Systel GmbH; Jürgen-Ponto-Platz 1; D-60329 Frankfurt am Main; Germany; http://www.dbsystel.de/
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the "Software"),
-// to deal in the Software without restriction, including without limitation
-// the rights to use, copy, modify, merge, publish, distribute, sublicense,
-// and/or sell copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-//
-
-import Foundation
-
-/**
- `NetworkTaskRepresenting` is a task which runs async to fetch data.
- */
-public protocol NetworkTask: AnyObject {
- /**
- Cancels a task.
- */
- func cancel()
-
- /**
- Resumes a task.
- */
- func resume()
-
- /**
- Suspends a task.
- */
- func suspend()
-}
diff --git a/Source/NetworkTaskMock.swift b/Source/NetworkTaskMock.swift
deleted file mode 100644
index 39afc7a..0000000
--- a/Source/NetworkTaskMock.swift
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-// Copyright (C) 2017 DB Systel GmbH.
-// DB Systel GmbH; Jürgen-Ponto-Platz 1; D-60329 Frankfurt am Main; Germany; http://www.dbsystel.de/
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the "Software"),
-// to deal in the Software without restriction, including without limitation
-// the rights to use, copy, modify, merge, publish, distribute, sublicense,
-// and/or sell copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-//
-
-import Foundation
-
-/// Mock implementation for `NetworkTask`.
-public class NetworkTaskMock: NetworkTask {
-
- /// Mock state of the network task
- public enum State {
- case canceled, resumed, suspended
- }
-
- /// Creates an `NetworkTaskMock` instance
- public init() {}
-
- /// State of the network taks. Can be used to assert.
- public private(set) var state: State?
-
- /// Cancel the request. Sets state to cancled.
- public func cancel() {
- state = .canceled
- }
-
- /// Resumes the request. Sets state to resumed.
- public func resume() {
- state = .resumed
- }
-
- /// Suspends the request. Sets state to suspended.
- public func suspend() {
- state = .suspended
- }
-}
diff --git a/Source/Resource+Decodable.swift b/Source/Resource+Decodable.swift
index ed738e8..731dfbb 100644
--- a/Source/Resource+Decodable.swift
+++ b/Source/Resource+Decodable.swift
@@ -31,12 +31,17 @@ extension Resource where Model: Decodable {
/// - Parameters:
/// - request: The request to get the remote data payload
/// - decoder: a decoder which can decode the payload into the model type
- public init(request: URLRequest, decoder: JSONDecoder) {
- self.init(request: request, parse: { try decoder.decode(Model.self, from: $0) })
+ /// - mapError: a closure which maps to Error
+ public init(request: URLRequest, decoder: JSONDecoder, mapError: @escaping @Sendable (_ networkError: NetworkError) -> E) {
+ self.init(request: request, parse: {
+ try decoder.decode(Model.self, from: $0)
+ },
+ mapError: mapError
+ )
}
}
-extension ResourceWithError where Model: Decodable {
+extension Resource where Model: Decodable, E: NetworkErrorConvertible {
/// Creates an instace of Resource where the result type is `Decodable` and
/// can be decoded with the given decoder
@@ -45,7 +50,13 @@ extension ResourceWithError where Model: Decodable {
/// - request: The request to get the remote data payload
/// - decoder: a decoder which can decode the payload into the model type
/// - mapError: a closure which maps to Error
- public init(request: URLRequest, decoder: JSONDecoder, mapError: @escaping (_ networkError: NetworkError) -> E) {
- self.init(request: request, parse: { try decoder.decode(Model.self, from: $0) }, mapError: mapError)
+ public init(request: URLRequest, decoder: JSONDecoder) {
+ self.init(request: request, parse: {
+ try decoder.decode(Model.self, from: $0)
+ },
+ mapError: {
+ E(networkError: $0)
+ }
+ )
}
}
diff --git a/Source/Resource+Inspect.swift b/Source/Resource+Inspect.swift
index 335b484..5ad717c 100644
--- a/Source/Resource+Inspect.swift
+++ b/Source/Resource+Inspect.swift
@@ -27,7 +27,7 @@ extension Resource {
This lets one inspect the data payload before data gets parsed.
```swift
- let resource: Resource = //
+ let resource: Resource = //
resource.inspectData { data in
print(String(bytes: data, encoding: .utf8))
}
@@ -36,11 +36,12 @@ extension Resource {
- parameter inspector: closure which gets passed the data
- returns: a new resource which gets instepcted before parsing
*/
- public func inspectData(_ inspector: @escaping (Data) -> Void) -> Resource {
- return Resource(request: request, parse: { data in
+ public func inspectData(_ inspector: @escaping @Sendable (Data) -> Void) -> Resource {
+ let parse: @Sendable (Data) throws -> Model = { data in
inspector(data)
return try self.parse(data)
- })
+ }
+ return Resource(request: request, parse: parse, mapError: mapError)
}
}
diff --git a/Source/Resource+Map.swift b/Source/Resource+Map.swift
index 5ef7ae3..aefb9a8 100644
--- a/Source/Resource+Map.swift
+++ b/Source/Resource+Map.swift
@@ -27,23 +27,8 @@ extension Resource {
///
/// - Parameter transform: transforms the original result of the resource
/// - Returns: the transformed resource
- public func map(transform: @escaping (Model) throws -> T) -> Resource {
- return Resource(request: request, parse: { data in
- return try transform(try self.parse(data))
- })
- }
-}
-
-extension ResourceWithError {
-
- /// Maps a resource result to a different resource. This is useful when you have result of R which contains T and your API request a resource of T,
- ///
- /// Error parsing is not changed
- ///
- /// - Parameter transform: transforms the original result of the resource
- /// - Returns: the transformed resource
- public func map(transform: @escaping (Model) throws -> T) -> ResourceWithError {
- return ResourceWithError(
+ public func map(transform: @escaping @Sendable (Model) throws -> T) -> Resource {
+ return Resource(
request: request,
parse: { data in
return try transform(try self.parse(data))
diff --git a/Source/Resource+Void.swift b/Source/Resource+Void.swift
index 9c961c0..06e3ccd 100644
--- a/Source/Resource+Void.swift
+++ b/Source/Resource+Void.swift
@@ -13,19 +13,20 @@ public extension Resource where Model == Void {
///
/// - Parameters:
/// - request: The request to get the remote data payload
- init(request: URLRequest) {
- self.init(request: request, parse: { _ in })
+ /// - mapError: a closure which maps to Error
+ init(request: URLRequest, mapError: @escaping @Sendable (_ networkError: NetworkError) -> E) {
+ self.init(request: request, parse: { _ in }, mapError: mapError)
}
}
-extension ResourceWithError where Model == Void {
+public extension Resource where Model == Void, E: NetworkErrorConvertible {
/// Creates an instace of Resource where the result type is `Void`
///
/// - Parameters:
/// - request: The request to get the remote data payload
- /// - mapError: a closure which maps to Error
- public init(request: URLRequest, mapError: @escaping (_ networkError: NetworkError) -> E) {
- self.init(request: request, parse: { _ in }, mapError: mapError)
+ init(request: URLRequest) {
+ self.init(request: request, parse: { _ in }, mapError: { E(networkError: $0) })
}
+
}
diff --git a/Source/Resource.swift b/Source/Resource.swift
index 9f8b9e5..c0074cb 100644
--- a/Source/Resource.swift
+++ b/Source/Resource.swift
@@ -30,25 +30,28 @@ import Foundation
**Example**:
```swift
let request: URLRequest = //
- let resource: Resource = Resource(request: request, parse: { data in
+ let resource: Resource = Resource(request: request, parse: { data in
String(data: data, encoding: .utf8)
})
```
*/
-public struct Resource {
+public struct Resource: Sendable {
/// The request to fetch the resource remote payload
public let request: URLRequest
-
+
/// Parses data into given model.
- public let parse: (_ data: Data) throws -> Model
-
- /// Creates a type safe resource, which can be used to fetch it with `NetworkService`
+ public let parse: @Sendable (_ data: Data) throws -> Model
+ public let mapError: @Sendable (_ networkError: NetworkError) -> E
+
+ /// Creates a type safe resource, which can be used to fetch it with NetworkService
///
/// - Parameters:
- /// - request: The request to get the remote data payload
- /// - parse: Parses data fetched with the request into given Model
- public init(request: URLRequest, parse: @escaping (Data) throws -> Model) {
+ /// - request: The request to get the remote data payload
+ /// - parse: Parses data fetched with the request into given Model
+
+ public init(request: URLRequest, parse: @escaping @Sendable (Data) throws -> Model, mapError: @escaping @Sendable (_ networkError: NetworkError) -> E) {
self.request = request
self.parse = parse
+ self.mapError = mapError
}
}
diff --git a/Source/ResourceWithError+NetworkErrorConvertible.swift b/Source/ResourceWithError+NetworkErrorConvertible.swift
new file mode 100644
index 0000000..920f53f
--- /dev/null
+++ b/Source/ResourceWithError+NetworkErrorConvertible.swift
@@ -0,0 +1,18 @@
+//
+// File.swift
+//
+//
+// Created by Lukas Schmidt on 13.10.23.
+//
+
+import Foundation
+
+public extension Resource where E: NetworkErrorConvertible {
+
+ init(request: URLRequest, parse: @escaping @Sendable (Data) throws -> Model) {
+ self.request = request
+ self.parse = parse
+ self.mapError = { E(networkError: $0) }
+ }
+
+}
diff --git a/Source/ResourceWithError.swift b/Source/ResourceWithError.swift
deleted file mode 100644
index 9cfca39..0000000
--- a/Source/ResourceWithError.swift
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-// Copyright (C) 2021 DB Systel GmbH.
-// DB Systel GmbH; Jürgen-Ponto-Platz 1; D-60329 Frankfurt am Main; Germany; http://www.dbsystel.de/
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the "Software"),
-// to deal in the Software without restriction, including without limitation
-// the rights to use, copy, modify, merge, publish, distribute, sublicense,
-// and/or sell copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-//
-
-import Foundation
-
-/**
- `ResourceWithError` describes a remote resource of generic type and generic error.
- The type can be fetched via HTTP(S) and parsed into the coresponding model object.
-
- **Example**:
- ```swift
- let request: URLRequest = //
- let resource: ResourceWithError = Resource(request: request, parse: { data in
- String(data: data, encoding: .utf8)
- }, mapError: { networkError in
- return CustomError(networkError)
- })
- ```
- */
-public struct ResourceWithError {
- /// The request to fetch the resource remote payload
- public let request: URLRequest
-
- /// Parses data into given model.
- public let parse: (_ data: Data) throws -> Model
- public let mapError: (_ networkError: NetworkError) -> E
-
- /// Creates a type safe resource, which can be used to fetch it with NetworkService
- ///
- /// - Parameters:
- /// - request: The request to get the remote data payload
- /// - parse: Parses data fetched with the request into given Model
-
- public init(request: URLRequest, parse: @escaping (Data) throws -> Model, mapError: @escaping (_ networkError: NetworkError) -> E) {
- self.request = request
- self.parse = parse
- self.mapError = mapError
- }
-}
diff --git a/Source/URLSession+NetworkAccess.swift b/Source/URLSession+NetworkAccess.swift
index 7a52ad8..c7fc912 100644
--- a/Source/URLSession+NetworkAccess.swift
+++ b/Source/URLSession+NetworkAccess.swift
@@ -25,21 +25,7 @@ import Foundation
/// Adds conformens to `NetworkAccess`. `URLSession` can be used as a network access.
extension URLSession: NetworkAccess {
- /**
- Fetches a request asynchrony from remote location.
-
- - parameter request: The request you want to fetch.
- - parameter callback: Callback which gets called when the request finishes.
-
- - returns: the running network task
- */
- public func load(request: URLRequest, callback: @escaping (Data?, HTTPURLResponse?, Error?) -> Void) -> NetworkTask {
- let task = dataTask(with: request, completionHandler: { data, response, error in
- callback(data, response as? HTTPURLResponse, error)
- })
-
- task.resume()
-
- return task
+ public func load(request: URLRequest) async throws -> (Data, URLResponse) {
+ return try await data(for: request)
}
}
diff --git a/Source/URLSessionDataTask+NetworkTask.swift b/Source/URLSessionDataTask+NetworkTask.swift
deleted file mode 100644
index 496b3c2..0000000
--- a/Source/URLSessionDataTask+NetworkTask.swift
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-// Copyright (C) 2017 DB Systel GmbH.
-// DB Systel GmbH; Jürgen-Ponto-Platz 1; D-60329 Frankfurt am Main; Germany; http://www.dbsystel.de/
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the "Software"),
-// to deal in the Software without restriction, including without limitation
-// the rights to use, copy, modify, merge, publish, distribute, sublicense,
-// and/or sell copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-//
-
-import Foundation
-
-/// URLSessionDataTask conforms to NetworkTask
-extension URLSessionDataTask: NetworkTask { }
diff --git a/Tests/ContainerNetworkTaskTest.swift b/Tests/ContainerNetworkTaskTest.swift
deleted file mode 100644
index e9e6cf0..0000000
--- a/Tests/ContainerNetworkTaskTest.swift
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// Created by Christian Himmelsbach on 23.07.18.
-//
-
-import XCTest
-import DBNetworkStack
-
-class ContainerNetworkTaskTest: XCTestCase {
-
- func testGIVEN_AuthenticatorNetworkTask_WHEN_ResumeTask_THEN_UnderlyingTaskShouldBeResumed() {
- //Given
- let taskMock = NetworkTaskMock()
- let task = ContainerNetworkTask()
- task.underlyingTask = taskMock
-
- //When
- task.resume()
-
- //Then
- XCTAssert(taskMock.state == .resumed)
- }
-
- func testGIVEN_AuthenticatorNetworkTask_WHEN_SuspendTask_THEN_UnderlyingTaskShouldBeSuspened() {
- //Given
- let taskMock = NetworkTaskMock()
- let task = ContainerNetworkTask()
- task.underlyingTask = taskMock
-
- //When
- task.suspend()
-
- //Then
- XCTAssert(taskMock.state == .suspended)
- }
-
- func testGIVEN_AuthenticatorNetworkTask_WHEN_CancelTask_THEN_UnderlyingTaskShouldBeCanceled() {
- // Given
- let taskMock = NetworkTaskMock()
- let task = ContainerNetworkTask()
- task.underlyingTask = taskMock
-
- // When
- task.cancel()
-
- // Then
- XCTAssert(taskMock.state == .canceled)
- }
-
-}
diff --git a/Tests/DecodableResoureTest.swift b/Tests/DecodableResoureTest.swift
index e61538a..2282fbb 100644
--- a/Tests/DecodableResoureTest.swift
+++ b/Tests/DecodableResoureTest.swift
@@ -26,15 +26,15 @@ import XCTest
@testable import DBNetworkStack
class DecodableResoureTest: XCTestCase {
- var resource: Resource {
+ var resource: Resource {
let request = URLRequest(path: "/train", baseURL: .defaultMock)
- return Resource(request: request, decoder: JSONDecoder())
+ return Resource(request: request, decoder: JSONDecoder())
}
func testResource_withValidData() {
//When
let fetchedTrain = try? resource.parse(Train.validJSONData)
-
+
//Then
XCTAssertEqual(fetchedTrain?.name, "ICE")
}
@@ -43,7 +43,7 @@ class DecodableResoureTest: XCTestCase {
//When
let nameResource = resource.map { $0.name }
let fetchedTrainName = try? nameResource.parse(Train.validJSONData)
-
+
//Then
XCTAssertEqual(fetchedTrainName, "ICE")
}
diff --git a/Tests/ModifyRequestNetworkService.swift b/Tests/ModifyRequestNetworkService.swift
index 1ef9297..9133082 100644
--- a/Tests/ModifyRequestNetworkService.swift
+++ b/Tests/ModifyRequestNetworkService.swift
@@ -27,27 +27,23 @@ import Foundation
class ModifyRequestNetworkServiceTest: XCTestCase {
- var networkServiceMock: NetworkServiceMock!
-
- override func setUp() {
- super.setUp()
- networkServiceMock = NetworkServiceMock()
- }
-
- func testRequest_withModifedRequest() {
+ func testRequest_withModifedRequest() async throws {
//Given
- let modification: [(URLRequest) -> URLRequest] = [ { request in
- return request.appending(queryParameters: ["key": "1"])
- } ]
- let networkService: NetworkService = ModifyRequestNetworkService(networkService: networkServiceMock, requestModifications: modification)
+ let networkServiceMock = NetworkServiceMock()
+ let modification: [@Sendable (URLRequest) -> URLRequest] = [
+ { $0.appending(queryParameters: ["key": "1"]) }
+ ]
+ let networkService = ModifyRequestNetworkService(networkService: networkServiceMock, requestModifications: modification)
let request = URLRequest(path: "/trains", baseURL: .defaultMock)
- let resource = Resource(request: request, parse: { _ in return 1 })
-
+ let resource = Resource(request: request, parse: { _ in return 1 })
+
//When
- networkService.request(resource, onCompletion: { _ in }, onError: { _ in })
-
+ await networkService.requestResult(for: resource)
+
//Then
- XCTAssert(networkServiceMock.lastRequest?.url?.absoluteString.contains("key=1") ?? false)
+ let lastRequest = networkServiceMock.lastRequest
+ let lastRequestURL = try XCTUnwrap(lastRequest?.url)
+ XCTAssert(lastRequestURL.absoluteString.contains("key=1"))
}
func testAddHTTPHeaderToRequest() {
@@ -62,7 +58,7 @@ class ModifyRequestNetworkServiceTest: XCTestCase {
XCTAssertEqual(newRequest.allHTTPHeaderFields?["header"], "head")
}
- func testAddDuplicatedQueryToRequest() {
+ func testAddDuplicatedQueryToRequest() throws {
//Given
let url = URL(staticString: "bahn.de?test=test&bool=true")
let request = URLRequest(url: url)
@@ -73,14 +69,15 @@ class ModifyRequestNetworkServiceTest: XCTestCase {
let newRequest = request.appending(queryParameters: parameters)
//Then
- let newURL: URL! = newRequest.url
- let query = URLComponents(url: newURL, resolvingAgainstBaseURL: true)?.queryItems
- XCTAssertEqual(query?.count, 2)
- XCTAssert(query?.contains(where: { $0.name == "test" && $0.value == "test2" }) ?? false)
- XCTAssert(query?.contains(where: { $0.name == "bool" && $0.value == "true" }) ?? false)
+ let newURL = try XCTUnwrap(newRequest.url)
+ let urlComponents = URLComponents(url: newURL, resolvingAgainstBaseURL: true)
+ let query = try XCTUnwrap(urlComponents?.queryItems)
+ XCTAssertEqual(query.count, 2)
+ XCTAssert(query.contains(where: { $0.name == "test" && $0.value == "test2" }))
+ XCTAssert(query.contains(where: { $0.name == "bool" && $0.value == "true" }))
}
- func testReplaceAllQueryItemsFromRequest() {
+ func testReplaceAllQueryItemsFromRequest() throws {
//Given
let url = URL(staticString: "bahn.de?test=test&bool=true")
let request = URLRequest(url: url)
@@ -91,9 +88,10 @@ class ModifyRequestNetworkServiceTest: XCTestCase {
let newRequest = request.replacingAllQueryItems(with: parameters)
//Then
- let newURL: URL! = newRequest.url
- let query = URLComponents(url: newURL, resolvingAgainstBaseURL: true)?.queryItems
- XCTAssertEqual(query?.count, 1)
- XCTAssert(query?.contains(where: { $0.name == "test5" && $0.value == "test2" }) ?? false)
+ let newURL = try XCTUnwrap(newRequest.url)
+ let urlComponents = URLComponents(url: newURL, resolvingAgainstBaseURL: true)
+ let query = try XCTUnwrap(urlComponents?.queryItems)
+ XCTAssertEqual(query.count, 1)
+ XCTAssert(query.contains(where: { $0.name == "test5" && $0.value == "test2" }))
}
}
diff --git a/Tests/NetworkAccessMock.swift b/Tests/NetworkAccessMock.swift
index 1c3d0de..d00a923 100644
--- a/Tests/NetworkAccessMock.swift
+++ b/Tests/NetworkAccessMock.swift
@@ -24,24 +24,22 @@
import Foundation
import DBNetworkStack
-class NetworkAccessMock: NetworkAccess {
- fileprivate(set) var data: Data?
- fileprivate(set) var response: HTTPURLResponse?
- fileprivate(set) var error: NSError?
-
+actor NetworkAccessMock: NetworkAccess {
+
+ init(result: Result<(Data, URLResponse), NSError>) {
+ self.result = result
+ }
+
+ private let result: Result<(Data, URLResponse), NSError>
fileprivate(set) var request: URLRequest?
- func load(request: URLRequest, callback: @escaping (Data?, HTTPURLResponse?, Error?) -> Void) -> NetworkTask {
+ func load(request: URLRequest) async throws -> (Data, URLResponse) {
self.request = request
-
- callback(data, response, error)
-
- return NetworkTaskMock()
- }
-
- func changeMock(data: Data?, response: HTTPURLResponse?, error: NSError?) {
- self.data = data
- self.response = response
- self.error = error
+ switch result {
+ case .success(let success):
+ return success
+ case .failure(let failure):
+ throw failure
+ }
}
}
diff --git a/Tests/NetworkErrorTest.swift b/Tests/NetworkErrorTest.swift
index 849c978..5dc8a75 100644
--- a/Tests/NetworkErrorTest.swift
+++ b/Tests/NetworkErrorTest.swift
@@ -26,17 +26,14 @@ import XCTest
@testable import DBNetworkStack
class NetworkErrorTest: XCTestCase {
-
- func urlResponseWith(statusCode: Int) -> HTTPURLResponse? {
- return HTTPURLResponse(url: .defaultMock, statusCode: statusCode, httpVersion: nil, headerFields: nil)
- }
+
private let testData: Data! = "test_string".data(using: .utf8)
- func testInit_WithHTTPStatusCode400() {
+ func testInit_WithHTTPStatusCode400() throws {
//Given
- let expectedResponse = urlResponseWith(statusCode: 400)
-
+ let expectedResponse = try XCTUnwrap(HTTPURLResponse(url: .defaultMock, statusCode: 400, httpVersion: nil, headerFields: nil))
+
//When
let error = NetworkError(response: expectedResponse, data: testData)
@@ -50,10 +47,10 @@ class NetworkErrorTest: XCTestCase {
}
}
- func testInit_WithHTTPStatusCode401() {
+ func testInit_WithHTTPStatusCode401() throws {
//Given
- let expectedResponse = urlResponseWith(statusCode: 401)
-
+ let expectedResponse = try XCTUnwrap(HTTPURLResponse(url: .defaultMock, statusCode: 401, httpVersion: nil, headerFields: nil))
+
//When
let error = NetworkError(response: expectedResponse, data: testData)
@@ -67,21 +64,21 @@ class NetworkErrorTest: XCTestCase {
}
}
- func testInit_WithHTTPStatusCode200() {
+ func testInit_WithHTTPStatusCode200() throws {
//Given
- let response = urlResponseWith(statusCode: 200)
-
+ let response = try XCTUnwrap(HTTPURLResponse(url: .defaultMock, statusCode: 200, httpVersion: nil, headerFields: nil))
+
//When
- let error = NetworkError(response: response, data: nil)
-
+ let error = NetworkError(response: response, data: testData)
+
//Then
XCTAssertNil(error)
}
- func testInit_WithHTTPStatusCode511() {
+ func testInit_WithHTTPStatusCode511() throws {
//Given
- let expectedResponse = urlResponseWith(statusCode: 511)
-
+ let expectedResponse = try XCTUnwrap(HTTPURLResponse(url: .defaultMock, statusCode: 511, httpVersion: nil, headerFields: nil))
+
//When
let error = NetworkError(response: expectedResponse, data: testData)
@@ -95,12 +92,12 @@ class NetworkErrorTest: XCTestCase {
}
}
- func testInit_WithInvalidHTTPStatusCode900() {
+ func testInit_WithInvalidHTTPStatusCode900() throws {
//Given
- let response = urlResponseWith(statusCode: 900)
-
+ let response = try XCTUnwrap(HTTPURLResponse(url: .defaultMock, statusCode: 900, httpVersion: nil, headerFields: nil))
+
//When
- let error = NetworkError(response: response, data: nil)
+ let error = NetworkError(response: response, data: testData)
//Then
XCTAssertNil(error)
@@ -138,8 +135,8 @@ class NetworkErrorTest: XCTestCase {
let debugDescription = error.debugDescription
//Then
- XCTAssert(debugDescription.hasPrefix("Authorization error: Int in
- throw NetworkError.unknownError })
- let data: Data! = "Data".data(using: .utf8)
-
- // When
- do {
- _ = try processor.process(response: .defaultMock, resource: resource, data: data, error: nil)
- } catch let error as NetworkError {
- // Then
- switch error {
- case .serializationError(let error, let recievedData): // Excpected
- switch error as? NetworkError {
- case .unknownError?:
- XCTAssert(true)
- default:
- XCTFail("Expects unknownError")
- }
-
- XCTAssertEqual(recievedData, data)
- default:
- XCTFail("Expected cancelled error (got \(error)")
- }
- } catch let error {
- XCTFail("Expected NetworkError (got \(type(of: error)))")
- }
- }
-
- func testParseSucessFullWithNilResponse() {
- //Given
- let resource = Resource(request: URLRequest.defaultMock, parse: { _ in return 0 })
-
- //When
- do {
- _ = try processor.process(response: nil, resource: resource, data: Data(), error: nil)
- } catch let error as NetworkError {
- // Then
- switch error {
- case .unknownError: // Excpected
- break
- default:
- XCTFail("Expected cancelled error (got \(error)")
- }
- } catch let error {
- XCTFail("Expected NetworkError (got \(type(of: error)))")
- }
-
- }
-}
diff --git a/Tests/NetworkServiceMockTest.swift b/Tests/NetworkServiceMockTest.swift
index 0b71b2e..59b50a0 100644
--- a/Tests/NetworkServiceMockTest.swift
+++ b/Tests/NetworkServiceMockTest.swift
@@ -26,303 +26,120 @@ import DBNetworkStack
class NetworkServiceMockTest: XCTestCase {
- var networkServiceMock: NetworkServiceMock!
- let resource = Resource(request: URLRequest(path: "/trains", baseURL: .defaultMock), parse: { _ in return 1 })
-
- override func setUp() {
- networkServiceMock = NetworkServiceMock()
- }
-
- func testRequestCount() {
- //When
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { _ in })
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { _ in })
-
- //Then
- XCTAssertEqual(networkServiceMock.requestCount, 2)
- }
-
- func testLastRequests() {
- //When
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { _ in })
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { _ in })
-
- //Then
- XCTAssertEqual(networkServiceMock.lastRequests, [resource.request, resource.request])
- }
-
- func testReturnSuccessWithData() throws {
- //Given
- var capturedResult: Int?
- var executionCount: Int = 0
-
- //When
- networkServiceMock.request(resource, onCompletion: { result in
- capturedResult = result
- executionCount += 1
- }, onError: { _ in })
- try networkServiceMock.returnSuccess(with: 1)
-
- //Then
- XCTAssertEqual(capturedResult, 1)
- XCTAssertEqual(executionCount, 1)
- }
+ let resource: Resource = Resource(request: URLRequest(path: "train", baseURL: .defaultMock), decoder: JSONDecoder())
- func testCorrectOrderOfReturnSuccessWithDataForMultipleRequests() throws {
+ func testRequestCount() async throws {
//Given
- var called1First = false
- var called2First = false
+ let networkServiceMock = NetworkServiceMock(
+ .success(Train(name: "1")),
+ .success(Train(name: "2"))
+ )
//When
- networkServiceMock.request(resource, onCompletion: { _ in
- if !called2First {
- called1First = true
- }
- }, onError: { _ in })
- networkServiceMock.request(resource, onCompletion: { _ in
- if !called1First {
- called2First = true
- }
- }, onError: { _ in })
- try networkServiceMock.returnSuccess(with: 0)
- try networkServiceMock.returnSuccess(with: 0)
+ try await networkServiceMock.request(resource)
+ try await networkServiceMock.request(resource)
//Then
- XCTAssertTrue(called1First)
- XCTAssertFalse(called2First)
+ let requestCount = networkServiceMock.requestCount
+ XCTAssertEqual(requestCount, 2)
}
-
- func testRequestSuccessWithDataChaining() throws {
+
+ func testLastRequests() async throws {
//Given
- var executionCount1: Int = 0
- var executionCount2: Int = 0
+ let networkServiceMock = NetworkServiceMock(
+ .success(Train(name: "1")),
+ .success(Train(name: "2"))
+ )
//When
- networkServiceMock.request(resource, onCompletion: { _ in
- executionCount1 += 1
- self.networkServiceMock.request(self.resource, onCompletion: { _ in
- executionCount2 += 1
- }, onError: { _ in })
- }, onError: { _ in })
- try networkServiceMock.returnSuccess(with: 0)
- try networkServiceMock.returnSuccess(with: 0)
+ try await networkServiceMock.request(resource)
+ try await networkServiceMock.request(resource)
//Then
- XCTAssertEqual(executionCount1, 1)
- XCTAssertEqual(executionCount2, 1)
+ let lastRequests = networkServiceMock.lastRequests
+ XCTAssertEqual(lastRequests, [resource.request, resource.request])
}
-
- func testReturnSuccessWithDataForAllRequests() throws {
+
+ func testReturnSuccessWithData() async throws {
//Given
- var executionCount1: Int = 0
- var executionCount2: Int = 0
+ let networkServiceMock = NetworkServiceMock(
+ .success(Train(name: "1"))
+ )
//When
- networkServiceMock.request(resource, onCompletion: { _ in
- executionCount1 += 1
- }, onError: { _ in })
- networkServiceMock.request(resource, onCompletion: { _ in
- executionCount2 += 1
- }, onError: { _ in })
- try networkServiceMock.returnSuccess(with: 0)
- try networkServiceMock.returnSuccess(with: 0)
+ let result = try await networkServiceMock.request(resource)
- //Then
- XCTAssertEqual(executionCount1, 1)
- XCTAssertEqual(executionCount2, 1)
- }
-
- func testReturnSuccessWithSerializedData() throws {
- //Given
- var capturedResult: Int?
- var executionCount: Int = 0
-
- //When
- networkServiceMock.request(resource, onCompletion: { result in
- capturedResult = result
- executionCount += 1
- }, onError: { _ in })
- try networkServiceMock.returnSuccess(with: 10)
//Then
- XCTAssertEqual(capturedResult, 10)
- XCTAssertEqual(executionCount, 1)
+ XCTAssertEqual(result, Train(name: "1"))
}
-
- func testCorrectOrderOfReturnSuccessWithSerializedDataForMultipleRequests() throws {
- //Given
- var capturedResult1: Int?
- var capturedResult2: Int?
- //When
- networkServiceMock.request(resource, onCompletion: { result in
- capturedResult1 = result
- }, onError: { _ in })
- networkServiceMock.request(resource, onCompletion: { result in
- capturedResult2 = result
- }, onError: { _ in })
- try networkServiceMock.returnSuccess(with: 10)
- try networkServiceMock.returnSuccess(with: 20)
-
- //Then
- XCTAssertEqual(capturedResult1, 10)
- XCTAssertEqual(capturedResult2, 20)
- }
-
- func testRequestSuccessWithSerializedDataChaining() throws {
+ func testCorrectOrderOfReturnSuccessWithDataForMultipleRequests() async throws {
//Given
- var executionCount1: Int = 0
- var executionCount2: Int = 0
-
- //When
- networkServiceMock.request(resource, onCompletion: { _ in
- executionCount1 += 1
- self.networkServiceMock.request(self.resource, onCompletion: { _ in
- executionCount2 += 1
- }, onError: { _ in })
- }, onError: { _ in })
- try networkServiceMock.returnSuccess(with: 10)
- try networkServiceMock.returnSuccess(with: 20)
-
- //Then
- XCTAssertEqual(executionCount1, 1)
- XCTAssertEqual(executionCount2, 1)
- }
-
- func testReturnSuccessWithSerializedDataForAllRequests() throws {
- //Given
- var executionCount1: Int = 0
- var executionCount2: Int = 0
-
+ let networkServiceMock = NetworkServiceMock(
+ .success(Train(name: "1")),
+ .success(Train(name: "2"))
+ )
+
//When
- networkServiceMock.request(resource, onCompletion: { _ in
- executionCount1 += 1
- }, onError: { _ in })
- networkServiceMock.request(resource, onCompletion: { _ in
- executionCount2 += 1
- }, onError: { _ in })
- try networkServiceMock.returnSuccess(with: 10)
- try networkServiceMock.returnSuccess(with: 10)
-
+ let result1 = try await networkServiceMock.request(resource)
+ let result2 = try await networkServiceMock.request(resource)
+
//Then
- XCTAssertEqual(executionCount1, 1)
- XCTAssertEqual(executionCount2, 1)
+ XCTAssertEqual(result1, Train(name: "1"))
+ XCTAssertEqual(result2, Train(name: "2"))
}
- func testReturnError() throws {
+ func testReturnError() async throws {
//Given
- var capturedError: NetworkError?
- var executionCount: Int = 0
-
+ let networkServiceMock = NetworkServiceMock(
+ Result.failure(.unknownError)
+ )
+
//When
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { error in
- capturedError = error
- executionCount += 1
- })
- try networkServiceMock.returnError(with: .unknownError)
-
+ let result = await networkServiceMock.requestResult(for: resource)
+
//Then
- if let error = capturedError, case .unknownError = error {
-
+ if case .failure(.unknownError) = result {
+
} else {
- XCTFail("Wrong error type")
+ XCTFail()
}
- XCTAssertEqual(executionCount, 1)
}
- func testCorrectOrderOfReturnErrorForMultipleRequests() throws {
+ func testCorrectOrderOfReturnErrorForMultipleRequests() async throws {
//Given
- var capturedError1: NetworkError?
- var capturedError2: NetworkError?
+ let networkServiceMock = NetworkServiceMock(
+ Result.failure(.unknownError),
+ Result.failure(.cancelled)
+ )
//When
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { error in
- capturedError1 = error
- })
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { error in
- capturedError2 = error
- })
- try networkServiceMock.returnError(with: .unknownError)
- try networkServiceMock.returnError(with: .cancelled)
+ let result1 = await networkServiceMock.requestResult(for: resource)
+ let result2 = await networkServiceMock.requestResult(for: resource)
//Then
- if case .unknownError? = capturedError1, case .cancelled? = capturedError2 {
-
+ if case .failure(.unknownError) = result1, case .failure(.cancelled) = result2 {
+
} else {
XCTFail("Wrong order of error responses")
}
}
-
- func testRequestErrorChaining() throws {
- //Given
- var executionCount1: Int = 0
- var executionCount2: Int = 0
-
- //When
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { _ in
- executionCount1 += 1
- self.networkServiceMock.request(self.resource, onCompletion: { _ in }, onError: { _ in
- executionCount2 += 1
- })
- })
-
- try networkServiceMock.returnError(with: .unknownError)
- try networkServiceMock.returnError(with: .unknownError)
-
- //Then
- XCTAssertEqual(executionCount1, 1)
- XCTAssertEqual(executionCount2, 1)
- }
-
- func testReturnErrorsForAllRequests() throws {
- //Given
- var executionCount1: Int = 0
- var executionCount2: Int = 0
+ func testReturnSuccessMismatchType() async {
//When
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { _ in
- executionCount1 += 1
- })
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { _ in
- executionCount2 += 1
- })
- try networkServiceMock.returnError(with: .unknownError)
- try networkServiceMock.returnError(with: .unknownError)
-
- //Then
- XCTAssertEqual(executionCount1, 1)
- XCTAssertEqual(executionCount2, 1)
- }
+ let networkServiceMock = NetworkServiceMock()
- func testReturnSuccessMismatchType() {
//When
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { _ in })
-
- //Then
- XCTAssertThrowsError(try networkServiceMock.returnSuccess(with: "Mismatch Type"))
- }
-
- func testReturnSuccessMissingRequest() {
- //Then
- XCTAssertThrowsError(try networkServiceMock.returnSuccess(with: 1))
- }
+ let result1 = await networkServiceMock.requestResult(for: resource)
- func testReturnErrorMissingRequest() {
//Then
- XCTAssertThrowsError(try networkServiceMock.returnError(with: .unknownError))
- }
-
- func testPendingRequestCountEmpty() {
- XCTAssertEqual(networkServiceMock.pendingRequestCount, 0)
- }
-
- func testPendingRequestCountNotEmpty() {
- //When
- networkServiceMock.request(resource, onCompletion: { _ in }, onError: { _ in })
+ if case .failure(.serverError) = result1 {
- //Then
- XCTAssertEqual(networkServiceMock.pendingRequestCount, 1)
+ } else {
+ XCTFail("Wrong order of error responses")
+ }
}
}
diff --git a/Tests/NetworkServiceTest.swift b/Tests/NetworkServiceTest.swift
index 7809b67..667f442 100644
--- a/Tests/NetworkServiceTest.swift
+++ b/Tests/NetworkServiceTest.swift
@@ -26,265 +26,95 @@ import XCTest
@testable import DBNetworkStack
class NetworkServiceTest: XCTestCase {
-
- var networkService: NetworkService!
-
- var networkAccess = NetworkAccessMock()
-
+
let trainName = "ICE"
- var resource: Resource {
+ var resource: Resource {
let request = URLRequest(path: "train", baseURL: .defaultMock)
return Resource(request: request, decoder: JSONDecoder())
}
+
- override func setUp() {
- networkService = BasicNetworkService(networkAccess: networkAccess)
- }
-
- func testRequest_withValidResponse() {
+ func testRequest_withValidResponse() async throws {
//Given
- networkAccess.changeMock(data: Train.validJSONData, response: .defaultMock, error: nil)
- let expection = expectation(description: "loadValidRequest")
-
- //When
- networkService.request(resource, onCompletionWithResponse: { train, response in
- XCTAssertEqual(train.name, self.trainName)
- XCTAssertEqual(response, .defaultMock)
- expection.fulfill()
- }, onError: { _ in
- XCTFail("Should not call error block")
- })
-
- waitForExpectations(timeout: 1, handler: nil)
-
- //Then
- XCTAssertEqual(networkAccess.request?.url?.absoluteString, "https://bahn.de/train")
- }
+ let networkAccess = NetworkAccessMock(result: .success((Train.validJSONData, HTTPURLResponse.defaultMock)))
+ let networkService = BasicNetworkService(networkAccess: networkAccess)
- func testRequest_withNoDataResponse() {
- //Given
- networkAccess.changeMock(data: nil, response: nil, error: nil)
- let expection = expectation(description: "testNoData")
-
//When
- var capturedError: NetworkError?
- networkService.request(resource, onCompletion: { _ in
- XCTFail("Should not call success block")
- }, onError: { error in
- capturedError = error
- expection.fulfill()
- })
-
+ let (train, response) = try await networkService.requestResultWithResponse(for: resource).get()
+
//Then
- waitForExpectations(timeout: 1, handler: nil)
-
- switch capturedError {
- case .serverError(let response, let data)?:
- XCTAssertNil(response)
- XCTAssertNil(data)
- default:
- XCTFail("Expect serverError")
- }
- }
-
- func testRequest_withFailingSerialization() {
- //Given
- networkAccess.changeMock(data: Train.JSONDataWithInvalidKey, response: nil, error: nil)
- let expection = expectation(description: "testRequest_withFailingSerialization")
-
- //When
- networkService.request(resource, onCompletion: { _ in
- XCTFail("Should not call success block")
- }, onError: { (error: NetworkError) in
- if case .serializationError(_, _) = error {
- expection.fulfill()
- } else {
- XCTFail("Expects serializationError")
- }
- })
-
- waitForExpectations(timeout: 1, handler: nil)
- }
-
- func testRequest_withErrorResponse() {
- //Given
- let error = NSError(domain: "", code: 0, userInfo: nil)
- networkAccess.changeMock(data: nil, response: nil, error: error)
- let expection = expectation(description: "testOnError")
-
- //When
- networkService.request(resource, onCompletion: { _ in
- }, onError: { resultError in
- //Then
- switch resultError {
- case .requestError:
- expection.fulfill()
- default:
- XCTFail("Expects requestError")
- }
- })
-
- waitForExpectations(timeout: 1, handler: nil)
+ XCTAssertEqual(train.name, self.trainName)
+ XCTAssertEqual(response, .defaultMock)
+ let request = await networkAccess.request
+ XCTAssertEqual(request?.url?.absoluteString, "https://bahn.de/train")
}
- private lazy var testData: Data! = {
- return "test_string".data(using: .utf8)
- }()
-
- func testRequest_withStatusCode401Response() {
+ func testRequest_withFailingSerialization() async {
//Given
- let expectedResponse = HTTPURLResponse(url: .defaultMock, statusCode: 401, httpVersion: nil, headerFields: nil)
- networkAccess.changeMock(data: testData, response: expectedResponse, error: nil)
- let expection = expectation(description: "testOnError")
-
- //When
- networkService.request(resource, onCompletion: { _ in
- }, onError: { resultError in
- //Then
- switch resultError {
- case .unauthorized(let response, let data):
- XCTAssertEqual(response, expectedResponse)
- XCTAssertEqual(data, self.testData)
- expection.fulfill()
- default:
- XCTFail("Expects unauthorized")
- }
- })
-
- waitForExpectations(timeout: 1, handler: nil)
- }
-
- func testGIVEN_aRequest_WHEN_requestWithResultResponse_THEN_ShouldRespond() {
- // GIVEN
-
- networkAccess.changeMock(data: Train.validJSONData, response: .defaultMock, error: nil)
- let expection = expectation(description: "loadValidRequest")
- var expectedResult: Result?
+ let networkAccess = NetworkAccessMock(result: .success((Train.JSONDataWithInvalidKey, HTTPURLResponse.defaultMock)))
+ let networkService = BasicNetworkService(networkAccess: networkAccess)
//When
- networkService.request(resource, onCompletion: { result in
- expectedResult = result
- expection.fulfill()
- })
-
- waitForExpectations(timeout: 1, handler: nil)
-
+ let result = await networkService.requestResult(for: resource)
+
//Then
- switch expectedResult {
- case .success(let train)?:
- XCTAssertEqual(train.name, self.trainName)
- case .failure?:
- XCTFail("Should be an error")
- case nil:
- XCTFail("Result should not be nil")
+ if case .failure(.serializationError(_, _, let data)) = result {
+ XCTAssertEqual(data, Train.JSONDataWithInvalidKey)
+ } else {
+ XCTFail("Expects serializationError")
}
- XCTAssertEqual(networkAccess.request?.url?.absoluteString, "https://bahn.de/train")
}
- func testGIVEN_aRequest_WHEN_requestWithResultErrorResponse_THEN_ShouldError() {
+ func testRequest_withErrorResponse() async {
//Given
- networkAccess.changeMock(data: nil, response: nil, error: nil)
- var expectedResult: Result?
- let expection = expectation(description: "testNoData")
-
- //When
-
- networkService.request(resource, onCompletion: { result in
- expectedResult = result
- expection.fulfill()
- })
-
- //Then
- waitForExpectations(timeout: 1, handler: nil)
-
- switch expectedResult {
- case .failure(let error)?:
- if case .serverError(let response, let data) = error {
- XCTAssertNil(response)
- XCTAssertNil(data)
- } else {
- XCTFail("Expect serverError")
- }
- default:
- XCTFail("Expect serverError")
- }
- }
-
- func testGIVEN_aRequest_WHEN_requestWithResultAndResponse_THEN_ShouldRespond() {
- // GIVEN
-
- networkAccess.changeMock(data: Train.validJSONData, response: .defaultMock, error: nil)
- let expection = expectation(description: "loadValidRequest")
- var expectedResult: Result<(Train, HTTPURLResponse), NetworkError>?
-
- //When
- networkService.request(resource: resource) { (result) in
- expectedResult = result
- expection.fulfill()
- }
-
- waitForExpectations(timeout: 1, handler: nil)
-
- //Then
- switch expectedResult {
- case .success(let result)?:
- XCTAssertEqual(result.0.name, self.trainName)
- XCTAssertEqual(result.1, .defaultMock)
- case .failure?:
- XCTFail("Should be an error")
- case nil:
- XCTFail("Result should not be nil")
- }
- XCTAssertEqual(networkAccess.request?.url?.absoluteString, "https://bahn.de/train")
- }
-
- @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
- func testGIVEN_aRequest_WHEN_requestWithAsyncResultAndResponse_THEN_ShouldRespond() async throws {
- // GIVEN
- networkAccess.changeMock(data: Train.validJSONData, response: .defaultMock, error: nil)
+ let error = NSError(domain: "", code: 0, userInfo: nil)
+ let networkAccess = NetworkAccessMock(result: .failure(error))
+ let networkService = BasicNetworkService(networkAccess: networkAccess)
//When
- let (result, response) = try await networkService.request(resource)
-
+ let result = await networkService.requestResult(for: resource)
//Then
- XCTAssertEqual(result.name, self.trainName)
- XCTAssertEqual(response, .defaultMock)
- XCTAssertEqual(networkAccess.request?.url?.absoluteString, "https://bahn.de/train")
+ switch result {
+ case .failure(.requestError):
+ return
+ default:
+ XCTFail("Expects requestError")
+ }
}
- @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
- func testGIVEN_aRequest_WHEN_requestWithAsyncResultAndResponse_THEN_ShouldThwo() async {
- // GIVEN
+ func testRequest_withErrorThrow() async {
+ //Given
let error = NSError(domain: "", code: 0, userInfo: nil)
- networkAccess.changeMock(data: nil, response: nil, error: error)
+ let networkAccess = NetworkAccessMock(result: .failure(error))
+ let networkService = BasicNetworkService(networkAccess: networkAccess)
//When
do {
try await networkService.request(resource)
- XCTFail("Schould throw")
- } catch let error {
- XCTAssertTrue(error is NetworkError)
+ XCTFail("Expects throws")
+ } catch {
+ return
}
}
- @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
- func testGIVEN_aRequest_WHEN_requestWithAsyncResultAndResponseAndCancel_THEN_ShouldThwo() async {
- // GIVEN
- let error = NSError(domain: "", code: 0, userInfo: nil)
- networkAccess.changeMock(data: nil, response: nil, error: error)
+ func testRequest_withStatusCode401Response() async throws {
+ //Given
+ let testData: Data! = "test_string".data(using: .utf8)
+ let expectedResponse = try XCTUnwrap(HTTPURLResponse(url: .defaultMock, statusCode: 401, httpVersion: nil, headerFields: nil))
+ let networkAccess = NetworkAccessMock(result: .success((testData, expectedResponse)))
+ let networkService = BasicNetworkService(networkAccess: networkAccess)
//When
- let task = Task {
- try await networkService.request(resource)
- }
- task.cancel()
- let result = await task.result
- if case .failure(let error) = result, let networkError = error as? CancellationError {
-
+ let result = await networkService.requestResult(for: resource)
+
+ //Then
+ if case .failure(.unauthorized(response: expectedResponse, data: testData)) = result {
+ return
} else {
- XCTFail("Schould throw")
+ XCTFail()
}
}
+
}
diff --git a/Tests/NetworkServiceWithErrorTest.swift b/Tests/NetworkServiceWithErrorTest.swift
index 3f71e3c..e1567ea 100644
--- a/Tests/NetworkServiceWithErrorTest.swift
+++ b/Tests/NetworkServiceWithErrorTest.swift
@@ -35,163 +35,55 @@ enum CustomError: Error {
class NetworkServiceWithErrorTest: XCTestCase {
- var networkService: NetworkService!
-
- var networkAccess = NetworkAccessMock()
-
let trainName = "ICE"
- var resource: ResourceWithError {
+ var resource: Resource {
let request = URLRequest(path: "train", baseURL: .defaultMock)
- return ResourceWithError(request: request, decoder: JSONDecoder(), mapError: { CustomError(networkError: $0) })
- }
-
- override func setUp() {
- networkService = BasicNetworkService(networkAccess: networkAccess)
- }
-
- func testRequest_withValidResponse() {
- //Given
- networkAccess.changeMock(data: Train.validJSONData, response: .defaultMock, error: nil)
- let expection = expectation(description: "loadValidRequest")
-
- //When
- networkService.request(resource, onCompletionWithResponse: { train, response in
- XCTAssertEqual(train.name, self.trainName)
- XCTAssertEqual(response, .defaultMock)
- expection.fulfill()
- }, onError: { _ in
- XCTFail("Should not call error block")
- })
-
- waitForExpectations(timeout: 1, handler: nil)
-
- //Then
- XCTAssertEqual(networkAccess.request?.url?.absoluteString, "https://bahn.de/train")
- }
-
- func testRequest_withError() {
- //Given
- networkAccess.changeMock(data: nil, response: nil, error: nil)
- let expection = expectation(description: "testNoData")
-
- //When
- var capturedError: CustomError?
- networkService.request(resource, onCompletion: { _ in
- XCTFail("Should not call success block")
- }, onError: { error in
- capturedError = error
- expection.fulfill()
- })
-
- //Then
- waitForExpectations(timeout: 1, handler: nil)
-
- XCTAssertEqual(capturedError, .error)
+ return Resource(request: request, decoder: JSONDecoder(), mapError: { CustomError(networkError: $0) })
}
- func testGIVEN_aRequest_WHEN_requestWithResultResponse_THEN_ShouldRespond() {
- // GIVEN
-
- networkAccess.changeMock(data: Train.validJSONData, response: .defaultMock, error: nil)
- let expection = expectation(description: "loadValidRequest")
- var expectedResult: Result?
-
- //When
- networkService.request(resource, onCompletion: { result in
- expectedResult = result
- expection.fulfill()
- })
-
- waitForExpectations(timeout: 1, handler: nil)
-
- //Then
- switch expectedResult {
- case .success(let train)?:
- XCTAssertEqual(train.name, self.trainName)
- case .failure?:
- XCTFail("Should be an error")
- case nil:
- XCTFail("Result should not be nil")
- }
- XCTAssertEqual(networkAccess.request?.url?.absoluteString, "https://bahn.de/train")
- }
-
- func testGIVEN_aRequest_WHEN_requestWithResultErrorResponse_THEN_ShouldError() {
+ func testRequest_withValidResponse() async throws {
//Given
- networkAccess.changeMock(data: nil, response: nil, error: nil)
- var expectedResult: Result?
- let expection = expectation(description: "testNoData")
-
- //When
-
- networkService.request(resource, onCompletion: { result in
- expectedResult = result
- expection.fulfill()
- })
-
- //Then
- waitForExpectations(timeout: 1, handler: nil)
+ let networkAccess = NetworkAccessMock(result: .success((Train.validJSONData, HTTPURLResponse.defaultMock)))
+ let networkService = BasicNetworkService(networkAccess: networkAccess)
- XCTAssertEqual(expectedResult, .failure(.error))
- }
-
- func testGIVEN_aRequest_WHEN_requestWithResultAndResponse_THEN_ShouldRespond() {
- // GIVEN
-
- networkAccess.changeMock(data: Train.validJSONData, response: .defaultMock, error: nil)
- let expection = expectation(description: "loadValidRequest")
- var expectedResult: Result<(Train, HTTPURLResponse), CustomError>?
-
//When
- networkService.request(resource: resource) { (result) in
- expectedResult = result
- expection.fulfill()
- }
-
- waitForExpectations(timeout: 1, handler: nil)
-
+ let (train, response) = try await networkService.requestResultWithResponse(for: resource).get()
+
//Then
- switch expectedResult {
- case .success(let result)?:
- XCTAssertEqual(result.0.name, self.trainName)
- XCTAssertEqual(result.1, .defaultMock)
- case .failure?:
- XCTFail("Should be an error")
- case nil:
- XCTFail("Result should not be nil")
- }
- XCTAssertEqual(networkAccess.request?.url?.absoluteString, "https://bahn.de/train")
+ XCTAssertEqual(train.name, self.trainName)
+ XCTAssertEqual(response, .defaultMock)
+ let request = await networkAccess.request
+ XCTAssertEqual(request?.url?.absoluteString, "https://bahn.de/train")
}
- @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
- func testGIVEN_aRequest_WHEN_requestWithAsyncResultAndResponse_THEN_ShouldRespond() async throws {
- // GIVEN
- networkAccess.changeMock(data: Train.validJSONData, response: .defaultMock, error: nil)
+ func testRequest_withError() async {
+ let error = NSError(domain: "", code: 0, userInfo: nil)
+ let networkAccess = NetworkAccessMock(result: .failure(error))
+ let networkService = BasicNetworkService(networkAccess: networkAccess)
//When
- let (result, response) = try await networkService.request(resource)
-
+ let result = await networkService.requestResult(for: resource)
//Then
- XCTAssertEqual(result.name, self.trainName)
- XCTAssertEqual(response, .defaultMock)
- XCTAssertEqual(networkAccess.request?.url?.absoluteString, "https://bahn.de/train")
+ XCTAssertEqual(result, .failure(.error))
}
-
- @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
- func testGIVEN_aRequest_WHEN_requestWithAsyncResultAndResponse_THEN_ShouldThwo() async {
- // GIVEN
+
+ func testRequestThrows_withError() async {
let error = NSError(domain: "", code: 0, userInfo: nil)
- networkAccess.changeMock(data: nil, response: nil, error: error)
+ let networkAccess = NetworkAccessMock(result: .failure(error))
+ let networkService = BasicNetworkService(networkAccess: networkAccess)
//When
do {
try await networkService.request(resource)
- XCTFail("Schould throw")
- } catch let error {
- XCTAssertTrue(error is CustomError)
+ XCTFail()
+ } catch let error as CustomError {
+ XCTAssertEqual(error, .error)
+ } catch {
+ XCTFail()
}
}
+
}
diff --git a/Tests/NetworkTaskMockTests.swift b/Tests/NetworkTaskMockTests.swift
deleted file mode 100644
index 11eee34..0000000
--- a/Tests/NetworkTaskMockTests.swift
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-// Copyright (C) 2017 DB Systel GmbH.
-// DB Systel GmbH; Jürgen-Ponto-Platz 1; D-60329 Frankfurt am Main; Germany; http://www.dbsystel.de/
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the "Software"),
-// to deal in the Software without restriction, including without limitation
-// the rights to use, copy, modify, merge, publish, distribute, sublicense,
-// and/or sell copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-//
-
-import XCTest
-import DBNetworkStack
-
-class NetworkTaskMockTests: XCTestCase {
-
- func testCancled() {
- //Given
- let task = NetworkTaskMock()
-
- //When
- task.cancel()
-
- //Then
- XCTAssertEqual(task.state, .canceled)
- }
-
- func testResumed() {
- //Given
- let task = NetworkTaskMock()
-
- //When
- task.resume()
-
- //Then
- XCTAssertEqual(task.state, .resumed)
- }
-
- func testSuspended() {
- //Given
- let task = NetworkTaskMock()
-
- //When
- task.suspend()
-
- //Then
- XCTAssertEqual(task.state, .suspended)
- }
-}
diff --git a/Tests/ResourceInspectTest.swift b/Tests/ResourceInspectTest.swift
index ba8540d..b0d615e 100644
--- a/Tests/ResourceInspectTest.swift
+++ b/Tests/ResourceInspectTest.swift
@@ -27,23 +27,42 @@ final class ResourceInspectTest: XCTestCase {
func testInspect() {
//Given
let data = Data()
- var capuredParsingData: Data?
- var capturedInspectedData: Data?
- let resource = Resource(request: URLRequest.defaultMock, parse: { data in
- capuredParsingData = data
+ let capturedParsingData = Container(nil)
+ let capturedInspectedData = Container(nil)
+
+ let resource = Resource(request: URLRequest.defaultMock, parse: { data in
+ capturedParsingData.setValue(data)
return 1
})
//When
let inspectedResource = resource.inspectData({ data in
- capturedInspectedData = data
+ capturedInspectedData.setValue(data)
})
let result = try? inspectedResource.parse(data)
-
+
//Then
XCTAssertNotNil(result)
- XCTAssertEqual(capuredParsingData, capturedInspectedData)
- XCTAssertEqual(data, capturedInspectedData)
- XCTAssertEqual(capuredParsingData, data)
+ XCTAssertEqual(capturedParsingData.getValue(), capturedInspectedData.getValue())
+ XCTAssertEqual(data, capturedInspectedData.getValue())
+ XCTAssertEqual(capturedParsingData.getValue(), data)
+ }
+}
+
+private class Container {
+ private var value: T
+
+ init(_ value: T) {
+ self.value = value
+ }
+
+ /// caller should manage concurrent access to its own state
+ func setValue(_ newValue: T) {
+ value = newValue
+ }
+
+ /// caller should manage concurrent access to its own state
+ func getValue() -> T {
+ value
}
}
diff --git a/Tests/ResourceTest.swift b/Tests/ResourceTest.swift
index 32febc4..c4ecd43 100644
--- a/Tests/ResourceTest.swift
+++ b/Tests/ResourceTest.swift
@@ -27,17 +27,25 @@ import DBNetworkStack
class ResourceTest: XCTestCase {
- func testResource() {
+ func testResource() throws {
//Given
let validData: Data! = "ICE".data(using: .utf8)
- let resource = Resource(request: URLRequest.defaultMock, parse: { String(data: $0, encoding: .utf8) })
-
+ let resource = Resource(request: URLRequest.defaultMock, parse: { String(data: $0, encoding: .utf8) })
+
//When
- let name = try? resource.parse(validData)
-
+ let name = try resource.parse(validData)
+
//Then
- XCTAssertEqual(name ?? nil, "ICE")
+ XCTAssertEqual(name, "ICE")
}
-
+
+ func testResourceWithVoidResult() throws {
+ //Given
+ let resource = Resource(request: URLRequest.defaultMock)
+
+ //When
+ try resource.parse(Data())
+ }
+
}
diff --git a/Tests/ResourceWithErrorTest.swift b/Tests/ResourceWithErrorTest.swift
index 9ea0e08..c073f9b 100644
--- a/Tests/ResourceWithErrorTest.swift
+++ b/Tests/ResourceWithErrorTest.swift
@@ -27,21 +27,38 @@ import DBNetworkStack
class ResourceWithErrorTest: XCTestCase {
- func testResource() {
+ func testResource() throws {
//Given
let validData: Data! = "ICE".data(using: .utf8)
- let resource = ResourceWithError(
+ let resource = Resource(
request: URLRequest.defaultMock,
parse: { String(data: $0, encoding: .utf8) },
mapError: { $0 }
)
//When
- let name = try? resource.parse(validData)
-
+ let name = try resource.parse(validData)
+
//Then
- XCTAssertEqual(name ?? nil, "ICE")
+ XCTAssertEqual(name, "ICE")
+ }
+
+ func testResourceMap() throws {
+ //Given
+ let validData: Data! = "ICE".data(using: .utf8)
+
+ let resource = Resource(
+ request: URLRequest.defaultMock,
+ parse: { String(data: $0, encoding: .utf8) },
+ mapError: { $0 }
+ )
+
+ //When
+ let numberOfCharacters = try resource.map(transform: { $0?.count }).parse(validData)
+
+ //Then
+ XCTAssertEqual(numberOfCharacters, 3)
}
func testResourceMapError() {
@@ -49,7 +66,7 @@ class ResourceWithErrorTest: XCTestCase {
enum CustomError: Error{
case error
}
- let resource = ResourceWithError(
+ let resource = Resource(
request: URLRequest.defaultMock,
mapError: { _ in return .error }
)
@@ -60,5 +77,16 @@ class ResourceWithErrorTest: XCTestCase {
//Then
XCTAssertEqual(mappedError, .error)
}
-
+
+ func testResourceWithVoidResult() throws {
+ //Given
+ let resource = Resource(
+ request: URLRequest.defaultMock,
+ mapError: { _ in return .error }
+ )
+
+ //When
+ try resource.parse(Data())
+ }
+
}
diff --git a/Tests/RetryNetworkserviceTest.swift b/Tests/RetryNetworkserviceTest.swift
index 3722e2f..0c3a70b 100644
--- a/Tests/RetryNetworkserviceTest.swift
+++ b/Tests/RetryNetworkserviceTest.swift
@@ -25,116 +25,100 @@ import XCTest
@testable import DBNetworkStack
class RetryNetworkserviceTest: XCTestCase {
- var networkServiceMock: NetworkServiceMock!
- var resource: Resource {
+ var resource: Resource {
let request = URLRequest(path: "/train", baseURL: .defaultMock)
return Resource(request: request, parse: { _ in return 1})
}
- override func setUp() {
- super.setUp()
- networkServiceMock = NetworkServiceMock()
- }
-
- override func tearDown() {
- networkServiceMock = nil
- super.tearDown()
- }
-
- func testRetryRequest_shouldRetry() throws {
+ func testRetryRequest_shouldRetry() async throws {
//Given
- let errorCount = 2
let numberOfRetries = 2
- var executedRetrys = 0
-
- let retryService = RetryNetworkService(networkService: networkServiceMock, numberOfRetries: numberOfRetries,
- idleTimeInterval: 0, shouldRetry: { _ in return true }, dispatchRetry: { _, block in
- executedRetrys += 1
- block()
- })
+ let networkServiceMock = NetworkServiceMock(
+ Result.failure(.unknownError),
+ Result.failure(.unknownError),
+ Result.success(1)
+ )
+
+ let retryService = RetryNetworkService(
+ networkService: networkServiceMock,
+ numberOfRetries: numberOfRetries,
+ idleTimeInterval: 0,
+ shouldRetry: { _ in
+ return true
+ }
+ )
//When
- weak var task = retryService.request(resource, onCompletion: { _ in
- XCTAssertEqual(executedRetrys, numberOfRetries)
- }, onError: { _ in
- XCTFail("Expects to not call error block")
- })
- try (0...failure(.unknownError),
+ Result.failure(.unknownError),
+ Result.failure(.unknownError),
+ Result.failure(.unknownError)
+ )
+ let retryService = RetryNetworkService(
+ networkService: networkServiceMock,
+ numberOfRetries: 3,
+ idleTimeInterval: 0,
+ shouldRetry: { _ in return true }
+ )
+
//When
- weak var task: NetworkTask?
- task = RetryNetworkService(networkService: networkServiceMock, numberOfRetries: 3,
- idleTimeInterval: 0, shouldRetry: { _ in return true },
- dispatchRetry: { _, block in
- executedRetrys += 1
- block()
- }).request(resource, onCompletion: { _ in
- XCTFail("Expects to not call completion block")
- }, onError: { _ in
- XCTAssertEqual(executedRetrys, 3)
- })
- try (0..<4).forEach { _ in
- try networkServiceMock.returnError(with: .unknownError)
- }
+ let result = await retryService.requestResult(for: resource)
+
//Then
- XCTAssertNil(task)
+ if case .failure(.unknownError) = result {
+
+ } else {
+ XCTFail()
+ }
}
- func testRetryRequest_shouldNotRetry() throws {
+ func testRetryRequest_shouldNotRetry() async throws {
//Given
- let shoudlRetry = false
- var capturedError: NetworkError?
-
+ let networkServiceMock = NetworkServiceMock(
+ Result.failure(.unknownError)
+ )
+ let retryService = RetryNetworkService(
+ networkService: networkServiceMock,
+ numberOfRetries: 3,
+ idleTimeInterval: 0,
+ shouldRetry: { _ in return true }
+ )
+
//When
- weak var task: NetworkTask?
- task = RetryNetworkService(networkService: networkServiceMock, numberOfRetries: 3,
- idleTimeInterval: 0, shouldRetry: { _ in return shoudlRetry },
- dispatchRetry: { _, block in
- XCTFail("Expects to not retry")
- block()
- }).request(resource, onCompletion: { _ in
- XCTFail("Expects to not call completion block")
- }, onError: { error in
- capturedError = error
- })
- try networkServiceMock.returnError(with: .unknownError)
-
- //Then
- XCTAssertNil(task)
- XCTAssertNotNil(capturedError)
+ await retryService.requestResult(for: resource)
}
}
diff --git a/Tests/TrainModel.swift b/Tests/TrainModel.swift
index 4c31db1..cce8d06 100644
--- a/Tests/TrainModel.swift
+++ b/Tests/TrainModel.swift
@@ -24,7 +24,7 @@
import Foundation
import DBNetworkStack
-struct Train: Decodable, Equatable {
+struct Train: Codable, Equatable {
let name: String
}
diff --git a/Tests/URLSession+NetworkAccessTest.swift b/Tests/URLSession+NetworkAccessTest.swift
deleted file mode 100644
index f465f5b..0000000
--- a/Tests/URLSession+NetworkAccessTest.swift
+++ /dev/null
@@ -1,80 +0,0 @@
-//
-// Copyright (C) 2017 DB Systel GmbH.
-// DB Systel GmbH; Jürgen-Ponto-Platz 1; D-60329 Frankfurt am Main; Germany; http://www.dbsystel.de/
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the "Software"),
-// to deal in the Software without restriction, including without limitation
-// the rights to use, copy, modify, merge, publish, distribute, sublicense,
-// and/or sell copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-//
-
-import XCTest
-import DBNetworkStack
-
-class URLSessionNetworkAccessTest: XCTestCase {
-
- var urlSessionMock: URLSessionMock!
-
- override func setUp() {
- urlSessionMock = URLSessionMock()
- }
-
- func testCorrectArgumentsGetPassedToURLSession() {
- //Given
- let request = URLRequest.defaultMock
-
- //When
- _ = urlSessionMock.load(request: request, callback: { _, _, _ in })
-
- //Then
- XCTAssertEqual(urlSessionMock.lastOutgoingRequest, request)
- }
-
- func testDataTaskGetsResumed() {
- //Given
- let request = URLRequest.defaultMock
-
- //When
- _ = urlSessionMock.load(request: request, callback: { _, _, _ in })
-
- //Then
- XCTAssertTrue(urlSessionMock.currentDataTask.didResume)
- }
-
- func testCompleteWithResult() {
- //Given
- let data = Data()
- let reponse = HTTPURLResponse(url: .defaultMock, statusCode: 0, httpVersion: nil, headerFields: nil)
- let error = NSError(domain: "", code: 0, userInfo: nil)
-
- var capturedData: Data?
- var capturedResponse: HTTPURLResponse?
- var capturedError: Error?
-
- //When
- _ = urlSessionMock.load(request: .defaultMock, callback: { data, response, error in
- capturedData = data
- capturedResponse = response
- capturedError = error
- })
- urlSessionMock.completeWith(data: data, response: reponse, error: error)
-
- //Then
- XCTAssertEqual(data, capturedData)
- XCTAssertEqual(reponse, capturedResponse)
- XCTAssertNotNil(capturedError)
- }
-}
diff --git a/Tests/URLSessionMock.swift b/Tests/URLSessionMock.swift
deleted file mode 100644
index 16bb806..0000000
--- a/Tests/URLSessionMock.swift
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// Copyright (C) 2017 DB Systel GmbH.
-// DB Systel GmbH; Jürgen-Ponto-Platz 1; D-60329 Frankfurt am Main; Germany; http://www.dbsystel.de/
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the "Software"),
-// to deal in the Software without restriction, including without limitation
-// the rights to use, copy, modify, merge, publish, distribute, sublicense,
-// and/or sell copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-// DEALINGS IN THE SOFTWARE.
-//
-
-import Foundation
-
-class URLSessionDataTaskMock: URLSessionDataTask {
- var didResume = false
-
- override init() {
- }
-
- override func resume() {
- didResume = true
- }
-}
-
-class URLSessionMock: URLSession {
-
- var lastOutgoingRequest: URLRequest?
- var currentDataTask: URLSessionDataTaskMock!
-
- private var completeRequest: ((Data?, URLResponse?, Error?) -> Void)?
-
- override func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
- lastOutgoingRequest = request
- currentDataTask = URLSessionDataTaskMock()
- completeRequest = completionHandler
- return currentDataTask
- }
-
- func completeWith(data: Data?, response: URLResponse?, error: Error?) {
- completeRequest?(data, response, error)
- completeRequest = nil
- }
-}