From 14c4ea64edcf0291eb188c46cfd1fcb3a46eb551 Mon Sep 17 00:00:00 2001 From: Aleksei Tiurnin Date: Thu, 20 Jan 2022 14:12:08 +0300 Subject: [PATCH 01/10] Added iOS 13 support + Tests --- Sources/Apexy/Client.swift | 4 +- Sources/ApexyAlamofire/AlamofireClient.swift | 7 +- .../URLSession+AsyncAwait13.swift | 73 +++++++++++++++++++ .../ApexyURLSession/URLSessionClient.swift | 5 +- .../URLSessionClientTests.swift | 36 +++++++++ 5 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 Sources/ApexyURLSession/URLSession+AsyncAwait13.swift diff --git a/Sources/Apexy/Client.swift b/Sources/Apexy/Client.swift index 54e8c5e..cdd405e 100644 --- a/Sources/Apexy/Client.swift +++ b/Sources/Apexy/Client.swift @@ -28,12 +28,12 @@ public protocol Client: AnyObject { /// Send request to specified endpoint. /// - Returns: response data from the server for the request. - @available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) func request(_ endpoint: T) async throws -> T.Content where T: Endpoint /// Upload data to specified endpoint. /// - Returns: response data from the server for the upload. - @available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) func upload(_ endpoint: T) async throws -> T.Content where T: UploadEndpoint } diff --git a/Sources/ApexyAlamofire/AlamofireClient.swift b/Sources/ApexyAlamofire/AlamofireClient.swift index 18b441a..f406f65 100755 --- a/Sources/ApexyAlamofire/AlamofireClient.swift +++ b/Sources/ApexyAlamofire/AlamofireClient.swift @@ -5,6 +5,7 @@ // Copyright © 2019 RedMadRobot. All rights reserved. // +import Apexy import Foundation import Alamofire @@ -179,7 +180,7 @@ open class AlamofireClient: Client { return progress } - @available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) + @available(macOS 10.15, iOS 12, watchOS 6, tvOS 13, *) public func request(_ endpoint: T) async throws -> T.Content where T : Endpoint { typealias ContentContinuation = CheckedContinuation let progressWrapper = ProgressWrapper() @@ -192,7 +193,7 @@ open class AlamofireClient: Client { }) } - @available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) + @available(macOS 10.15, iOS 12, watchOS 6, tvOS 13, *) public func upload(_ endpoint: T) async throws -> T.Content where T : UploadEndpoint { typealias ContentContinuation = CheckedContinuation let progressWrapper = ProgressWrapper() @@ -247,7 +248,7 @@ public extension Error { } } -@available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) private final class ProgressWrapper { var progress: Progress? { diff --git a/Sources/ApexyURLSession/URLSession+AsyncAwait13.swift b/Sources/ApexyURLSession/URLSession+AsyncAwait13.swift new file mode 100644 index 0000000..8c5843d --- /dev/null +++ b/Sources/ApexyURLSession/URLSession+AsyncAwait13.swift @@ -0,0 +1,73 @@ +// +// URLSession+AsyncAwait13.swift +// ApexyURLSession +// +// Created by Aleksei Tiurnin on 20.01.2022. +// + +import Foundation + +extension URLSession { + @available(macOS, introduced: 10.15, deprecated: 12, message: "Extension is no longer necessary. Use API built into SDK") + @available(iOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") + @available(watchOS, introduced: 6, deprecated: 8, message: "Extension is no longer necessary. Use API built into SDK") + @available(tvOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") + func data( + for request: URLRequest, + delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { + try await withCheckedThrowingContinuation { continuation in + let task = self.dataTask(with: request) { data, response, error in + guard let data = data, let response = response else { + let error = error ?? URLError(.badServerResponse) + return continuation.resume(throwing: error) + } + + continuation.resume(returning: (data, response)) + } + + task.resume() + } + } + + @available(macOS, introduced: 10.15, deprecated: 12, message: "Extension is no longer necessary. Use API built into SDK") + @available(iOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") + @available(watchOS, introduced: 6, deprecated: 8, message: "Extension is no longer necessary. Use API built into SDK") + @available(tvOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") + public func upload( + for request: URLRequest, + fromFile fileURL: URL, + delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { + try await withCheckedThrowingContinuation { continuation in + let task = self.uploadTask(with: request, fromFile: fileURL) { data, response, error in + guard let data = data, let response = response else { + let error = error ?? URLError(.badServerResponse) + return continuation.resume(throwing: error) + } + + continuation.resume(returning: (data, response)) + } + task.resume() + } + } + + @available(macOS, introduced: 10.15, deprecated: 12, message: "Extension is no longer necessary. Use API built into SDK") + @available(iOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") + @available(watchOS, introduced: 6, deprecated: 8, message: "Extension is no longer necessary. Use API built into SDK") + @available(tvOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") + public func upload( + for request: URLRequest, + from bodyData: Data, + delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { + try await withCheckedThrowingContinuation { continuation in + let task = self.uploadTask(with: request, from: bodyData) { data, response, error in + guard let data = data, let response = response else { + let error = error ?? URLError(.badServerResponse) + return continuation.resume(throwing: error) + } + + continuation.resume(returning: (data, response)) + } + task.resume() + } + } +} diff --git a/Sources/ApexyURLSession/URLSessionClient.swift b/Sources/ApexyURLSession/URLSessionClient.swift index 18f79a5..856c6c3 100644 --- a/Sources/ApexyURLSession/URLSessionClient.swift +++ b/Sources/ApexyURLSession/URLSessionClient.swift @@ -1,3 +1,4 @@ +import Apexy import Foundation open class URLSessionClient: Client { @@ -127,7 +128,7 @@ open class URLSessionClient: Client { return task.progress } - @available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) open func request(_ endpoint: T) async throws -> T.Content where T : Endpoint { var request = try endpoint.makeRequest() request = try requestAdapter.adapt(request) @@ -156,7 +157,7 @@ open class URLSessionClient: Client { } } - @available(macOS 12, iOS 15, watchOS 8, tvOS 15, *) + @available(macOS 10.15, iOS 12, watchOS 6, tvOS 13, *) open func upload(_ endpoint: T) async throws -> T.Content where T : UploadEndpoint { var request: (request: URLRequest, body: UploadEndpointBody) = try endpoint.makeRequest() request.request = try requestAdapter.adapt(request.request) diff --git a/Tests/ApexyURLSessionTests/URLSessionClientTests.swift b/Tests/ApexyURLSessionTests/URLSessionClientTests.swift index 7bbcd12..33624be 100644 --- a/Tests/ApexyURLSessionTests/URLSessionClientTests.swift +++ b/Tests/ApexyURLSessionTests/URLSessionClientTests.swift @@ -119,6 +119,42 @@ final class URLSessionClientTests: XCTestCase { wait(for: [exp], timeout: 0.1) } + + @available(iOS 13.0, *) + @available(macOS 10.15, *) + func testClientDataRequestUsingAsyncAwait() async throws { + let endpoint = EmptyEndpoint() + let data = "Test".data(using: .utf8)! + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: "2.0", headerFields: nil)! + return (response, data) + } + + do { + let content = try await client.request(endpoint) + XCTAssertEqual(content, data) + } catch { + XCTFail("Expected result: .success, actual result: .failure") + } + } + + @available(iOS 13.0, *) + @available(macOS 10.15, *) + func testClientUploadUsingAsyncAwait() async throws { + let data = "apple".data(using: .utf8)! + let endpoint = SimpleUploadEndpoint(data: data) + MockURLProtocol.requestHandler = { request in + let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: "2.0", headerFields: nil)! + return (response, data) + } + + do { + let content = try await client.upload(endpoint) + XCTAssertEqual(content, data) + } catch { + XCTFail("Expected result: .success, actual result: .failure") + } + } } private final class MockURLProtocol: URLProtocol { From 12060860bb7fb09ca3b02483b2a6c0cd030d9b9c Mon Sep 17 00:00:00 2001 From: Aleksei Tiurnin Date: Thu, 20 Jan 2022 14:27:49 +0300 Subject: [PATCH 02/10] Update `runs-on` macOS version --- .github/workflows/release.yml | 2 +- .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 305dc1d..5fdd6eb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ on: jobs: release: name: Make CocoaPods release - runs-on: macOS-latest + runs-on: macos-11 steps: - name: Checkout uses: actions/checkout@v2 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e7b80dc..ffecc5c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ on: jobs: test: name: Run tests - runs-on: macOS-latest + runs-on: macos-11 steps: - name: Checkout uses: actions/checkout@v2 From 9b12da09a491ca3f02b38881140f332c5b9b7cd7 Mon Sep 17 00:00:00 2001 From: Aleksei Tiurnin Date: Thu, 20 Jan 2022 14:40:47 +0300 Subject: [PATCH 03/10] Added XCode 13 selection through env variable in ci.yml --- .github/workflows/release.yml | 2 ++ .github/workflows/tests.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5fdd6eb..f5d2557 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,8 @@ on: jobs: release: name: Make CocoaPods release + env: + DEVELOPER_DIR: /Applications/Xcode_13.app/Contents/Developer runs-on: macos-11 steps: - name: Checkout diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ffecc5c..cdc6e0a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,8 @@ on: jobs: test: + env: + DEVELOPER_DIR: /Applications/Xcode_13.app/Contents/Developer name: Run tests runs-on: macos-11 steps: From 9e3cd40f3b35405b4d4260e8772ac238b1fb2f91 Mon Sep 17 00:00:00 2001 From: Aleksei Tiurnin Date: Thu, 20 Jan 2022 14:42:02 +0300 Subject: [PATCH 04/10] iOS version fixed --- Sources/ApexyAlamofire/AlamofireClient.swift | 4 ++-- Sources/ApexyURLSession/URLSessionClient.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/ApexyAlamofire/AlamofireClient.swift b/Sources/ApexyAlamofire/AlamofireClient.swift index f406f65..a4f309b 100755 --- a/Sources/ApexyAlamofire/AlamofireClient.swift +++ b/Sources/ApexyAlamofire/AlamofireClient.swift @@ -180,7 +180,7 @@ open class AlamofireClient: Client { return progress } - @available(macOS 10.15, iOS 12, watchOS 6, tvOS 13, *) + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) public func request(_ endpoint: T) async throws -> T.Content where T : Endpoint { typealias ContentContinuation = CheckedContinuation let progressWrapper = ProgressWrapper() @@ -193,7 +193,7 @@ open class AlamofireClient: Client { }) } - @available(macOS 10.15, iOS 12, watchOS 6, tvOS 13, *) + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) public func upload(_ endpoint: T) async throws -> T.Content where T : UploadEndpoint { typealias ContentContinuation = CheckedContinuation let progressWrapper = ProgressWrapper() diff --git a/Sources/ApexyURLSession/URLSessionClient.swift b/Sources/ApexyURLSession/URLSessionClient.swift index 856c6c3..2c175c7 100644 --- a/Sources/ApexyURLSession/URLSessionClient.swift +++ b/Sources/ApexyURLSession/URLSessionClient.swift @@ -157,7 +157,7 @@ open class URLSessionClient: Client { } } - @available(macOS 10.15, iOS 12, watchOS 6, tvOS 13, *) + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) open func upload(_ endpoint: T) async throws -> T.Content where T : UploadEndpoint { var request: (request: URLRequest, body: UploadEndpointBody) = try endpoint.makeRequest() request.request = try requestAdapter.adapt(request.request) From ec15da1b83188b984d4c20fb34c653cfaecd8226 Mon Sep 17 00:00:00 2001 From: Aleksei Tiurnin Date: Thu, 20 Jan 2022 14:44:58 +0300 Subject: [PATCH 05/10] xcode-select fix in test pipeline --- .github/workflows/release.yml | 2 -- .github/workflows/tests.yml | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f5d2557..5fdd6eb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,8 +8,6 @@ on: jobs: release: name: Make CocoaPods release - env: - DEVELOPER_DIR: /Applications/Xcode_13.app/Contents/Developer runs-on: macos-11 steps: - name: Checkout diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cdc6e0a..aa2fd37 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,12 +13,12 @@ on: jobs: test: - env: - DEVELOPER_DIR: /Applications/Xcode_13.app/Contents/Developer name: Run tests runs-on: macos-11 steps: - name: Checkout uses: actions/checkout@v2 + - name: XCode select + run: xcode-select -s /Applications/Xcode_13.2.1.app - name: Build and test run: swift test --enable-code-coverage --disable-automatic-resolution \ No newline at end of file From 1ac856e1dbdfb89b7b262163bfe05d58992c1ecb Mon Sep 17 00:00:00 2001 From: Aleksei Tiurnin Date: Thu, 20 Jan 2022 14:47:07 +0300 Subject: [PATCH 06/10] added sudo to xcode-select --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index aa2fd37..21807b3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -19,6 +19,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - name: XCode select - run: xcode-select -s /Applications/Xcode_13.2.1.app + run: sudo xcode-select -s /Applications/Xcode_13.2.1.app - name: Build and test run: swift test --enable-code-coverage --disable-automatic-resolution \ No newline at end of file From 1a61286a841c6c45305d9acb0cfc1894b5902f7e Mon Sep 17 00:00:00 2001 From: Alexey Date: Thu, 20 Jan 2022 15:15:13 +0300 Subject: [PATCH 07/10] Update .github/workflows/tests.yml Co-authored-by: Daniil Subbotin --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 21807b3..2890a51 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - - name: XCode select + - name: Change Xcode run: sudo xcode-select -s /Applications/Xcode_13.2.1.app - name: Build and test run: swift test --enable-code-coverage --disable-automatic-resolution \ No newline at end of file From e4ab189db843305f49d278b7b306f2c981394737 Mon Sep 17 00:00:00 2001 From: Aleksei Tiurnin Date: Thu, 20 Jan 2022 15:19:05 +0300 Subject: [PATCH 08/10] xcode-select latest xcode added --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5fdd6eb..38d5042 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,9 @@ jobs: - name: Checkout uses: actions/checkout@v2 + - name: Change Xcode + run: sudo xcode-select -s /Applications/Xcode_13.2.1.app + - name: Install Cocoapods run: gem install cocoapods From 410bfc55c9ad59eda58b4790dec0cde68f7a7ed5 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Thu, 20 Jan 2022 16:23:18 +0300 Subject: [PATCH 09/10] Added cancelation to URLSession tasks --- Sources/Apexy/ProgressWrapper.swift | 36 ++++++++ Sources/ApexyAlamofire/AlamofireClient.swift | 24 ----- .../URLSession+AsyncAwait13.swift | 87 ++++++++++++------- 3 files changed, 92 insertions(+), 55 deletions(-) create mode 100644 Sources/Apexy/ProgressWrapper.swift diff --git a/Sources/Apexy/ProgressWrapper.swift b/Sources/Apexy/ProgressWrapper.swift new file mode 100644 index 0000000..9f31ba1 --- /dev/null +++ b/Sources/Apexy/ProgressWrapper.swift @@ -0,0 +1,36 @@ +// +// ProgressWrapper.swift +// ApexyURLSession +// +// Created by Aleksei Tiurnin on 20.01.2022. +// + +import Foundation + +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) +public final class ProgressWrapper { + + public var progress: Progress? { + get { + lock.lock() + defer { lock.unlock() } + return _progress + } + set { + lock.lock() + defer { lock.unlock() } + _progress = newValue + } + } + + private var _progress: Progress? + private let lock = NSLock() + + public init(_progress: Progress? = nil) { + self._progress = _progress + } + + public func cancel() { + progress?.cancel() + } +} diff --git a/Sources/ApexyAlamofire/AlamofireClient.swift b/Sources/ApexyAlamofire/AlamofireClient.swift index a4f309b..2b88dc0 100755 --- a/Sources/ApexyAlamofire/AlamofireClient.swift +++ b/Sources/ApexyAlamofire/AlamofireClient.swift @@ -247,27 +247,3 @@ public extension Error { return self } } - -@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) -private final class ProgressWrapper { - - var progress: Progress? { - get { - lock.lock() - defer { lock.unlock() } - return _progress - } - set { - lock.lock() - defer { lock.unlock() } - _progress = newValue - } - } - - private var _progress: Progress? - private let lock = NSLock() - - func cancel() { - progress?.cancel() - } -} diff --git a/Sources/ApexyURLSession/URLSession+AsyncAwait13.swift b/Sources/ApexyURLSession/URLSession+AsyncAwait13.swift index 8c5843d..59f4031 100644 --- a/Sources/ApexyURLSession/URLSession+AsyncAwait13.swift +++ b/Sources/ApexyURLSession/URLSession+AsyncAwait13.swift @@ -5,6 +5,7 @@ // Created by Aleksei Tiurnin on 20.01.2022. // +import Apexy import Foundation extension URLSession { @@ -15,19 +16,26 @@ extension URLSession { func data( for request: URLRequest, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { - try await withCheckedThrowingContinuation { continuation in - let task = self.dataTask(with: request) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) + typealias ContentContinuation = CheckedContinuation<(Data, URLResponse), Error> + let progressWrapper = ProgressWrapper() + return try await withTaskCancellationHandler(handler: { + progressWrapper.cancel() + }, operation: { + try await withCheckedThrowingContinuation { (continuation: ContentContinuation) in + let task = self.dataTask(with: request) { data, response, error in + guard let data = data, let response = response else { + let error = error ?? URLError(.badServerResponse) + return continuation.resume(throwing: error) + } + + continuation.resume(returning: (data, response)) + } + progressWrapper.progress = task.progress + + task.resume() } - - continuation.resume(returning: (data, response)) - } - - task.resume() + }) } - } @available(macOS, introduced: 10.15, deprecated: 12, message: "Extension is no longer necessary. Use API built into SDK") @available(iOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") @@ -37,18 +45,27 @@ extension URLSession { for request: URLRequest, fromFile fileURL: URL, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { - try await withCheckedThrowingContinuation { continuation in - let task = self.uploadTask(with: request, fromFile: fileURL) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) + typealias ContentContinuation = CheckedContinuation<(Data, URLResponse), Error> + let progressWrapper = ProgressWrapper() + return try await withTaskCancellationHandler(handler: { + progressWrapper.cancel() + }, operation: { + try await withCheckedThrowingContinuation { (continuation: ContentContinuation) in + let task = self.uploadTask(with: request, fromFile: fileURL) { data, response, error in + guard let data = data, let response = response else { + let error = error ?? URLError(.badServerResponse) + return continuation.resume(throwing: error) + } + + continuation.resume(returning: (data, response)) + } + + progressWrapper.progress = task.progress + + task.resume() } - - continuation.resume(returning: (data, response)) - } - task.resume() + }) } - } @available(macOS, introduced: 10.15, deprecated: 12, message: "Extension is no longer necessary. Use API built into SDK") @available(iOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") @@ -58,16 +75,24 @@ extension URLSession { for request: URLRequest, from bodyData: Data, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { - try await withCheckedThrowingContinuation { continuation in - let task = self.uploadTask(with: request, from: bodyData) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) + typealias ContentContinuation = CheckedContinuation<(Data, URLResponse), Error> + let progressWrapper = ProgressWrapper() + return try await withTaskCancellationHandler(handler: { + progressWrapper.cancel() + }, operation: { + try await withCheckedThrowingContinuation { (continuation: ContentContinuation) in + let task = self.uploadTask(with: request, from: bodyData) { data, response, error in + guard let data = data, let response = response else { + let error = error ?? URLError(.badServerResponse) + return continuation.resume(throwing: error) + } + + continuation.resume(returning: (data, response)) + } + progressWrapper.progress = task.progress + + task.resume() } - - continuation.resume(returning: (data, response)) - } - task.resume() + }) } - } } From 474734fa39a2b69248d528d25af89193bb076238 Mon Sep 17 00:00:00 2001 From: Aleksey Date: Thu, 20 Jan 2022 16:40:53 +0300 Subject: [PATCH 10/10] Availability for extension. Adapt function for async/await --- .../URLSession+AsyncAwait13.swift | 93 +++++++------------ 1 file changed, 36 insertions(+), 57 deletions(-) diff --git a/Sources/ApexyURLSession/URLSession+AsyncAwait13.swift b/Sources/ApexyURLSession/URLSession+AsyncAwait13.swift index 59f4031..f95b5be 100644 --- a/Sources/ApexyURLSession/URLSession+AsyncAwait13.swift +++ b/Sources/ApexyURLSession/URLSession+AsyncAwait13.swift @@ -8,90 +8,69 @@ import Apexy import Foundation +@available(macOS, introduced: 10.15, deprecated: 12, message: "Extension is no longer necessary. Use API built into SDK") +@available(iOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") +@available(watchOS, introduced: 6, deprecated: 8, message: "Extension is no longer necessary. Use API built into SDK") +@available(tvOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") extension URLSession { - @available(macOS, introduced: 10.15, deprecated: 12, message: "Extension is no longer necessary. Use API built into SDK") - @available(iOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") - @available(watchOS, introduced: 6, deprecated: 8, message: "Extension is no longer necessary. Use API built into SDK") - @available(tvOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") - func data( - for request: URLRequest, - delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { - typealias ContentContinuation = CheckedContinuation<(Data, URLResponse), Error> + + typealias ContentContinuation = CheckedContinuation<(Data, URLResponse), Error> + + private func adaptToAsync( + dataTaskClosure: (ContentContinuation) -> URLSessionDataTask) async throws -> (Data, URLResponse) { let progressWrapper = ProgressWrapper() return try await withTaskCancellationHandler(handler: { progressWrapper.cancel() }, operation: { try await withCheckedThrowingContinuation { (continuation: ContentContinuation) in - let task = self.dataTask(with: request) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) - } - - continuation.resume(returning: (data, response)) - } + let task = dataTaskClosure(continuation) progressWrapper.progress = task.progress task.resume() } }) + } + + public func data( + for request: URLRequest, + delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { + return try await adaptToAsync(dataTaskClosure: { continuation in + return self.dataTask(with: request) { data, response, error in + guard let data = data, let response = response else { + let error = error ?? URLError(.badServerResponse) + return continuation.resume(throwing: error) + } + continuation.resume(returning: (data, response)) + } + }) } - @available(macOS, introduced: 10.15, deprecated: 12, message: "Extension is no longer necessary. Use API built into SDK") - @available(iOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") - @available(watchOS, introduced: 6, deprecated: 8, message: "Extension is no longer necessary. Use API built into SDK") - @available(tvOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") public func upload( for request: URLRequest, fromFile fileURL: URL, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { - typealias ContentContinuation = CheckedContinuation<(Data, URLResponse), Error> - let progressWrapper = ProgressWrapper() - return try await withTaskCancellationHandler(handler: { - progressWrapper.cancel() - }, operation: { - try await withCheckedThrowingContinuation { (continuation: ContentContinuation) in - let task = self.uploadTask(with: request, fromFile: fileURL) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) - } - - continuation.resume(returning: (data, response)) + return try await adaptToAsync(dataTaskClosure: { continuation in + return self.uploadTask(with: request, fromFile: fileURL) { data, response, error in + guard let data = data, let response = response else { + let error = error ?? URLError(.badServerResponse) + return continuation.resume(throwing: error) } - - progressWrapper.progress = task.progress - - task.resume() + continuation.resume(returning: (data, response)) } }) } - @available(macOS, introduced: 10.15, deprecated: 12, message: "Extension is no longer necessary. Use API built into SDK") - @available(iOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") - @available(watchOS, introduced: 6, deprecated: 8, message: "Extension is no longer necessary. Use API built into SDK") - @available(tvOS, introduced: 13, deprecated: 15, message: "Extension is no longer necessary. Use API built into SDK") public func upload( for request: URLRequest, from bodyData: Data, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) { - typealias ContentContinuation = CheckedContinuation<(Data, URLResponse), Error> - let progressWrapper = ProgressWrapper() - return try await withTaskCancellationHandler(handler: { - progressWrapper.cancel() - }, operation: { - try await withCheckedThrowingContinuation { (continuation: ContentContinuation) in - let task = self.uploadTask(with: request, from: bodyData) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) - } - - continuation.resume(returning: (data, response)) + return try await adaptToAsync(dataTaskClosure: { continuation in + return self.uploadTask(with: request, from: bodyData) { data, response, error in + guard let data = data, let response = response else { + let error = error ?? URLError(.badServerResponse) + return continuation.resume(throwing: error) } - progressWrapper.progress = task.progress - - task.resume() + continuation.resume(returning: (data, response)) } }) }