Hello everyone!
I stumbled upon an async
issues that seem too straightforward to actually be true.
The problems
Async IBAction crash
If you mark an @IBAction
as async
, the app will crash:
@IBAction func ibActionButtonPressed() async {
// Crash when called
}
The exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[AsyncIBActionCrash.ViewController ibActionButtonPressed]: unrecognized selector sent to instance 0x7fae38e0c180'
Async notification crash
If a notification is called that is linked to a selector which is marked as async
, the app will crash:
func setup() {
NotificationCenter.default.addObserver(
self,
selector: #selector(asyncFunction),
name: Notification.Name("AsyncNotification"),
object: nil
)
}
func callNotification() {
NotificationCenter.default.post(name: Notification.Name("AsyncNotification"), object: nil)
}
@objc func asyncFunction() async {
// Crash when called from notification
}
The exception:
EXC_BAD_ACCESS (code=1, address=0x8)
performSelector
crash
Lastly, the app also crashes if a performSelector
is called on a selector that is marked as async
:
func callPerformSelector() {
performSelector(onMainThread: #selector(asyncFunction), with: nil, waitUntilDone: false)
}
@objc func asyncFunction() async {
// Crash when called from performSelector
}
The exception:
EXC_BAD_ACCESS (code=1, address=0x10)
The environment
- Xcode 13.4.1
- Swift 5.6.1
- iOS SDK 15.5
- Crashes happen on both simulator and on a physical device
Discussion
The workaround for these are quite easy – one must provide a wrapper function so that the @IBAction
, notification or performSelector
do not call the async
function directly. E.g., with @IBAction
crash:
@IBAction func ibActionButtonPressed() {
Task {
await asyncFunction()
}
}
func asyncFunction() async {
// No crash
}
However, the main problem is probably even not that we cannot make these functions async
but that nothing complains about it – analyzer is silent, compiler is silent – the issue is only discovered on runtime (and in the case of notification or performSelector
it is difficult to understand why it even crashed).
I have created a simple project with the issue: GitHub - kkizlaitis/AsyncCrashes: A simple project with multiple async crash issues