Delegate to AsyncStream transition


I have an architectural question.

Let's say you have a legacy URLSession API that is delegate-based. We have a 1:1 relationship. I.e. regardless of how many tasks one has on the session, they will all come back through the delegate, with the only way to differentiate tasks being a check of the id, or task description.

Step 1. To transition to an async-like interface, I can use AsyncStream, as in Apple's example here. Apple Developer Documentation which seems straightforward, so using it as the basis for this example.

Step 2: The real issue, how do I achieve a 1..* relationship? Let's say we have a delegate wrapper - MyAsyncDelegateWrapper. It can have a map of async streams based on some identifier, e.g.

let myMap = [String: AsyncStream<Data>]

I'd add to this every time I create a new task.

I now have my map (the to many bit of the relationship), so all good, but let's say I'm wrapping a delegate function such as

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)

While I can retrieve an element from my map based on dataTask id, how can I "push" to the stream? I don't have access to the continuation, do I have to keep a reference to it somehow?

Is there overall a better way to achieve 2? (Imagine I am constrained by the API I'm wrapping around, so can't use a "newer version")

Also, is there a performance issue with having multiple streams existing simultaneously within an application? Many thanks in advance for your help :heart:

1 Like

There are two ways to do that:

  1. You can keep a reference to continuation in your MyAsyncDelegateWrapper.
  2. You can create a enum for delegate methods and take a closure so that you can push delegate event to asyncstream. e.g.
class MyAsyncDelegateWrapper {
  enum Action {
      case didReceive(Data)
  var onAction: ((Action) -> Void)?

// in the method where request starts
let stream = AsyncStream< MyAsyncDelegateWrapper.Action> { continuation in
  delegate.onAction = { action in