I decide to use async/ await with the Combine framework. I used an extension on the Future class and put a Task block inside it. It seems OK, and I get a result or error, but I have a problem with this, If I want to cancel progress on my async function only I canceled the publisher.
I used below the extension and I learned on https://tanaschita.com/20220822-bridge-async-await-to-combine-future/
extension Future where Failure == Error {
convenience init(taskPriority: TaskPriority? = nil, asyncFunc: @escaping () async throws -> Output) {
self.init { promise in
Task(priority: taskPriority) {
do {
let result = try await asyncFunc()
promise(.success(result))
} catch {
promise(.failure(error))
}
}
}
}
}
and write two helper functions for saving data into core data (for example):
func saveIntoCoreData() throws {
throw DatabaseError.canNotSave
}
func doSomeThing() async throws -> String {
return try await withCheckedThrowingContinuation { continuation in
do {
Thread.sleep(forTimeInterval: 5)
try saveIntoCoreData()
continuation.resume(with: .success("Result"))
} catch {
continuation.resume(with: .failure(error))
}
}
}
and finally, I called these methods with Future and I expected to get a cancel error and stop running for saving data into core data but just got a canceled event from the publisher, and data was saved into core data.
cancelable = Future(taskPriority: .userInitiated) { [weak self] in
return try await self?.doSomeThing()
}.eraseToAnyPublisher()
.sink { completion in
switch completion {
case .finished:
print("Finished")
case .failure(let error):
print(error.localizedDescription)
}
} receiveValue: { value in
print("result: \(value)")
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.cancelable?.cancel()
}
I used try Task.checkCancellation()
but the task is not canceled and did not work.
Does anyone have an idea to stop the Task?
Futures can't be canceled, but Publishers can.
Use Ian Keen's AnyPublisher extension
Then you can:
extension AnyPublisher where Failure == Error {
init(taskPriority: TaskPriority? = nil, asyncFunc: @escaping () async throws -> Output) {
self.init { subscriber in
let task = Task(priority: taskPriority) {
do {
subscriber.send(try await asyncFunc())
subscriber.send(completion: .finished)
} catch {
subscriber.send(completion: .failure(error))
}
}
return AnyCancellable { task.cancel() }
}
}
}