.NET MAUI Local Notifications
Implementation overview
- Define cross-platform
INotificationManagerService interface and event args
- Implement Android notification service (channel, AlarmManager, BroadcastReceiver)
- Implement iOS/Mac Catalyst notification service (UNUserNotificationCenter)
- Register platform implementations via DI
- Configure platform permissions and MainActivity
See references/local-notifications-api.md for full implementation code.
Platform gotchas
Android
| Issue |
Fix |
| Notifications silently fail on API 33+ |
Must request POST_NOTIFICATIONS runtime permission first |
PendingIntent crash on Android 12+ |
Must include PendingIntentFlags.Immutable for API 31+ |
| Scheduled notifications lost on reboot |
AlarmManager does not survive device restart β re-schedule on boot via BOOT_COMPLETED receiver |
| No notification appears |
Channel not created β required on API 26+ (Android 8.0) |
| Notification tap doesn't return to app |
LaunchMode = LaunchMode.SingleTop must be set on MainActivity |
// β
Correct β API 31+ requires Immutable flag
var pendingIntentFlags = (Build.VERSION.SdkInt >= BuildVersionCodes.S)
? PendingIntentFlags.CancelCurrent | PendingIntentFlags.Immutable
: PendingIntentFlags.CancelCurrent;
// β Wrong β crashes on Android 12+
var pendingIntentFlags = PendingIntentFlags.CancelCurrent;
iOS / Mac Catalyst
| Issue |
Fix |
| No notification prompt appears |
Permission already denied β user must re-enable in Settings |
| Foreground notifications don't show |
Must implement UNUserNotificationCenterDelegate and set Current.Delegate |
Notification shows Alert not Banner |
Use UNNotificationPresentationOptions.Banner on iOS 14+ |
// β
iOS 14+ β use Banner
completionHandler(OperatingSystem.IsIOSVersionAtLeast(14)
? UNNotificationPresentationOptions.Banner
: UNNotificationPresentationOptions.Alert);
// β Always using Alert β deprecated on iOS 14+
completionHandler(UNNotificationPresentationOptions.Alert);
Windows
β οΈ Windows App SDK supports toast notifications but scheduled notifications are not yet supported. Immediate notifications work.
DI registration β platform-specific only
// β
Must use #if guards β there's no cross-platform implementation
#if ANDROID
builder.Services.AddTransient<INotificationManagerService,
Platforms.Android.NotificationManagerService>();
#elif IOS
builder.Services.AddTransient<INotificationManagerService,
Platforms.iOS.NotificationManagerService>();
#elif MACCATALYST
builder.Services.AddTransient<INotificationManagerService,
Platforms.MacCatalyst.NotificationManagerService>();
#endif
Common anti-patterns
// β Sending without checking permission β silent failure on API 33+
notificationManager.SendNotification("Title", "Body");
// β
Request permission first
#if ANDROID
var status = await Permissions.RequestAsync<Platforms.Android.NotificationPermission>();
if (status != PermissionStatus.Granted) return;
#endif
// β Updating UI directly from notification callback β cross-thread exception
notificationManager.NotificationReceived += (s, e) =>
myLabel.Text = ((NotificationEventArgs)e).Title;
// β
Marshal to UI thread
notificationManager.NotificationReceived += (s, e) =>
MainThread.BeginInvokeOnMainThread(() =>
myLabel.Text = ((NotificationEventArgs)e).Title);
Decision framework
| Need |
Approach |
| Immediate notification |
SendNotification(title, message) with null notifyTime |
| Scheduled reminder |
SendNotification(title, message, DateTime.Now.AddMinutes(30)) |
| Persist across reboot (Android) |
Add BOOT_COMPLETED receiver to re-schedule alarms |
| Rich notifications (images, actions) |
Extend platform implementations with native APIs |
| Push notifications from server |
Use a different pattern entirely (FCM/APNs) |
Quick checklist