skills/shiny-notifications/SKILL.md
Cross-platform local notification management for .NET MAUI apps using Shiny, supporting scheduled, repeating, and geofence-triggered notifications with channels, badges, and interactive actions.
npx skillsauth add shinyorg/skills shiny-notificationsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill when the user needs to:
| 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 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.
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
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 AccessRequestFlags when the notification uses triggers:
AccessRequestFlags.TimeSensitivity for scheduled or repeating notifications.AccessRequestFlags.LocationAware for geofence-triggered notifications.RequestRequiredAccess extension method that infers flags from the notification object.A Notification must have a Message set -- validation will throw otherwise.
Only one trigger type per notification -- you cannot mix ScheduleDate, RepeatInterval, and Geofence on the same notification.
Implement INotificationDelegate for 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 Send extension for simple notifications:
await notificationManager.Send("Title", "Message body");
For platform-specific properties, use the native subclasses:
AndroidNotification and AndroidChannelAppleNotification and AppleChannelAlways inject INotificationManager via constructor injection -- never create instances directly.
Use CancelScope wisely when cancelling:
CancelScope.DisplayedOnly -- clears only shown notifications.CancelScope.Pending -- clears only scheduled/triggered notifications.CancelScope.All -- clears everything (default).Notification: Both Shiny.Notifications and Shiny.Push define a Notification type. If both packages are referenced in the same project, do NOT add both namespaces as global usings. Use Shiny.Notifications.Notification FQN or a file-level using Shiny.Notifications; directive to disambiguate.AccessState result before attempting to send notifications.Channel.Default) always exists with Identifier = "Notifications" and ChannelImportance.Low.InvalidOperationException.BadgeCount only on immediate notifications (not triggered ones) -- validation will fail otherwise.IntervalTrigger with either Interval (raw TimeSpan) or TimeOfDay (daily/weekly recurring), never both.Center and Radius are both set on GeofenceTrigger.notification for the default small icon, or set SmallIconResourceName on AndroidNotification.RequestRequiredAccess extension method to automatically determine needed permission flags from a Notification object.Payload dictionary on Notification to pass custom data that you can read back in your INotificationDelegate.OnEntry.development
Guide for generating code that uses Shiny.Data.Sync for reliable, background-capable bidirectional JSON sync over HTTP on iOS, Android, Windows, Linux, macOS, and Blazor WASM
devops
Guide for implementing push notifications in .NET MAUI apps using Shiny.Push (native FCM/APNs) and Shiny.Push.AzureNotificationHubs
tools
GPS tracking, geofence monitoring, and motion activity recognition for .NET MAUI, iOS, and Android using Shiny.Locations
data-ai
Background job scheduling and execution for .NET MAUI (iOS/Android native OS schedulers) and in-process jobs for plain .NET, Linux, macOS, and Blazor WASM using Shiny.Jobs