Skip to content
Shiny.NET
Shiny MAUI Shell v7 - App Links, App Shortcuts, & Navigation Interception!Shortcut me to it

Lifecycle Hooks

Like .NET MAUI, Shiny has always needed a set of platform lifecycle hooks. Many operations within Shiny — permissions, app foregrounding and backgrounding, push notification registration, deep links, activity results — require reacting to events that only the platform itself can raise. Shiny does not take a direct dependency on .NET MAUI; instead it exposes its own lifecycle interfaces that work in MAUI, native iOS/Android, and other .NET hosts.

Shiny’s host wires a platform-specific lifecycle executor into the app startup. Any service you register with the DI container that implements one of the lifecycle sub-interfaces below is automatically resolved and called when the corresponding platform event fires. You never call these methods yourself — just implement the interface and register the service.

// In MauiProgram.cs (or your native host setup)
builder.Services.AddSingleton<IIosLifecycle.IContinueActivity, MyUniversalLinkHandler>();
builder.Services.AddSingleton<IAndroidLifecycle.IOnActivityNewIntent, MyDeepLinkHandler>();

iOS lifecycle interfaces live in Shiny.Hosting.IIosLifecycle. Implement any of the nested sub-interfaces below to hook into the corresponding UIApplicationDelegate or UNUserNotificationCenterDelegate callback.

Interface Fires On
IApplicationLifecycle App entering foreground / background
IOnFinishedLaunching FinishedLaunching with launch options
IContinueActivity Universal links & Handoff (ContinueUserActivity)
IRemoteNotifications APNs token registration, registration failure, and silent push delivery
INotificationHandler Foreground notification presentation and user tap responses
IHandleEventsForBackgroundUrl Background NSURLSession completion
using Shiny.Hosting;
using Foundation;
using UIKit;
public class UniversalLinkHandler : IIosLifecycle.IContinueActivity
{
public bool Handle(NSUserActivity activity, UIApplicationRestorationHandler completionHandler)
{
if (activity.ActivityType == NSUserActivityType.BrowsingWeb)
{
var url = activity.WebPageUrl?.ToString();
// route the URL inside your app
return true;
}
return false;
}
}
// Registration
builder.Services.AddSingleton<IIosLifecycle.IContinueActivity, UniversalLinkHandler>();

Android lifecycle interfaces live in Shiny.Hosting.IAndroidLifecycle. The executor observes the Android Application and the currently-attached Activity, so these callbacks fire regardless of which activity is active.

Interface Fires On
IApplicationLifecycle App entering foreground / background
IOnActivityOnCreate Activity OnCreate (including saved instance state)
IOnActivityNewIntent Activity OnNewIntent — used for deep links and push taps
IOnActivityResult Activity result callbacks from StartActivityForResult
IOnActivityRequestPermissionsResult Runtime permission request results
using Shiny.Hosting;
using Android.App;
using Android.Content;
public class DeepLinkHandler : IAndroidLifecycle.IOnActivityNewIntent
{
public void Handle(Activity activity, Intent intent)
{
var data = intent?.DataString;
if (!string.IsNullOrEmpty(data))
{
// route the URL inside your app
}
}
}
// Registration
builder.Services.AddSingleton<IAndroidLifecycle.IOnActivityNewIntent, DeepLinkHandler>();

AppKit apps (net10.0-macos) use Shiny.Hosting.IMacLifecycle. It has the same shape as the iOS set, adapted to NSApplicationDelegate:

Interface Fires On
IApplicationLifecycle App becoming active / resigning active
IOnFinishedLaunching DidFinishLaunching, with the launch user-info dictionary (may be null)
IContinueActivity Universal links & Handoff
IRemoteNotifications APNs token registration, registration failure, and remote notification delivery
INotificationHandler Foreground notification presentation and user responses

tvOS uses the iOS interfaces, with one exception: IIosLifecycle.INotificationHandler does not exist on tvOS. A tvOS notification can only change the app icon badge. There is no UNNotificationResponse, and nothing is presented in the foreground. Wrap any implementation of it in #if !TVOS.

These platforms have no lifecycle executor. Windows and Blazor WebAssembly have no hooks for Shiny to forward. A plain .NET host (console, service, GTK) is never told that it moved to the background, so Shiny doesn’t pretend that it was. ShinyLifecycleTask is a startup task only on Android, iOS, tvOS, Mac Catalyst and macOS. On Windows the class exists but is not an IShinyStartupTask, and on plain net10.0 and Blazor it doesn’t exist at all. In code shared with those targets, implement IShinyStartupTask directly.

IApplicationLifecycle exists on IIosLifecycle, IAndroidLifecycle, and IMacLifecycle with the same shape. If you need a single handler that works on every platform, the easiest route is to inherit from ShinyLifecycleTask — it implements the correct interface on each platform and runs as an IShinyStartupTask, so it is automatically instantiated on app launch.

using Shiny;
[Singleton]
public class AppPresenceTask : ShinyLifecycleTask
{
readonly ILogger<AppPresenceTask> logger;
public AppPresenceTask(ILogger<AppPresenceTask> logger)
{
this.logger = logger;
}
public override void Start()
=> this.logger.LogInformation("App launched");
protected override void OnStateChanged(bool backgrounding)
=> this.logger.LogInformation(backgrounding ? "Backgrounded" : "Foregrounded");
}
// Registration (alongside AddGeneratedServices())
builder.Services.AddGeneratedServices();