| name | maui-push-notifications |
| description | End-to-end guide for adding push notifications to .NET MAUI apps. Covers Firebase Cloud Messaging (Android), Apple Push Notification Service (iOS), Azure Notification Hubs as the cross-platform broker, an ASP.NET Core backend API, and the MAUI client wiring on every platform. USE FOR: "push notification", "FCM", "APNS", "Firebase Cloud Messaging", "Azure Notification Hubs", "device registration", "remote notification", "send push notification", "notification token". DO NOT USE FOR: local/scheduled notifications (use maui-local-notifications), general permission handling (use maui-permissions), or background tasks without notifications (use maui-app-lifecycle).
|
Push Notifications — Gotchas & Best Practices
Troubleshooting Table
| Issue | Cause | Fix |
|---|
| Token changes every debug run (Android) | Debug builds regenerate Firebase tokens | Always re-register in OnNewToken |
HttpClient requests fail silently | BaseAddress missing trailing / | Ensure endpoint ends with / |
| iOS push won't arrive on simulator | Simulators don't support APNS | Use a physical iOS device |
| No notifications on Android 13+ | POST_NOTIFICATIONS required (API 33+) | Call RequestPermissions in OnCreate |
| Notification channel missing (API 26+) | Android requires explicit channel creation | Create channel before sending |
SendNotificationAsync throws for >20 tags | Azure NH tag expression limit | Batch tags in groups of 20 |
422 on device registration | Platform string mismatch | Use "fcmv1" (not "gcm") and "apns" |
Token empty at RegisterAsync | Race condition on cold start | Guard with IsNullOrWhiteSpace check |
Critical Gotchas
⚠️ BaseAddress trailing slash
HttpClient.BaseAddress must end with /. Without it, relative URI resolution silently breaks.
_http = new HttpClient { BaseAddress = new Uri("https://example.com") };
_http = new HttpClient { BaseAddress = new Uri("https://example.com/") };
⚠️ Android: Always re-register on token refresh
Firebase tokens regenerate frequently in debug builds. If you skip OnNewToken, the backend holds a stale token and pushes silently fail.
public override void OnNewToken(string token)
{
var svc = IPlatformApplication.Current?.Services.GetService<IPushNotificationService>();
if (svc is null) return;
svc.Token = token;
_ = svc.RegisterAsync();
}
⚠️ Android 13+ requires POST_NOTIFICATIONS permission
Without explicitly requesting POST_NOTIFICATIONS on API 33+, notifications are silently dropped.
⚠️ Azure NH tag expression limit: 20 tags
SendNotificationAsync throws if a tag expression references more than 20 tags. Batch into chunks:
var batches = request.Tags.Chunk(20);
foreach (var batch in batches)
{
var tagExpr = string.Join(" || ", batch);
await _hub.SendFcmV1NativeNotificationAsync(payload, tagExpr, ct);
}
⚠️ Use "fcmv1" not "gcm"
The legacy "gcm" platform string causes 422 errors on device registration. Always use "fcmv1" for Android and "apns" for iOS.
⚠️ iOS simulators cannot receive push notifications
APNS only works on physical iOS devices. Simulators silently ignore push registrations.
Platform Checklist
Android
iOS
Backend
Architecture Overview
MAUI app → ASP.NET Core backend → Azure Notification Hub → FCM / APNS → device
See references/push-notifications-api.md for full implementation templates.