Hello,
In order to adapt Swift Concurrency, we have to do it in small steps and it means converting just parts of our code. Moreover, in some cases we will have to continue using Combine/other reactive frameworks.
My question is, is there a way to convert async function call to Combine that works without warnings with strict concurrency enabled?
My first idea was:
extension Future {
convenience init(asyncFunction: @escaping () async throws -> Output) where Failure == Error {
self.init { (promise: @escaping (Result<Output, any Error>) -> Void) in
Task {
do {
let value = try await asyncFunction() // Capture of 'asyncFunction' with non-sendable type '() async throws -> Output' in a `@Sendable` closure
promise(.success(value)) // Capture of 'promise' with non-sendable type '(Result<Output, any Error>) -> Void' in a `@Sendable` closure
} catch {
promise(.failure(error))
}
}
}
}
}
I can mark asyncFunction
with Sendable
and let users of this init
take care of that. But I cannot get rid of the second warning.
I also tried:
extension AnyPublisher {
init(asyncFunction: @escaping () async throws -> Output) where Failure == Error {
let subject = PassthroughSubject<Output, Failure>()
let task = Task {
do {
let value = try await asyncFunction()
subject.send(value)
subject.send(completion: .finished)
} catch {
subject.send(completion: .failure(error))
}
}
self.init(subject.handleEvents(receiveCancel: { task.cancel() }))
}
}
But PassthroughSubject
is also not Sendable
and gives similar warnings as above.
The other idea would be to write custom Subject
that would be Sendable
but I'm not sure how complicated this would be.