91 lines
2.8 KiB
Swift
91 lines
2.8 KiB
Swift
import Combine
|
|
|
|
// Ref: https://www.swiftbysundell.com/articles/connecting-async-await-with-other-swift-code/
|
|
|
|
extension Publishers {
|
|
public struct MissingOutputError: Error {}
|
|
}
|
|
|
|
extension Publisher {
|
|
public func singleOutput() async throws -> Output {
|
|
var cancellable: AnyCancellable?
|
|
var didReceiveValue = false
|
|
|
|
return try await withCheckedThrowingContinuation { continuation in
|
|
cancellable = sink(
|
|
receiveCompletion: { completion in
|
|
switch completion {
|
|
case .failure(let error):
|
|
continuation.resume(throwing: error)
|
|
case .finished:
|
|
if !didReceiveValue {
|
|
continuation.resume(
|
|
throwing: Publishers.MissingOutputError()
|
|
)
|
|
}
|
|
}
|
|
},
|
|
receiveValue: { value in
|
|
guard !didReceiveValue else { return }
|
|
|
|
didReceiveValue = true
|
|
cancellable?.cancel()
|
|
continuation.resume(returning: value)
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ref: https://www.swiftbysundell.com/articles/calling-async-functions-within-a-combine-pipeline/
|
|
|
|
extension Publisher {
|
|
public func asyncMap<T>(
|
|
_ transform: @escaping (Output) async -> T
|
|
) -> Publishers.FlatMap<Future<T, Never>, Self> {
|
|
flatMap { value in
|
|
Future { promise in
|
|
Task {
|
|
let output = await transform(value)
|
|
promise(.success(output))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public func asyncMap<T>(
|
|
_ transform: @escaping (Output) async throws -> T
|
|
) -> Publishers.FlatMap<Future<T, Error>, Self> {
|
|
flatMap { value in
|
|
Future { promise in
|
|
Task {
|
|
do {
|
|
let output = try await transform(value)
|
|
promise(.success(output))
|
|
} catch {
|
|
promise(.failure(error))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public func asyncMap<T>(
|
|
_ transform: @escaping (Output) async throws -> T
|
|
) -> Publishers.FlatMap<Future<T, Error>,
|
|
Publishers.SetFailureType<Self, Error>> {
|
|
flatMap { value in
|
|
Future { promise in
|
|
Task {
|
|
do {
|
|
let output = try await transform(value)
|
|
promise(.success(output))
|
|
} catch {
|
|
promise(.failure(error))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|