-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add async/await shim * Add async/await tests * Add an async/await shim for URLSessionTransport * Guard Async/Await with compiler version check Also, rearrange some code into new files. * Restrict Async/Await to Darwin … until URLSession APIs are implemented in swift-corelibs-foundation. See https://forums.swift.org/t/how-to-use-async-await-w-docker/49591/7
- Loading branch information
Showing
5 changed files
with
200 additions
and
22 deletions.
There are no files selected for viewing
33 changes: 33 additions & 0 deletions
33
Sources/StructuredAPIClient/NetworkClient+AsyncAwait.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the StructuredAPIClient open source project | ||
// | ||
// Copyright (c) Stairtree GmbH | ||
// Licensed under the MIT license | ||
// | ||
// See LICENSE.txt for license information | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Foundation | ||
#if canImport(FoundationNetworking) | ||
import FoundationNetworking | ||
#endif | ||
|
||
#if compiler(>=5.5) && canImport(_Concurrency) && canImport(Darwin) | ||
|
||
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) | ||
extension NetworkClient { | ||
public func load<Request: NetworkRequest>(_ req: Request) async throws -> Request.ResponseDataType { | ||
try await withCheckedThrowingContinuation { continuation in | ||
self.load(req) { switch $0 { | ||
case .success(let value): continuation.resume(returning: value) | ||
case .failure(let error): continuation.resume(throwing: error) | ||
} } | ||
} | ||
} | ||
} | ||
|
||
#endif |
48 changes: 48 additions & 0 deletions
48
Sources/StructuredAPIClient/Transport/URLSessionTransport+AsyncAwait.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the StructuredAPIClient open source project | ||
// | ||
// Copyright (c) Stairtree GmbH | ||
// Licensed under the MIT license | ||
// | ||
// See LICENSE.txt for license information | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Foundation | ||
#if canImport(FoundationNetworking) | ||
import FoundationNetworking | ||
#endif | ||
|
||
#if compiler(>=5.5) && canImport(_Concurrency) && canImport(Darwin) | ||
|
||
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) | ||
extension URLSessionTransport { | ||
|
||
/// Sends the request using a `URLSessionDataTask` | ||
/// - Parameter request: The configured request to send. | ||
/// - Returns: The received response from the server. | ||
public func send(request: URLRequest) async throws -> TransportResponse { | ||
do { | ||
let (data, response) = try await session.data(for: request) | ||
guard let httpResponse = response as? HTTPURLResponse else { | ||
throw TransportFailure.network(URLError(.unsupportedURL)) | ||
} | ||
return httpResponse.asTransportResponse(withData: data) | ||
|
||
} catch let netError as URLError { | ||
if netError.code == .cancelled { throw TransportFailure.cancelled } | ||
throw TransportFailure.network(netError) | ||
|
||
} catch let error as TransportFailure { | ||
throw error | ||
|
||
} catch { | ||
throw TransportFailure.unknown(error) | ||
} | ||
} | ||
} | ||
|
||
#endif |
40 changes: 40 additions & 0 deletions
40
Sources/StructuredAPIClient/Transport/URLSessionTransport+Combine.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the StructuredAPIClient open source project | ||
// | ||
// Copyright (c) Stairtree GmbH | ||
// Licensed under the MIT license | ||
// | ||
// See LICENSE.txt for license information | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import Foundation | ||
#if canImport(FoundationNetworking) | ||
import FoundationNetworking | ||
#endif | ||
|
||
#if canImport(Combine) | ||
import Combine | ||
|
||
@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) | ||
extension URLSessionTransport { | ||
public func publisher(forRequest request: URLRequest) -> AnyPublisher<TransportResponse, Error> { | ||
return self.session.dataTaskPublisher(for: request) | ||
.mapError { netError -> Error in | ||
if netError.code == .cancelled { return TransportFailure.cancelled } | ||
else { return TransportFailure.network(netError) } | ||
} | ||
.tryMap { output in | ||
guard let response = output.response as? HTTPURLResponse else { | ||
throw TransportFailure.network(URLError(.unsupportedURL)) | ||
} | ||
return response.asTransportResponse(withData: output.data) | ||
} | ||
.eraseToAnyPublisher() | ||
} | ||
} | ||
#endif | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
Tests/StructuredAPIClientTests/NetworkClientWithAsyncAwaitTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the StructuredAPIClient open source project | ||
// | ||
// Copyright (c) Stairtree GmbH | ||
// Licensed under the MIT license | ||
// | ||
// See LICENSE.txt for license information | ||
// | ||
// SPDX-License-Identifier: MIT | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import XCTest | ||
#if canImport(FoundationNetworking) | ||
import FoundationNetworking | ||
#endif | ||
@testable import StructuredAPIClient | ||
import StructuredAPIClientTestSupport | ||
|
||
#if compiler(>=5.5) && canImport(_Concurrency) && canImport(Darwin) | ||
|
||
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) | ||
final class NetworkClientWithAsyncAwaitTests: XCTestCase { | ||
|
||
func testNetworkClientWithAsyncAwait() async throws { | ||
struct TestRequest: NetworkRequest { | ||
func makeRequest(baseURL: URL) throws -> URLRequest { URLRequest(url: baseURL) } | ||
func parseResponse(_ response: TransportResponse) throws -> String { .init(decoding: response.body, as: UTF8.self) } | ||
} | ||
|
||
let response: Result<TransportResponse, Error> = .success(.init(status: .ok, headers: [:], body: Data("Test".utf8))) | ||
|
||
let requestAssertions: (URLRequest) -> Void = { | ||
XCTAssertEqual($0.url, URL(string: "https://test.somewhere.com")!) | ||
} | ||
|
||
let client = NetworkClient(baseURL: URL(string: "https://test.somewhere.com")!, transport: TestTransport(responses: [response], assertRequest: requestAssertions)) | ||
|
||
let value = try await client.load(TestRequest()) | ||
|
||
XCTAssertEqual(value, "Test") | ||
XCTAssertEqual(client.baseURL.absoluteString, "https://test.somewhere.com") | ||
} | ||
|
||
func testTokenAuthWithAsyncAwait() async throws { | ||
struct TestRequest: NetworkRequest { | ||
func makeRequest(baseURL: URL) throws -> URLRequest { URLRequest(url: baseURL) } | ||
func parseResponse(_ response: TransportResponse) throws -> String { .init(decoding: response.body, as: UTF8.self) } | ||
} | ||
|
||
let accessToken = TestToken(raw: "abc", expiresAt: Date()) | ||
let refreshToken = TestToken(raw: "def", expiresAt: Date()) | ||
|
||
let tokenProvider = TestTokenProvider(accessToken: accessToken, refreshToken: refreshToken) | ||
|
||
let response: Result<TransportResponse, Error> = .success(.init(status: .ok, headers: [:], body: Data("Test".utf8))) | ||
|
||
let requestAssertions: (URLRequest) -> Void = { | ||
XCTAssertEqual($0.url, URL(string: "https://test.somewhere.com")!) | ||
XCTAssertEqual($0.allHTTPHeaderFields?["Authorization"], "Bearer abc") | ||
} | ||
|
||
let client = NetworkClient( | ||
baseURL: URL(string: "https://test.somewhere.com")!, | ||
transport: TokenAuthenticationHandler( | ||
base: TestTransport(responses: [response], assertRequest: requestAssertions), | ||
tokenProvider: tokenProvider | ||
) | ||
) | ||
|
||
let value = try await client.load(TestRequest()) | ||
|
||
XCTAssertEqual(value, "Test") | ||
XCTAssertEqual(client.baseURL.absoluteString, "https://test.somewhere.com") | ||
} | ||
} | ||
|
||
#endif |