Push notifications are a highly effective way to re-engage your customers, inviting them to come back into your app due to new content, a sale, or other reasons. For my Ambie White Noise app, push notifications directly led to higher revenue and an increase in monthly active users (MAU). I recently had to rebuild Ambie’s push notification infrastructure, and so I wanted to share my architecture here.
How it started
Historically, it was very easy to send push notifications for UWP apps thanks to built-in integration in Microsoft’s Partner Center developer dashboard. In fact, you’ll still find documentation here. I used this notification dashboard for the last few years, and I consistently saw revenue for every notification I sent to Ambie users. It was extremely effective.
Unfortunately, I saw that the notifications page on Partner Center was removed in November 2024. And as expected, my revenue and MAU numbers dropped in November. So, I prioritized building my own notification system.
What I tried
Initially, I tried using Azure Notification Hub. This product is basically a replacement to Partner Center, albeit more barebones. Partner Center was simple to use since all you had to provide was some strings and URLs to images, and then it would translate the strings automatically. Moreover, the registration process was fully handled by the Partner Center SDK. With Azure Notification Hub, you had to handle the translation yourself. But more importantly, it just didn’t work for me. After setting things up, I attempted to send toast notifications to my devices and it just failed mysteriously. So I decided to go back to basics.
Windows Notification Service (WNS)
Windows Notification Service is the foundational service provided by Microsoft and Windows to send notifications to devices. Azure Notification Hub was actually an abstraction layer that attempted to work with WNS on your behalf (and also work with other platforms, such as Apple and Google). But there’s nothing preventing developers from interacting directly with WNS, so that’s what I tried. The result was magic.
Final Architecture
Ambie’s new notification system involves 3 main components:
- The Ambie White Noise app, built with the high performance UWP framework
- Windows Notifications Service (WNS)
- My new Ambie Notifications server
This architecture is actually recommended by the WNS documentation. The component that I’m most proud of is my notification server, built with a microservice architecture in mind.
First, the registration step works with Ambie communicating with WNS to establish a notification URI. This URI is what we actually use to send a notification to that specific device. So you can imagine that in order to send notifications, we need to save this URI in a central location. That’s where my new server comes in.
Ambie submits the URI to my service bus. The service bus is designed to decouple the registration process between the app and the server. From the app’s perspective, it’s a fire-and-forget operation.
The Azure Function has a Service Bus trigger that will cause it to wake up when new messages appear in the queue. Once woken, it will write save the registration details to my Cosmos DB database.
To send a notification, I simply send a payload via Nightingale to the Azure Function. A different function with an HTTP trigger will be woken up and it will translate the payload, generate the toast XML content, and use all the URIs stored on Cosmos DB to send notifications.
It all sounds simple now, but I admit it took some effort to design, built, test, and iterate this whole architecture. I now have a notification system that is more reliable and more scalable than any system I’ve used before. Best of all, it’s essentially 100% free.
Azure Functions has a generous free tier. Cosmos DB also has a free tier. Service Bus also has a free tier, with a threshold that I doubt I will surpass.
What’s Next
At the moment, the entire system is built especially for Ambie. But I can already see a way to generalize this system. If people are interested, I may open source the client and service code, allowing developers to deploy this system for their own UWP apps. Would you be interested? Let me know.