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( _ transform: @escaping (Output) async -> T ) -> Publishers.FlatMap, Self> { flatMap { value in Future { promise in Task { let output = await transform(value) promise(.success(output)) } } } } public func asyncMap( _ transform: @escaping (Output) async throws -> T ) -> Publishers.FlatMap, 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( _ transform: @escaping (Output) async throws -> T ) -> Publishers.FlatMap, Publishers.SetFailureType> { flatMap { value in Future { promise in Task { do { let output = try await transform(value) promise(.success(output)) } catch { promise(.failure(error)) } } } } } }