About Notification center messages (SF-0011)

Great changes to the NotificationCenter API. Now, instead of the abstract Notification, we can define a Sendable type and use it to post and observe messages. The message type must conform to MainActorMessage or AsyncMessage protocol. The first is for time-critical messages. The second is not tied to the main thread and supports AsyncSequence (see examples below).

Post continues below.

#swift

And the most important thing here is that we can use this API to subscribe to any notification available in Apple SDK. Finally, it will be possible to leave the notification name, userInfo dictionary and its keys in the past.

To find out more, follow the links below:
Documentation - https://developer.apple.com/documentation/foundation/notification-center-messages
WWDC link - https://developer.apple.com/videos/play/wwdc2025/245/?time=645
SF-0011 proposal - https://github.com/swiftlang/swift-foundation/blob/main/Proposals/0011-concurrency-safe-notifications.md

Notification center messages | Apple Developer Documentation

Use Foundation’s notification center with Swift concurrency.

Apple Developer Documentation

@romanli It looks like you can just drop a MainActorMessage observationToken, and that will cancel the observation? So putting it in an instance variable, means when the observer deallocates, it's cleaned up. So that's good.

In the AsyncMessage example,I'm not seeing how you can ensure that the Task is canceled when the observer is deallocated (or that it would self-unregister on the next notification posting like the old ObjC interface). Is that possible?

@cocoaphony So that's good.
>>> Agreed 🙂.

I'm not seeing how you can ensure that the Task is canceled when the observer is deallocated
>>> ```deinit { observationTask.cancel() }```, I supose. ... or subscribe in the following way:
```swift
self.observationToken = NotificationCenter.default.addObserver(for: MyAsyncMessage.self) { message in // <- async closure
print(message.text)
}
```

@romanli generally it’s very hard to call that cancel in deinit because of Sendable restrictions. Eventually that’s supposed to be fixed, but currently it’s made it very hard to safely consume async sequences that don’t terminate (like notifications).

But the second option is good to see, if the closure can be async. (That’s the thing that usually gets you; closures that need to be async but don’t support it.)