MainActor and NSInternalInconsistencyException: 'Call must be made on main thread'

Hello,

When attempting to assign the UNNotificationResponse to a Published property on the main thread inside UNUserNotificationCenterDelegate's method

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async

both Task { @MainActor in } and await MainActor.run are throwing a NSInternalInconsistencyException: 'Call must be made on main thread'.

I thought both of them were essentially doing the same thing, i.e. call their closure on the main thread. So why is this exception thrown? Is my understanding of the MainActor still incorrect, or is this a bug?

Thank you


Note: Task { await MainActor.run { ... } } and DispatchQueue.main.async don't throw any exception.

Hi,

I just ran into the same issue. However our app still crashed when the delegate method was empty and the user tapped on the notification on the Lock Screen while the app was in the foreground.

For us switching back to the old completion based delegate method solved the issue.

After a little bit of digging we found a proper solution for the problem.

You need to mark the delegate method with @MainActor like:

@MainActor
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {

    // Do your stuff

}
11

Deleted

This is pretty weird.

While the synchronous variant with completionHandler gets called correctly on Main thread with a FrontBoardServices → UIScene call stack, the async variant get called on a random thread via swift_job_runImpl, assuming some behind-the-scene magic related to Concurrency causing this call to be…Tasked? Or anything calling it outside of Main thread.

It affects us by causing crashes in production.

We can temporarily fix this by downgrading to the synchronous variant with completion block, but I guess this should be escalated because it's causing issues from somewhere within the OS stack as the actual crash occurs in [UIApplication _updateSnapshotAndStateRestorationWithAction:windowScene:] which has to be somewhere around the thread-splitting moment, causing both our delegate call and the UIApplication call scheduled outside of Main thread.

As this protocol is realated to UIKit-based protocol in the first place (even tho legacy one in Objective-C code base), it should be carefully held on Main thread to be in sync with @MainActor requirements of UIKit stuff.

Cc @eskymo

MainActor and NSInternalInconsistencyException: 'Call must be made on main thread'
 
 
Q