Hi guys,
I have watched all swift concurrency related WWDC videos, played with a lot of code, and upgraded many of my legacy packages to use async/await successfully.
There is one missing piece that I am struggling with, and I am looking for a "best practice" kind of answer.
Imaging an API like Network.framework
or AVCaptureSession
. Or any other component that does some background work on its workerQueue
(also used for internal synchronisation) and that requires that API consumers to specify a callbackQueue
where to invoke delegate methods.
Typically the workerQueue
is created inside the class.
The callbackQueue
needs to be specified externally.
The AVCaptureSession
does this for example via setSampleBufferDelegate(_:queue:) | Apple Developer Documentation.
The Network.framework
does this for example via start(queue:) | Apple Developer Documentation.
Now I have been following this pattern myself for years and it works great.
Imagine now that you want to create an async/await wrapper around these existing classes. You will use the nice withCheckedThrowingContinuation
to invoke a call and resume the continuation on the delegate callback.
All working nice.
But there is still the callbackQueue
that needs to be specified. But where should that come from?
Looks like it does not matter much for the async variants, so I can just create a serial queue internally, use it for callbacks, and hide it from the async API consumer.
Or should I expose it to the API caller? Looks to me like a newcomer async/await person does not know what to do with this?
Here is an example server code (that I am not sure if it is valid) that accepts incoming connections and receives packets from the connection. Implemented via AsyncStream
.
let server = Server(preferredPort: 1234)
let callbackQueue = DispatchQueue(label: "serverCallbackQueue")
try await server.start(queue: callbackQueue)
Task {
for await x in server.peers {
// handle incoming peer connection
// this code will be executing on the callbackQueue
Task {
for await packet in x.packets {
// process incoming packet data
// this code will also be executing on the callbackQueue
}
// peer disconnected
}
}
}
The imaginary API requires me to specify the callbackQueue
although from the pure Swift Concurrency perspective I feel this should not be required.
So the question is:
What is the best practice how to bridge Swift Concurrency world with the existing APIs that require callback dispatch queue?
thanks for any recommendations, clarifications
Martin