Guide for implementing push notifications in .NET MAUI apps using Shiny.Push (native FCM/APNs) and Shiny.Push.AzureNotificationHubs
Use this skill when the user needs to:
| Item | Value |
|---|---|
| NuGet (Native Push) | Shiny.Push |
| NuGet (Azure NH) | Shiny.Push.AzureNotificationHubs |
| Primary Namespace | Shiny.Push |
| Config Namespace | Shiny (extension methods on IServiceCollection) |
| Platforms | iOS (APNs), Android (FCM), WebAssembly (experimental) |
Register in your MauiProgram.cs:
using Shiny;
builder.Services.AddPush<MyPushDelegate>();
On Android, this uses FirebaseConfig with embedded google-services.json by default. To provide Firebase values manually:
#if ANDROID
builder.Services.AddPush<MyPushDelegate>(FirebaseConfig.FromValues(
appId: "your-app-id",
senderId: "your-sender-id",
projectId: "your-project-id",
apiKey: "your-api-key"
));
#else
builder.Services.AddPush<MyPushDelegate>();
#endif
using Shiny;
builder.Services.AddPushAzureNotificationHubs<MyPushDelegate>(
"Endpoint=sb://...;SharedAccessKeyName=...;SharedAccessKey=...",
"your-hub-name"
);
On Android with custom Firebase config:
#if ANDROID
builder.Services.AddPushAzureNotificationHubs<MyPushDelegate>(
"Endpoint=sb://...",
"your-hub-name",
FirebaseConfig.FromValues("appId", "senderId", "projectId", "apiKey")
);
#endif
Always implement IPushDelegate (or subclass PushDelegate) to handle push events. Register it as a generic type parameter on AddPush<T>() or AddPushAzureNotificationHubs<T>().
Request access before using push. Call IPushManager.RequestAccess() and check PushAccessState.Status == AccessState.Available before assuming push is working.
Use PushAccessState.Assert() when you want to throw on denied/restricted permissions rather than checking the status manually.
Multiple delegates are supported. You can register multiple IPushDelegate implementations; all will be called. Use AddShinyService<T>() for additional delegates.
Apple-specific customization:
IPushManager to IApplePushManager for custom UNAuthorizationOptions.IApplePushDelegate (extends IPushDelegate) to control foreground presentation options and background fetch results.PushNotification may be an ApplePushNotification with access to the raw NSDictionary payload.Android-specific customization:
PushNotification received in OnReceived may be an AndroidPushNotification with access to the native RemoteMessage.AndroidPushNotification.CreateBuilder() to build a NotificationCompat.Builder from the push data.AndroidPushNotification.SendDefault(notificationId) for quick notification display.FirebaseConfig.DefaultChannel to set a default NotificationChannel.FirebaseConfig.IntentAction to set a custom intent action for notification taps.Tags/Topics:
IPushManager.Tags != null (or use pushManager.IsTagsSupport()) before using tag operations.TrySetTags, TryGetTags, TryRequestAccessWithTags for safe tag operations.Azure Notification Hubs specifics:
IPushInstallationEvent to modify the Installation object (add templates, tags) before it is sent to ANH.AzureNotificationConfig.BeforeSendInstallation callback as an alternative to IPushInstallationEvent.AzureNotificationConfig.ExpirationTime controls token expiration. Each RequestAccess or tag update bumps expiration.AzureNotificationConfig.AzureAuthenticationWaitTimeMs (default 1000ms) adds a delay after registration to allow ANH propagation.Namespace conventions: Extension methods on IServiceCollection live in the Shiny namespace. All push types live in Shiny.Push.
Do NOT reference platform-specific types (e.g., AndroidPushNotification, ApplePushNotification, IApplePushManager, IApplePushDelegate, FirebaseConfig) in shared/cross-platform code. Guard them with #if ANDROID / #if APPLE preprocessor directives or use runtime platform checks.
Notification: Both Shiny.Push and Shiny.Notifications define a Notification type. If both packages are referenced in the same project, do NOT add both namespaces as global usings. Use Shiny.Push.PushNotification or FQN to disambiguate.OnNewToken in your delegate to sync the updated token with your backend server.OnEntry to navigate the user to the appropriate screen when they tap a notification.OnReceived for silent/data-only notifications and background processing. On iOS, ensure content-available: 1 is set in the push payload for background delivery.PushAccessState.Status after RequestAccess() -- do not assume success.RequestAccess().AddPush<TDelegate>() over AddPush() + manual delegate registration to ensure correct service lifetime.AzureAuthenticationWaitTimeMs if you encounter "InstallationId not found" errors.RegistrationToken on IPushManager is the provider-level token (e.g., ANH InstallationId), while NativeRegistrationToken is the raw OS token (FCM token or APNs device token). Use RegistrationToken when communicating with your backend.