A global actor MainActor
will provide the ability to ensure that something executes on the main thread (I think we've also called it UIActor
in some places... MainActor
is the better name). You can decorate any function with @MainActor
to ensure that it will execute on the main thread, so let's add such a method to note when the pass was signed:
extension ViewController {
@MainActor func passWasSigned(signed: Data, signature: Data) {
self.isPassSigned = true
// set some label in the UI, on the main queue
}
}
Now our state update is guaranteed to happen on the main thread, so let's write viewWillAppear
. I noted that the PKPassLibrary API you used follows the Objective-C conventions for completion-handler methods, so it is automatically imported as async
based on SE-0297 that is also under review now. So, let's immediately jump into async-land by creating a detached task in viewWillAppear
:
extension ViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
Task.runDetached {
do {
let (signed, signature) = await try library.sign(data, using: pass)
await passWasSigned(signed: signed, signature: signature) // okay, hops over to the main actor to do its work
} catch {
// tell the user something went wrong, I hope
}
}
}
}
Now, @asyncHandler
is mean to help with exactly these cases. It essentially runs the body of the function in an implicitly-created detached task on the given actor, so we can eliminate some boilerplate:
extension ViewController {
@MainActor @asyncHandler
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
do {
let (signed, signature) = await try library.sign(data, using: pass)
// hops back to @MainActor now
isPassSigned = true
// set some label in the UI, on the main actor
} catch {
// tell the user something went wrong, I hope
}
}
}
Heh, I appreciate your faith in us, but let's see how the above stacks up ;)
Doug