Good morning!
I've been going through my projects code base trying to adapt some of its callback/delegation-based API's to Swift Concurrency world using AsyncStreams.
Looking at the documentation it seemed like the perfect choice AsyncStream | Apple Developer Documentation
especially the first paragraph where it states that:
Blockquote
In particular, an asynchronous stream is well-suited to adapt callback- or delegation-based APIs to participate withasync
-await
.
But while investigating this further I noticed that AsyncStream doesn't really work the same way that a callback or delegate would - on a fundamental level.
Having a hypothetical method in Foo
class and a Bar
class as the delegate
or callback
consumer
func foo() {
...
delegate.bar(value) or callback(value)
...
}
Both delegate and callback will notify the Bar
class synchronously. So the Foo
class will give the control over to delegate that will then call the bar
method. Once the work there is done, the Foo
class will regain control and continue with whatever work there is to be done there.
I'm trying to understand how AsyncStream fits into situations like the one described above when there is some additional work to be done on the calling side. Maybe I need to update some model data in Bar
class - refresh views, reload data etc. before I pop/dismiss the top Foo view
.
It seems like this order of operations is no longer possible using AsyncStreams since elements produced using yield
are not delivered immediately
func foo() {
...
continuation.yield(value) <--- locally stored continuation, registers value
...
}
foo return
<---- Bar consumes the value via stream
It seems to me that the quote from apple docs, the one I posted at the top, is false because those concepts are fundamentally different. They work differently. With callback/delegate I have full control over how and when it'll be called and delivered whereas the AsyncStream is fire-and-forget type of thing.
I'm not sure how those could be equivalent? It's possible that I'm missing something. This new Concurrency model is still a bit of a mystery to me so maybe someone could explain this to me?
It feels like for those concepts to be somewhat similar yield()
should be suspending, switching to task related to stream observation, producing an element for the consumer and giving back control to the caller when element is consumed.
Maybe there is a way to actually achieve that?