Remember when you first learned about NotificationCenter? That moment when you realized you could make different parts of your app communicate without tight coupling felt like discovering a secret passage in a maze. For years, we’ve been comfortable with the familiar dance of addObserver and removeObserver, posting notifications with userInfo dictionaries, and managing observation lifecycles manually. But Apple has been quietly orchestrating a revolution that's about to change everything.
The introduction of async messaging capabilities and enhanced observation patterns in NotificationCenter represents more than just syntactic sugar — it’s a fundamental shift toward safer, more expressive inter-component communication. And if you’re still writing notification code the old way, you’re missing out on some genuinely game-changing improvements.
If you’ve spent any time building apps for Apple platforms, chances are you’ve used NotificationCenter. For years, it’s been the silent backbone of decoupled messaging in UIKit, SwiftUI, and beyond. But now, with the introduction of new types like AsyncMessage, MainActorMessage, MessageIdentifier, and ObservationToken, Apple is making NotificationCenter smarter, safer, and—yes—more Swift.
Let’s break down what’s new, why it matters, and what it tells us about the future of Foundation.
#Why Now? The Legacy of NotificationCenter
Before we dive into the new types, it’s worth asking: Why did NotificationCenter need an update in the first place?
In a world increasingly dominated by structured concurrency and Swift’s emphasis on type safety, the old Notification.Name system was starting to feel brittle. Notifications were loosely typed. Observers could leak. And in multi-threaded contexts, ensuring the right delivery timing was more art than science.
Apple saw the cracks forming — and instead of patching them, it laid new foundations.
#Enter AsyncMessage: Modern Messaging in a Concurrency-First World
At the heart of the redesign is AsyncMessage, a new protocol that defines messages sent asynchronously via NotificationCenter. Think of it as a type-safe wrapper around what used to be a loosely defined blob of data.
Instead of posting notifications with string-based names and user info dictionaries, AsyncMessage lets you define structured messages with clear payload types:
struct MyMessage: AsyncMessage {
let userID: String
}
This fits like a glove with Swift’s `async/await` syntax:
for await message in NotificationCenter.default.notifications(of: MyMessage.self) {
// Handle message.userID
}
No more juggling selectors or wondering if your observer is still retained. It’s just Swift code — modern, elegant, and easy to reason about.
#MainActorMessage: Guarantees You Can Count On
If you’ve ever been burned by UI updates on background threads (and who hasn’t?), you’ll love MainActorMessage.
This is a marker protocol — messages that conform to it are guaranteed to be delivered on the main thread. No need to manually dispatch to the main queue anymore. If you’re using messages to update SwiftUI views, animate UIKit components, or trigger UI feedback, this safety net is invaluable.
Here’s how it works:
struct ToastMessage: MainActorMessage {
let title: String
let duration: Double
}
And then somewhere in your SwiftUI view model:
Task {
for await message in NotificationCenter.default.notifications(of: ToastMessage.self) {
showToast(title: message.title)
}
}
It’s a subtle feature, but one that shows Apple’s increasing dedication to making the right thing easy and the wrong thing hard.
#MessageIdentifier: The End of Magic Strings
Old Notification Center usage was plagued by this problem:
NotificationCenter.default.post(name: Notification.Name("UserLoggedIn"), object: nil)
That "UserLoggedIn" string? No autocomplete. No compiler help. No refactoring support. A typo waiting to happen.
Now enter MessageIdentifier. This new type provides a namespaced, type-safe way to refer to messages. You can define it like this:
enum AppMessages {
static let userLoggedIn = MessageIdentifier(UserLoggedInMessage.self)
}
Then use it consistently throughout your app:
NotificationCenter.default.post(AppMessages.userLoggedIn,
UserLoggedInMessage(userID: "123"))
This might remind you of SwiftUI’s approach to identifiers and preferences. The benefits are obvious: autocompletion, fewer bugs, and clarity around what messages exist.
#ObservationToken: Because Clean-Up Still Matters
Memory leaks from forgotten observers used to be a rite of passage. You’d add an observer, forget to remove it, and spend an afternoon tracking a retain cycle.
With ObservationToken, Apple introduces an explicit token you can cancel when you're done observing:
let token = NotificationCenter.default.observe(UserLoggedInMessage.self) { message in
// Handle login
}
// Later, when needed
token.cancel()
Yes, structured concurrency with for await often handles cancellation automatically. But sometimes you need manual control—and now you have it, cleanly and explicitly.
#Real-World Use Case: Improving Onboarding Flows
Imagine you’re building a health app that collects user info, connects to HealthKit, and shows a dashboard. During onboarding, multiple steps need to coordinate — profile creation, permission prompts, and first-time setup.
With the new Notification Center, you can define a set of typed messages like:
struct ProfileCreated: AsyncMessage { let userID: String }
struct HealthKitPermissionGranted: AsyncMessage { }
struct OnboardingComplete: MainActorMessage { }
Each step can post its own message. Observers downstream can react only when all prerequisites are met:
for await profile in NotificationCenter.default.notifications(of: ProfileCreated.self) {
print("Created profile for user \(profile.userID)")
}
for await _ in NotificationCenter.default.notifications(of: HealthKitPermissionGranted.self) {
// Continue onboarding
}
This kind of modularity wasn’t impossible before — but now, it’s idiomatic.
#Backwards Compatibility and Adoption Strategy
One big question: Do I need to rewrite my NotificationCenter usage right away?
Short answer: No.
The legacy APIs still work. But for new code — or when you’re refactoring a module — it’s worth switching. Start small: define a couple of AsyncMessage types for internal signals. Use MainActorMessage for UI updates. Adopt MessageIdentifier for message routing in complex flows.
Over time, your Notification Center codebase will become more testable, more readable, and less error-prone.
#What This Says About Swift’s Direction
This Notification Center revamp is more than just a technical update. It reflects broader trends in Apple’s ecosystem:
- Structured Concurrency is the default, not a side option.
- Type safety is king. Loosely typed patterns are being phased out across the board.
- The compiler is your co-pilot. With identifiers, actor semantics, and strict typing, Swift is increasingly about eliminating entire classes of bugs at compile time.
Apple is modernizing Foundation methodically. These changes, like those to URLSession, Codable, and Observation, all follow a common vision: safe, expressive APIs that feel native to Swift.
#Final Thoughts: A Small Change With Big Ripples
The changes to NotificationCenter won’t make flashy headlines. There are no colorful animations or dramatic keynotes. But if you care about building reliable, maintainable apps, these updates matter — a lot.
They represent the quiet evolution of Apple’s core APIs toward a more Swifty, concurrent, and declarative future.
So the next time you reach for a notification to trigger an event, pause for a second. Define a message type. Add a protocol conformance. Use for await.
It may feel like a small shift — but it’s the kind of shift that adds up to better apps, happier teams, and fewer bugs down the road.
About the Author
Manav is an iOS developer passionate about clean architecture, platform evolution, and writing code that lasts. He builds health and wellness apps at scale and occasionally writes to demystify the fast-moving world of Apple frameworks.
What’s New in NotificationCenter: A Closer Look at Apple’s Quiet Revolution was originally published in Atlys Engineering on Medium, where people are continuing the conversation by highlighting and responding to this story.
