Platform Setup
A push only arrives when three pieces agree: the platform (Apple, Google, Microsoft, the browser) knows your app,
the app registers with it and hands its token to your server, and the server holds the credentials to send.
This page walks through all three for every platform Shiny.Push supports.
| Platform | Platform console | App | Server |
|---|---|---|---|
| iOS / macOS (APNs) | Apple Developer | Shiny.Push |
AddApns |
| Android (FCM) | Firebase | Shiny.Push |
AddFcm |
| iOS via Firebase | Firebase + Apple Developer | Shiny.Push.FirebaseMessaging |
AddApns with the native token |
| Windows (WNS) | Partner Center | Shiny.Push |
AddWns |
| Blazor WebAssembly (Web Push) | none (VAPID keys) | Shiny.Push |
AddWebPush |
| Azure Notification Hubs | Azure portal + the stores above | Shiny.Push.AzureNotificationHubs |
Azure Notification Hubs SDK |
The server column is the Shiny.Extensions.Push library, which sends to APNs, FCM, WNS and Web
Push directly. You can use any backend instead: the app only needs to send its token somewhere.
Send the token to your server
Section titled “Send the token to your server”Every platform ends the same way: IPushDelegate.OnNewToken hands you a token, and the app sends it to your server.
Do it from the delegate so token rotation is covered, not only the first registration:
public class MyPushDelegate(IMyApi api) : PushDelegate{ public override Task OnNewToken(string token) => api.RegisterPushToken(new { DeviceToken = token,#if IOS || MACCATALYST Platform = "iOS",#elif MACOS Platform = "MacOS",#elif ANDROID Platform = "Android",#elif WINDOWS Platform = "Windows",#else Platform = "WebBrowser",#endif#if DEBUG Environment = "Sandbox", // debug builds get APNs sandbox tokens#else Environment = "Production", // App Store and TestFlight builds get production tokens#endif DeviceId = myInstallId, // a GUID you persist once per install UserIdentifier = currentUserId });
public override Task OnUnRegistered(string token) => api.UnregisterPushToken(token);}On the server, turn that into a DeviceRegistration, which says what each
platform’s token looks like and how Web Push subscriptions are split up.
Apple (APNs)
Section titled “Apple (APNs)”Covers iOS, Mac Catalyst, macOS and tvOS. Requires a paid Apple Developer Program membership.
Apple Developer portal
Section titled “Apple Developer portal”-
Enable push on the App ID. Open Certificates, Identifiers & Profiles → Identifiers, select your App ID (it must match
ApplicationIdin your.csproj), tick Push Notifications and save. -
Regenerate your provisioning profiles. Profiles created before you enabled push don’t carry the capability. Re-download them under Profiles, or let automatic provisioning refresh them.
-
Create an APNs auth key for the server. See APNs → Apple Developer setup. One
.p8key works for every app on the team, in both sandbox and production.
-
Add the entitlement.
aps-environmentmust bedevelopmentfor debug builds andproductionfor release builds:<ItemGroup Condition="$(TargetFramework.Contains('-ios')) OR $(TargetFramework.Contains('-maccatalyst'))"><CustomEntitlements Include="aps-environment" Type="string" Value="development" Condition="'$(Configuration)' == 'Debug'" Visible="false" /><CustomEntitlements Include="aps-environment" Type="string" Value="production" Condition="'$(Configuration)' == 'Release'" Visible="false" /></ItemGroup>Signing fails with a profile that doesn’t have the Push Notifications capability, such as a wildcard profile.
-
Enable background delivery. Add
remote-notificationtoUIBackgroundModesinPlatforms/iOS/Info.plistso silent pushes (content-available: 1) wake the app:<key>UIBackgroundModes</key><array><string>remote-notification</string></array> -
Register Shiny.Push with
services.AddPush<MyPushDelegate>()(Getting Started), callIPushManager.RequestAccess(), and send the token to your server. -
Test on a physical device. The simulator can receive pushes but can’t register for a real APNs token.
Server
Section titled “Server”Configure AddApns with the Team ID, Key ID, bundle id and .p8 key. Register each device with
Platform = iOS (or MacOS) and the Environment the app reported: debug-signed builds get sandbox tokens, while
App Store and TestFlight builds get production tokens, and the two aren’t interchangeable.
For the payload to reach IPushDelegate.OnReceived in the background, send ContentAvailable = true (see the
FAQ).
Android (Firebase Cloud Messaging)
Section titled “Android (Firebase Cloud Messaging)”Firebase console
Section titled “Firebase console”-
Create or open a project in the Firebase console.
-
Add the Android app. In Project settings → General, click Add app → Android and enter your
ApplicationIdas the package name. -
Download
google-services.jsonfrom the same page. This is the app’s configuration, and it isn’t secret. -
Create a service account key for the server. See FCM → Firebase setup. That key is secret and never goes in the app.
-
Add
google-services.jsontoPlatforms/Androidwith build actionGoogleServicesJson, or pass aFirebaseConfigwith the values instead (Getting Started). -
Add the tap intent filter to
MainActivitysoIPushDelegate.OnEntryfires when a notification is tapped (Getting Started). -
Register Shiny.Push with
services.AddPush<MyPushDelegate>(), callIPushManager.RequestAccess()(it also asks for the Android 13+ notification permission), and send the token to your server.
Server
Section titled “Server”Configure AddFcm with the service account JSON and register each device with Platform = Android.
Two payload rules decide which delegate method fires:
OnReceivedonly fires for messages with a data section, so put something inPushNotification.Data.PushNotification.DeepLinkis sent as the Androidclick_action. The tap only reachesOnEntryif that matches the intent filter (SHINY_PUSH_NOTIFICATION_CLICKby default), so for Shiny apps put deep links inDatainstead.
iOS via Firebase
Section titled “iOS via Firebase”Use this when you want FCM tokens and topics on iOS as well as Android. Android setup is unchanged.
Firebase console and Apple Developer portal
Section titled “Firebase console and Apple Developer portal”-
Do the Apple Developer portal steps above. Firebase still delivers through APNs.
-
Add the iOS app. In Project settings → General, click Add app → iOS and enter your bundle id. Download
GoogleService-Info.plist. -
Give Firebase your APNs key. Under Project settings → Cloud Messaging → Apple app configuration, upload the
.p8APNs auth key with its Key ID and Team ID.
Add GoogleService-Info.plist with build action BundleResource, add the entitlement and background mode from the
Apple section, and register with services.AddPushFirebaseMessaging<MyPushDelegate>(). See Firebase (iOS).
Server
Section titled “Server”OnNewToken receives the FCM token on iOS. The Shiny.Extensions.Push FCM transport only delivers to
Platform = Android registrations, so either send IPushManager.NativeRegistrationToken (the APNs token) as an
iOS registration and use AddApns, or send the FCM token through your own FCM sender.
Windows (WNS)
Section titled “Windows (WNS)”Partner Center
Section titled “Partner Center”-
Reserve the app name in Partner Center → Apps and games. WNS only issues channels to apps with a Store identity.
-
Associate the project with the Store. In Visual Studio, right-click the project → Publish → Associate App with the Store, or copy the identity from the app’s Product identity page into
Platforms/Windows/Package.appxmanifest. The app must run packaged (MSIX).
Register with services.AddPush<MyPushDelegate>(). On Windows, RequestAccess() creates a channel with
PushNotificationChannelManager, and the token is the channel URI. Send it to your server.
Server
Section titled “Server”Configure AddWns and register each device with Platform = Windows.
Web Push (Blazor WebAssembly)
Section titled “Web Push (Blazor WebAssembly)”There’s no store console. You generate your own VAPID key pair, and the browser vendor’s push service delivers to whatever server holds the private key.
-
Generate a VAPID key pair once, for example with the
web-pushCLI:Terminal window npx web-push generate-vapid-keys -
Keep the private key on the server only. The public key goes in the app.
-
Register with the public key:
services.AddPush<MyPushDelegate>(new WebPushOptions{VapidPublicKey = "<vapid-public-key>"}); -
Serve the service worker. The bundled worker is served from
./_content/Shiny.Push/push-sw.js(the defaultServiceWorkerPath). If you already have a service worker, setServiceWorkerPathto yours andimportScripts('./_content/Shiny.Push/push-sw.js')from it. -
Serve over HTTPS (
localhostis fine for development). Safari only allows Web Push after the user installs the site as a PWA. -
Send the token. On the web the token is the browser’s whole
PushSubscriptionserialized as JSON.
Server
Section titled “Server”Configure AddWebPush with the same key pair and a contact Subject. The subscription JSON
is split into a DeviceRegistration: endpoint becomes DeviceToken, and keys.p256dh and keys.auth go in
Data. See Client Apps → Web Push. The bundled service worker shows
title, body and icon and passes Data to your delegate, so put deep links in Data.
Azure Notification Hubs
Section titled “Azure Notification Hubs”Azure Notification Hubs sits between your server and the stores: the app registers an installation with the hub, and your server sends to the hub by tag. Supported on iOS, Android and Windows.
Store credentials
Section titled “Store credentials”Do the platform console steps above first, for every platform you target:
- Apple: the App ID push capability and an APNs
.p8key (how). - Android: the Firebase project,
google-services.json, and a service account key (how). - Windows: the Partner Center association, plus the Package SID and client secret from the app’s WNS settings.
Azure portal
Section titled “Azure portal”-
Create a Notification Hub (and its namespace) from Create a resource → Notification Hub.
-
Add the store credentials under the hub’s Settings:
- Apple (APNS): choose Token, enter the Key ID, bundle id, Team ID and the contents of the
.p8file, and pick Sandbox or Production to match your build. - Google (FCM v1): enter the private key, client email and project id from the service account JSON.
- Windows (WNS): enter the Package SID and security key.
- Apple (APNS): choose Token, enter the Key ID, bundle id, Team ID and the contents of the
-
Copy the connection strings from Manage → Access Policies. The app uses
DefaultListenSharedAccessSignature, which can only register. The server usesDefaultFullSharedAccessSignature, which can send, and must never ship in the app.
Microsoft’s Notification Hubs documentation has screenshots of each step.
Do the app steps for each platform above (entitlements, google-services.json, Store association), then register with the
listen connection string:
services.AddPushAzureNotificationHubs<MyPushDelegate>( "Endpoint=sb://<namespace>.servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=<key>", "<hub-name>");OnNewToken receives the hub installation id, not the native token. Tags set through IPushManager.Tags are stored
on the installation. See Azure Push Notifications.
Server
Section titled “Server”Shiny.Extensions.Push doesn’t talk to Notification Hubs. Send with Microsoft’s
Microsoft.Azure.NotificationHubs package and the
full connection string:
var hub = NotificationHubClient.CreateClientFromConnectionString(fullAccessConnectionString, "<hub-name>");
// to every installation tagged "premium"await hub.SendAppleNativeNotificationAsync("{\"aps\":{\"alert\":\"Hello\"}}", "premium");await hub.SendFcmV1NativeNotificationAsync("{\"message\":{\"data\":{\"message\":\"Hello\"}}}", "premium");
// to one device, using the installation id the app reported from OnNewTokenawait hub.SendAppleNativeNotificationAsync(payload, $"$InstallationId:{installationId}");

