name: shiny-core description: Core infrastructure, hosting, DI, key-value stores, lifecycle hooks, and platform abstractions for Shiny on .NET MAUI, iOS, Android, Mac Catalyst, macOS, Windows, Linux, and Blazor WebAssembly auto_invoke: true triggers: - shiny core - shiny setup - shiny host - HostBuilder - IHost - IPlatform - IKeyValueStore - IKeyValueStoreFactory - StoreKeys - AccessState - IShinyStartupTask - ShinyLifecycleTask - IAndroidLifecycle - IIosLifecycle - IMacLifecycle - UseShiny - AddShinyStores - AddGeneratedServices - Bind attribute - Service attribute - Singleton attribute - Scoped attribute - Transient attribute - NotifyPropertyChanged - key value store - settings store - secure store - platform abstraction - lifecycle hook - startup task - connectivity - IConnectivity - IBattery - AddConnectivity - AddBattery - IRepository - IRepositoryEntity - Shiny.Core - Shiny.Core.Linux - Shiny.Core.Blazor - Shiny.Hosting.Maui - Shiny.Hosting.Native - Shiny.Extensions.DependencyInjection - Shiny.Extensions.Stores - Shiny.Extensions.Configuration
Shiny Core
Shiny.Core is the foundational library for the Shiny ecosystem. It provides the hosting model, platform abstractions, lifecycle hooks, connectivity/battery monitoring, and the AOT-friendly type registry that all other Shiny modules build upon. Storage and DI registration are layered on via the Shiny.Extensions.DependencyInjection and Shiny.Extensions.Stores source-generated packages — these are pulled in transitively by Shiny.Core so you don't add them manually.
When to Use This Skill
- The user needs to set up Shiny hosting in a MAUI, native, Blazor, or Linux/macOS app
- The user asks about
IHost,HostBuilder, orUseShiny - The user needs key-value storage (
IKeyValueStore, settings, secure store) viaShiny.Extensions.Stores - The user wants source-generated persistence with
[Bind]partial properties or a service-attributed DI registration with[Service]/[Singleton]/[Scoped]/[Transient] - The user asks about platform abstractions (
IPlatform, directories, main thread invocation) - The user needs Android, iOS, or macOS lifecycle hooks (
IAndroidLifecycle,IIosLifecycle,IMacLifecycle) - The user needs startup tasks (
IShinyStartupTask,ShinyLifecycleTask) - The user asks about
AccessState, permission handling, orPermissionException - The user needs network connectivity monitoring (
IConnectivity) or battery status (IBattery) - The user needs an entity repository (
IRepository,IRepositoryEntity) — provided byShiny.Extensions.Stores - The user asks about remote configuration (
IRemoteConfigurationProvider) orShiny.Extensions.Configuration - The user needs observable collections (
INotifyReadOnlyCollection<T>,INotifyCollectionChanged<T>,BindingList<T>)
Library Overview
| Item | Value |
|---|---|
| NuGet | Shiny.Core (pulls in Shiny.Extensions.DependencyInjection + Shiny.Extensions.Stores) |
| Namespace | Shiny, Shiny.Hosting, Shiny.Net, Shiny.Power, Shiny.Collections, Shiny.Extensions.Stores (storage), Shiny.Extensions.Configuration (remote config) |
| Platforms | iOS, Mac Catalyst, macOS, Android, Windows, Linux, Blazor WebAssembly, plain .NET |
Companion Libraries
| NuGet | Namespace | Purpose |
|---|---|---|
Shiny.Hosting.Maui |
Shiny |
MAUI hosting integration (UseShiny) |
Shiny.Hosting.Native |
Shiny |
Native hosting base classes (ShinyAppDelegate, ShinyAndroidApplication, ShinyAndroidActivity) |
Shiny.Core.Linux |
Shiny |
Linux platform implementation + AddConnectivity() / AddBattery() |
Shiny.Core.Blazor |
Shiny |
Blazor WebAssembly platform implementation + AddConnectivity() / AddBattery() |
Shiny.Extensions.DependencyInjection |
Shiny |
Source-generated [Service] / [Singleton] / [Scoped] / [Transient] DI registration. Pulled in by Shiny.Core. |
Shiny.Extensions.Stores |
Shiny.Extensions.Stores |
IKeyValueStore, IRepository, source-generated [Bind] partial-property persistence, static Shiny.Stores.Default/Secure accessor. Pulled in by Shiny.Core. |
Shiny.Extensions.Stores.Web |
Shiny.Extensions.Stores |
Blazor WebAssembly localStorage / sessionStorage adapters (AddShinyWebAssemblyStores()) |
Shiny.Extensions.Serialization |
Shiny.Extensions.Serialization |
AOT-safe System.Text.Json serializer extensions used by Shiny modules |
Shiny.Extensions.Configuration |
Shiny.Extensions.Configuration |
Remote configuration provider and platform preferences |
Setup
MAUI Setup
In MauiProgram.cs, call UseShiny() on the MauiAppBuilder. This registers all core infrastructure services, the platform key/value stores, and lifecycle wiring automatically:
using Shiny;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseShiny(); // Registers Shiny core services, stores, and lifecycle hooks
// Register your own services from [Service]/[Singleton]/[Scoped]/[Transient] attributes
builder.Services.AddGeneratedServices();
// Add device monitoring (these are no-ops if already registered)
builder.Services.AddConnectivity();
builder.Services.AddBattery();
return builder.Build();
}
}
Native (Non-MAUI) Setup
For native iOS apps, inherit from ShinyAppDelegate:
[Register("AppDelegate")]
public class AppDelegate : ShinyAppDelegate
{
protected override IHost CreateShinyHost()
{
var builder = HostBuilder.Create();
// Register services on builder.Services
return builder.Build();
}
}
For native Android apps, inherit from ShinyAndroidApplication and use ShinyAndroidActivity:
[Application]
public class MainApplication : ShinyAndroidApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer) {}
protected override IHost CreateShinyHost()
{
var builder = HostBuilder.Create();
// Register services on builder.Services
return builder.Build();
}
}
[Activity(MainLauncher = true)]
public class MainActivity : ShinyAndroidActivity { }
Linux / macOS / plain .NET Setup
Shiny.Core.Linux provides the Linux IPlatform, IConnectivity, and IBattery implementations and is targeted at console / GTK apps. Use the same HostBuilder.Create() flow and call AddConnectivity() / AddBattery() from Shiny.Core.Linux if you need device monitoring.
Blazor WebAssembly Setup
For Blazor WASM, reference Shiny.Core.Blazor and call AddConnectivity() / AddBattery() to wire navigator-based monitoring. Storage requires Shiny.Extensions.Stores.Web and a call to host.Services.UseShinyStores() after Build() so the static Shiny.Stores accessor snapshots the IJSRuntime-backed LocalStorageKeyValueStore.
Code Generation Instructions
When generating code that uses Shiny.Core, follow these conventions:
- Always call
UseShiny()in MAUI apps or inherit the proper native base classes. This is required before any Shiny module works. - Register your own services via attributes from
Shiny.Extensions.DependencyInjection—[Service(ServiceLifetime.Singleton)], or the shortcuts[Singleton],[Scoped],[Transient]. The source generator emitsservices.AddGeneratedServices()— call it once during host build. Multiple interfaces, keyed services, and open generics are honoured automatically. - Use the
[Bind]partial-property pattern fromShiny.Extensions.Storesfor persisted settings instead of anINotifyPropertyChangedbase class. The generator emits getters/setters that round-trip through the store with zero reflection — fully AOT/trim safe. - Implement
IShinyStartupTaskfor code that should run immediately after the DI container is built. Register it withservices.AddSingleton<IShinyStartupTask, MyTask>()or tag it[Singleton]and explicitly add theIShinyStartupTaskinterface. - Inherit
ShinyLifecycleTaskfor startup tasks that also need foreground/background application-lifecycle events. It composesIAndroidLifecycle.IApplicationLifecycle,IIosLifecycle.IApplicationLifecycle,IMacLifecycle.IApplicationLifecycle, andIShinyStartupTaskinto one base class. - Implement
IAndroidLifecycle.*,IIosLifecycle.*, orIMacLifecycle.*sub-interfaces for platform-specific lifecycle hooks. Register them in DI and the lifecycle executor dispatches to them automatically. - Inject a keyed
IKeyValueStorefromShiny.Extensions.Storesvia[FromKeyedServices(StoreKeys.Default)] IKeyValueStore store(orStoreKeys.Secure). The store factory is also available viaIKeyValueStoreFactory.Get(alias). - Use the static
Shiny.Stores.Default/Shiny.Stores.Secureaccessors for one-off reads/writes outside DI contexts — the accessor self-bootstraps on first use afterAddShinyStores()has run (callhost.Services.UseShinyStores()afterBuild()on Blazor WASM). - Use
IPlatformto accessAppData,Cache,Publicdirectories andInvokeOnMainThread(). - Use
AccessStateenum andstate.Assert()extension method to validate permissions before proceeding with platform operations. - Use the
ChangedC# events onIConnectivityandIBatteryto react to network or battery state — these are no longer observable. Rx has been removed fromShiny.CoreandShiny.Jobs; onlyShiny.BluetoothLEretains reactive streams. Subscribe with+= handlerand unsubscribe in yourDispose/ page-leave hook. - Use
IRepositoryfor entity persistence, with entities implementingIRepositoryEntity(must have anIdentifierproperty). The default implementation is a filesystem JSON store registered byservices.AddDefaultRepository()and used internally by Locations, Notifications, and HTTP Transfers. - For Blazor WASM, call
host.Services.UseShinyStores()immediately afterbuilder.Build()so the staticShiny.Storesaccessor captures the DI-resolvedLocalStorageKeyValueStore(it needsIJSRuntime).
Conventions
- Shiny services are typically singletons; the
[Singleton]attribute is the right default. - Persisted settings classes should be
partial classwith[Bind]partial properties fromShiny.Extensions.Stores. - Place startup tasks in a
Tasks/orInfrastructure/folder. - Place settings classes in a
Settings/orModels/folder. - Always handle
AccessState.Denied,AccessState.Disabled, andAccessState.NotSetupgracefully. - Prefer C# events on Shiny.Core abstractions (
IConnectivity.Changed,IBattery.Changed) over Rx — Rx is intentionally absent from Core. - Extension methods in
Shinynamespace are available when the appropriate package is referenced.
Namespace Ambiguities with MAUI
When using Shiny in a MAUI app, several Shiny types collide with MAUI implicit usings. Do NOT add all Shiny namespaces as global usings. Use explicit namespaces or FQNs for these:
| Type | Shiny Namespace | MAUI Namespace | Resolution |
|---|---|---|---|
IConnectivity |
Shiny.Net |
Microsoft.Maui.Networking |
Use Shiny.Net.IConnectivity FQN |
IBattery |
Shiny.Power |
Microsoft.Maui.Devices |
Use Shiny.Power.IBattery FQN |
DeviceInfo |
Shiny.BluetoothLE |
Microsoft.Maui.Devices |
Use FQN for whichever you need |
Safe global usings (won't conflict with MAUI):
global using Shiny;
global using Shiny.Extensions.Stores; // IKeyValueStore, StoreKeys, [Bind]
global using Shiny.Jobs;
global using Shiny.Locations;
global using Shiny.BluetoothLE;
// Do NOT globally use: Shiny.Net, Shiny.Power, Shiny.Notifications, Shiny.Push, Shiny.BluetoothLE.Hosting
Best Practices
- Initialize Shiny early --
UseShiny()must be called in the builder chain before building the MAUI app. For native apps, the host must be created andRun()called in the application startup. - Prefer attribute-based registration -- tag services with
[Singleton]/[Scoped]/[Transient]and let theShiny.Extensions.DependencyInjectionsource generator emitAddGeneratedServices(). AOT-clean, no reflection at startup, multiple interfaces handled. - Prefer
[Bind]partial properties for persisted settings instead ofINotifyPropertyChangedplumbing. The generator emits getters/setters that round-trip through the configuredIKeyValueStore. - Keep startup tasks lightweight --
IShinyStartupTask.Start()runs synchronously on the main thread at startup. - Use the right keyed store --
StoreKeys.Defaultfor general preferences (backed by SharedPreferences / NSUserDefaults / ApplicationData.LocalSettings / localStorage),StoreKeys.Securefor sensitive data (Android Keystore / iOS Keychain / Windows secure storage). - Use the static
Shiny.Stores.Default/Shiny.Stores.Secure/Shiny.Stores.Keyed(alias)accessor for one-off reads/writes outside DI. - Check
Host.IsInitializedbefore accessingHost.Currentin code that may run before initialization. - Use
BindingList<T>for thread-safe observable collections that can be bound to UI. - Use the JSON contexts emitted by Shiny modules if you mix
Shiny.Extensions.Serializationwith your own — Shiny.Jobs, Shiny.Locations, Shiny.Notifications, and Shiny.Net.Http each ship their ownJsonSerializerContextfor AOT safety.