Skip to content

Instantly share code, notes, and snippets.

@wotjd
Created April 1, 2024 16:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wotjd/15df7be2a0e4395de6a57a861b2fc3ca to your computer and use it in GitHub Desktop.
Save wotjd/15df7be2a0e4395de6a57a861b2fc3ca to your computer and use it in GitHub Desktop.
Moya async request with handling cancellation
import Foundation
import Moya
struct AsyncRequestError: Error {
// TODO: add Description
}
extension MoyaProvider {
// method #1 - using AsyncThrowingStream
func request(_ target: Target) async throws -> Response {
let stream = AsyncThrowingStream<Response, Error> { continuation in
var didFinish = false
let cancellable = request(target) {
switch $0 {
case .success(let response):
continuation.yield(response)
didFinish = true
continuation.finish()
case .failure(let error):
didFinish = true
continuation.finish(throwing: error)
}
}
@Sendable func cancel() {
cancellable.cancel()
}
continuation.onTermination = { [didFinish] termination in
switch termination {
case .cancelled where !didFinish:
cancel()
default:
break
}
}
}
let response = try await {
var response: Response?
for try await element in stream {
guard response == nil else { continue }
response = element
}
return response
}()
// NOTE: should handle this optional case properly since the sequence can be empty
guard let response else { throw AsyncRequestError() }
return response
}
// method #2 - using withTaskCancellationHandler & withCheckedThrowingContinuation
func request(_ target: Target) async throws -> Response {
let lock = NSLock()
var checkedContinuation: CheckedContinuation<Response, Error>?
var result: Result<Response, MoyaError>?
let cancellable = request(target) {
lock.lock()
if let checkedContinuation {
checkedContinuation.resume(with: $0)
} else {
result = $0
}
lock.unlock()
}
@Sendable func cancel() {
cancellable.cancel()
}
return try await withTaskCancellationHandler {
try await withCheckedThrowingContinuation { continuation in
lock.lock()
if let result {
continuation.resume(with: result)
} else {
checkedContinuation = continuation
}
lock.unlock()
}
} onCancel: { cancel() }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment