Skip to content

Commit

Permalink
Default async/await implementation (#37)
Browse files Browse the repository at this point in the history
* Add default implementation async/await methods in Client. AsyncAwaitHelper added.

* fix intents. used resume with Result<>. self use removed
  • Loading branch information
laqiguy authored Feb 4, 2022
1 parent eea1c31 commit 38b4081
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 49 deletions.
25 changes: 25 additions & 0 deletions Sources/Apexy/AsyncAwaitHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// AsyncAwaitHelper.swift
//
//
// Created by Aleksei Tiurnin on 31.01.2022.
//

import Foundation

@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
public enum AsyncAwaitHelper {
public typealias ContentContinuation<T> = CheckedContinuation<T, Error>

public static func adaptToAsync<T>(dataTaskClosure: (ContentContinuation<T>) -> Progress) async throws -> T {
let progressWrapper = ProgressWrapper()
return try await withTaskCancellationHandler(handler: {
progressWrapper.cancel()
}, operation: {
try await withCheckedThrowingContinuation { (continuation: ContentContinuation<T>) in
let progress = dataTaskClosure(continuation)
progressWrapper.progress = progress
}
})
}
}
16 changes: 16 additions & 0 deletions Sources/Apexy/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,19 @@ public protocol Client: AnyObject {
func upload<T>(_ endpoint: T) async throws -> T.Content where T: UploadEndpoint

}

@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
public extension Client {

func request<T>(_ endpoint: T) async throws -> T.Content where T: Endpoint {
try await AsyncAwaitHelper.adaptToAsync(dataTaskClosure: { continuation in
request(endpoint, completionHandler: continuation.resume)
})
}

func upload<T>(_ endpoint: T) async throws -> T.Content where T: UploadEndpoint {
try await AsyncAwaitHelper.adaptToAsync(dataTaskClosure: { continuation in
upload(endpoint, completionHandler: continuation.resume)
})
}
}
27 changes: 1 addition & 26 deletions Sources/ApexyAlamofire/AlamofireClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,32 +181,7 @@ open class AlamofireClient: Client {
progress.cancellationHandler = { [weak request] in request?.cancel() }
return progress
}

@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
public func request<T>(_ endpoint: T) async throws -> T.Content where T : Endpoint {
typealias ContentContinuation = CheckedContinuation<T.Content, Error>
let progressWrapper = ProgressWrapper()
return try await withTaskCancellationHandler(handler: {
progressWrapper.cancel()
}, operation: {
try await withCheckedThrowingContinuation { (continuation: ContentContinuation) in
progressWrapper.progress = request(endpoint) { continuation.resume(with: $0) }
}
})
}

@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *)
public func upload<T>(_ endpoint: T) async throws -> T.Content where T : UploadEndpoint {
typealias ContentContinuation = CheckedContinuation<T.Content, Error>
let progressWrapper = ProgressWrapper()
return try await withTaskCancellationHandler(handler: {
progressWrapper.cancel()
}, operation: {
try await withCheckedThrowingContinuation { (continuation: ContentContinuation) in
progressWrapper.progress = upload(endpoint) { continuation.resume(with: $0) }
}
})
}

}

// MARK: - Helper
Expand Down
35 changes: 12 additions & 23 deletions Sources/ApexyURLSession/URLSession+AsyncAwait13.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,65 +13,54 @@ import Foundation
@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 {

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 = 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
return try await AsyncAwaitHelper.adaptToAsync(dataTaskClosure: { continuation in
let task = 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()
return task.progress
})
}

public func upload(
for request: URLRequest,
fromFile fileURL: URL,
delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) {
return try await adaptToAsync(dataTaskClosure: { continuation in
return self.uploadTask(with: request, fromFile: fileURL) { data, response, error in
return try await AsyncAwaitHelper.adaptToAsync(dataTaskClosure: { continuation in
let task = 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()
return task.progress
})
}

public func upload(
for request: URLRequest,
from bodyData: Data,
delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse) {
return try await adaptToAsync(dataTaskClosure: { continuation in
return self.uploadTask(with: request, from: bodyData) { data, response, error in
return try await AsyncAwaitHelper.adaptToAsync(dataTaskClosure: { continuation in
let task = 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()
return task.progress
})
}
}

0 comments on commit 38b4081

Please sign in to comment.