Recommended for most apps
.NET MAUI is the recommended hosting model for Shiny. A single line of code wires up all platform lifecycle hooks, dependency injection, and background services — so you can focus on registering your Shiny modules.
Recommended for most apps
.NET MAUI is the recommended hosting model for Shiny. A single line of code wires up all platform lifecycle hooks, dependency injection, and background services — so you can focus on registering your Shiny modules.
Install the NuGet package
Add UseShiny() to your MauiProgram.cs
using Shiny;
namespace ShinyApp;
public static class MauiProgram{ public static MauiApp CreateMauiApp() { var builder = MauiApp .CreateBuilder() .UseMauiApp<App>() .UseShiny() // <-- this wires all Shiny lifecycle through MAUI .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); });
// Register your Shiny services here // builder.Services.AddGps<MyGpsDelegate>(); // builder.Services.AddJob(typeof(MyJob));
return builder.Build(); }}Register Shiny services
Use builder.Services to register any Shiny modules you need — GPS, Bluetooth, Jobs, Notifications, etc. See each module’s Getting Started page for specifics.
Nothing extra is needed. Mac Catalyst is a first-class MAUI target — UseShiny() handles it the same way it handles iOS.
Native macOS (AppKit) uses MAUI’s macOS backend — not Mac Catalyst, not UseShiny(). Create an NSApplication entry point that uses MacOSMauiApplication as the delegate, call UseMauiAppMacOS<App>() + AddMacOSEssentials() in MauiProgram, and register Shiny services directly on builder.Services:
using AppKit;using Microsoft.Maui.Platform.MacOS.Hosting;using Shiny.Hosting;
public class Program{ static void Main(string[] args) { NSApplication.Init(); NSApplication.SharedApplication.Delegate = new AppDelegate(); NSApplication.Main(args); }}
public class AppDelegate : MacOSMauiApplication{ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
// Forward these only if you're using Shiny.Push [Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")] public void DidRegisterForRemoteNotifications(NSApplication app, NSData deviceToken) => Host.Lifecycle.OnRegisteredForRemoteNotifications(deviceToken);
[Export("application:didFailToRegisterForRemoteNotificationsWithError:")] public void DidFailToRegisterForRemoteNotifications(NSApplication app, NSError error) => Host.Lifecycle.OnFailedToRegisterForRemoteNotifications(error);
[Export("application:didReceiveRemoteNotification:")] public void DidReceiveRemoteNotification(NSApplication app, NSDictionary userInfo) => Host.Lifecycle.OnDidReceiveRemoteNotification(userInfo);}using Microsoft.Maui.Essentials.MacOS;using Microsoft.Maui.Platform.MacOS.Hosting;
public static class MauiProgram{ public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiAppMacOS<App>() .AddMacOSEssentials();
// Register Shiny modules directly — no UseShiny() on native macOS builder.Services.AddBluetoothLE(); builder.Services.AddNotifications();
return builder.Build(); }}Shiny modules with native macOS support: BLE, BLE Hosting, Notifications, Push, Battery, Connectivity.
Linux does not need UseShiny() — there are no native lifecycle hooks to wire up. Use the MAUI GTK4 entry point and register Shiny services directly on builder.Services:
var builder = MauiApp.CreateBuilder();builder .UseMauiAppLinuxGtk4<App>() .AddLinuxGtk4Essentials();
// Register Shiny modules directly — no UseShiny() required on Linuxbuilder.Services.AddBluetoothLE();builder.Services.AddNotifications();Shiny modules with Linux support: BLE, BLE Hosting, Notifications, Mediator, Stores, Localization, Document DB, Reflector, DI.
Blazor does not use UseShiny() — there is no native platform lifecycle to wire. Register Shiny modules directly on builder.Services in your Blazor Program.cs:
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddBluetoothLE();builder.Services.AddGps();builder.Services.AddPush(new WebPushOptions { VapidPublicKey = "..." });
await builder.Build().RunAsync();Shiny registers everything through the standard IServiceCollection, so any container with a
Microsoft.Extensions.DependencyInjection adapter works — including Prism’s DryIoc container. There is
one caveat worth knowing about.
Shiny’s key/value stores are registered as keyed services (StoreKeys.Default, StoreKeys.Secure)
and Shiny’s own types inject them with [FromKeyedServices(StoreKeys.Default)]. Keyed services arrived
in .NET 8, and several container adapters still ship a pre-keyed-services core — Prism.Container.DryIoc,
for example, is built on DryIoc 5.x. Those adapters silently ignore the [FromKeyedServices]
attribute and try to resolve the plain service type instead.
AddShinyStores() handles this by also registering the default store unkeyed, pointing at the same
instance as StoreKeys.Default, so Shiny’s platform types resolve correctly on those containers.
There is no equivalent fallback for StoreKeys.Secure — two stores can’t share one unkeyed service
type. In your code, avoid [FromKeyedServices] for the secure or custom stores when the app uses a
non-Microsoft container, and use the static accessor instead:
// works on every containervar token = Shiny.Stores.Secure.Get<string>("token");var custom = Shiny.Stores.Keyed("my-store");| Scenario | Hosting Model |
|---|---|
| Standard .NET MAUI app | MAUI (this page) |
| .NET native without MAUI (iOS/Android) | Native |
| Existing app with complex platform setup | Manual |