Resuming the discussion from SE-0296: async/await - #185 by Lantua.
I'm asking how to expect to rewrite an extremely common existing pattern: non-concurrent asynchronous code using completion handlers, including (possibly) thread-hopping via DispatchQueue.async on serial queues.
Here's a simple example:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
observer = NotificationCenter.default.addObserver(
forName: UIApplication.didBecomeActiveNotification,
object: nil,
queue: .main) { _ in
// do something on main thread
}
}
}
The closure body is here executed on the main thread, which makes it possible to reason about mutation of instance variables.
Here's an uglier version of the same pattern:
class ViewController: UIViewController {
var library: PKPassLibrary! // assume a proper value
var pass: PKSecureElementPass! // assume a proper value
var data: Data! // assume a proper value
var isPassSigned = false
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
library.sign(data, using: pass) { signed, signature, error in
DispatchQueue.main.async {
self.isPassSigned = error == nil
// set some label in the UI, on the main queue
}
}
}
}
Note that making ViewController
an actor is not an option here, nor is changing any of the instance functions of ViewController to be async
.
We're starting in code that Swift will regard as a synchronous environment, and we're trying to bridge to an asynchronous environment without introducing any concurrency (i.e. no parallel execution on separate threads)
It is claimed that this is what actors are for.
Let's start with: how would we write this with actors? (If the answer is good, then my original concern is moot.)