name: shiny-notifications description: Cross-platform local notification management for .NET MAUI apps using Shiny, supporting scheduled, repeating, and geofence-triggered notifications with channels, badges, and interactive actions. auto_invoke: true triggers: - local notifications - notification manager - notification channel - notification delegate - push notification - scheduled notification - geofence notification - repeating notification - badge count - notification actions - Shiny.Notifications - INotificationManager - INotificationDelegate - notification permission - notification sound - channel importance
Shiny Notifications
When to Use This Skill
Use this skill when the user needs to:
- Send local notifications (immediate, scheduled, repeating, or geofence-triggered)
- Manage notification channels with importance levels, sounds, and actions
- Handle notification tap responses and interactive action buttons
- Request notification permissions on iOS and Android
- Manage app icon badge counts
- Configure platform-specific notification behavior (Android ongoing, iOS subtitles, etc.)
Library Overview
| Item | Value |
|---|---|
| NuGet Package | Shiny.Notifications (iOS, Mac Catalyst, Android, macOS, Windows); Shiny.Notifications.Linux (Linux) |
| Primary Namespace | Shiny.Notifications |
| Registration Namespace | Shiny (extension methods on IServiceCollection) |
| Platforms | iOS, Mac Catalyst, Android, macOS, Windows, Linux |
| Dependencies | Shiny.Core, Shiny.Locations, Shiny.Support.Repositories |
Linux
Linux notifications ship in a separate package, Shiny.Notifications.Linux. They are delivered via the freedesktop org.freedesktop.Notifications D-Bus service (GNOME, KDE, XFCE, etc.) and support the same INotificationManager API surface as the other platforms. Scheduled notifications are tracked in-process only — there is no OS-level scheduler like BGTaskScheduler or WorkManager, so the host process must be running for a scheduled notification to fire. Channels are exposed but only a subset of freedesktop hints (urgency, category, image) are actually honoured by most daemons. Geofence triggers and time-sensitive flags are not applicable.
Register with services.AddNotifications<TDelegate>(); from the Shiny namespace — the same call site as the other platforms.
Setup
Register the notification services in your MauiProgram.cs:
using Shiny;
// Without a delegate (fire-and-forget notifications)
services.AddNotifications();
// With a delegate to handle notification taps
services.AddNotifications<MyNotificationDelegate>();
On iOS, you can optionally pass an IosConfiguration to control authorization and presentation options:
#if IOS || MACCATALYST
services.AddNotifications<MyNotificationDelegate>(new IosConfiguration(
UNAuthorizationOptions: UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound,
PresentationOptions: UNNotificationPresentationOptions.Banner | UNNotificationPresentationOptions.Badge | UNNotificationPresentationOptions.Sound
));
#endif
Code Generation Instructions
When generating code that uses Shiny Notifications, follow these conventions:
Always request access before sending notifications:
var access = await notificationManager.RequestAccess(); if (access != AccessState.Available) { // Handle denied permission return; }Use
AccessRequestFlagswhen the notification uses triggers:AccessRequestFlags.TimeSensitivityfor scheduled or repeating notifications.AccessRequestFlags.LocationAwarefor geofence-triggered notifications.- Or use the
RequestRequiredAccessextension method that infers flags from the notification object.
A
Notificationmust have aMessageset -- validation will throw otherwise.Only one trigger type per notification -- you cannot mix
ScheduleDate,RepeatInterval, andGeofenceon the same notification.Implement
INotificationDelegatefor handling user taps:public class MyNotificationDelegate : INotificationDelegate { public async Task OnEntry(NotificationResponse response) { // response.Notification -- the original notification // response.ActionIdentifier -- which action button was pressed // response.Text -- text reply if action was TextReply type } }Create channels before sending notifications that reference them:
notificationManager.AddChannel(new Channel { Identifier = "alerts", Importance = ChannelImportance.High, Sound = ChannelSound.High });Use the convenience
Sendextension for simple notifications:await notificationManager.Send("Title", "Message body");For platform-specific properties, use the native subclasses:
- Android:
AndroidNotificationandAndroidChannel - iOS:
AppleNotificationandAppleChannel
- Android:
Always inject
INotificationManagervia constructor injection -- never create instances directly.Use
CancelScopewisely when cancelling:CancelScope.DisplayedOnly-- clears only shown notifications.CancelScope.Pending-- clears only scheduled/triggered notifications.CancelScope.All-- clears everything (default).
Namespace Ambiguities
Notification: BothShiny.NotificationsandShiny.Pushdefine aNotificationtype. If both packages are referenced in the same project, do NOT add both namespaces as global usings. UseShiny.Notifications.NotificationFQN or a file-levelusing Shiny.Notifications;directive to disambiguate.
Best Practices
- Always check the
AccessStateresult before attempting to send notifications. - Use channels to group notifications by category (e.g., "alerts", "reminders", "messages").
- The default channel (
Channel.Default) always exists withIdentifier = "Notifications"andChannelImportance.Low. - Do not remove the default channel -- the library will throw an
InvalidOperationException. - Set
BadgeCountonly on immediate notifications (not triggered ones) -- validation will fail otherwise. - Use
IntervalTriggerwith eitherInterval(raw TimeSpan) orTimeOfDay(daily/weekly recurring), never both. - For geofence notifications, ensure
CenterandRadiusare both set onGeofenceTrigger. - On Android, create a drawable resource named
notificationfor the default small icon, or setSmallIconResourceNameonAndroidNotification. - Prefer the
RequestRequiredAccessextension method to automatically determine needed permission flags from aNotificationobject. - Use
Payloaddictionary onNotificationto pass custom data that you can read back in yourINotificationDelegate.OnEntry.