Between AsyncAlgorithms and AsyncExtensions I've been able to replace most usage of Combine with Swift Concurrency.
But the one thing that keeps me coming back to Combine is when I need a value to be immediately available. Consider this example (substitute any publisher and action):
class Coordinator {
@Published var name = "John"
@Published var nameCharacterCount = 0
var nameObserver: AnyCancellable?
init() {
nameObserver = $name
.map { $0.count }
.sink { [weak self] count in
self?.nameCharacterCount = count
}
}
}
The sink callback will immediately be invoked and nameCharacterCount will have the most up to date value from the very beginning.
However if you were to implement this with swift concurrency:
class Coordinator {
@Published var name = "John"
@Published var nameCharacterCount = 0
var nameObserver: Task<Void, Never>?
init() {
let sequence = $name.values
nameObserver = Task { [weak self] in
for await name in sequence {
self?.nameCharacterCount = name.count
}
}
}
deinit {
nameObserver?.cancel()
}
}
There would be a slight initial pause because the Task closure would not fire until the next tick of the run loop.
From my understanding, if you are already in an async context and there is a value ready to return immediately from a sequence, there is no delay with the first iteration of the loop. But jumping to an async context always enforces a delay.
Would it be possible to add some kind of way to create a Task that fully inherited the current running context? Something like Task.attached
, that would run immediately and only return from the initializer once it encountered an actual await
pause?