Context:
Consider this code, where an object subscribes to a notification and handles it on the main queue:
@MainActor
final class ModelController
{
init()
{
NotificationCenter.default.addObserver(forName: .appTerminationRequestedNotification, object: nil, queue: .main) { note in
Task { @MainActor in
print("calling task closure.")
}
}
}
}
This Task
never fires. The notification closure is called correctly, but the Task
itself appears to be skipped.
(Even though we've explicitly told the notification handler to run on the main queue, the compiler can't reason about that, so it's necessary to redundantly dispatch to the MainActor when you must access state.)
A Working Alternative:
This works just fine:
NotificationCenter.default.addObserver(forName: .appTerminationRequestedNotification, object: nil, queue: .main) { note in
MainActor.assumeIsolated {
print("This works just fine.")
}
}
The notification is posted here. The app in question is a SwiftUI Lifecycle app using @NSApplicationDelegateAdaptor(AppDelegate.self)
/// Posed when the user attempts to quit the app so that ModelController can gracefully close a database.
static let appTerminationRequestedNotification = Notification.Name("com.example.Foo.appTerminationRequested")
class AppDelegate: NSObject, NSApplicationDelegate
{
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply
{
NotificationCenter.default.post(name: .appTerminationRequestedNotification, object: self)
return .terminateLater
}
}
Question:
Why? I've used Task { @MainActor in }
for other notification handlers and have never had an issue. I'm trying to determine if this behavior is new in macOS 26 or is somehow related to the termination flow, etc.