plugins/shiny-client/skills/shiny-core/SKILL.md
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
npx skillsauth add shinyorg/skills shiny-coreInstall 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.
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.
IHost, HostBuilder, or UseShinyIKeyValueStore, settings, secure store) via Shiny.Extensions.Stores[Bind] partial properties or a service-attributed DI registration with [Service] / [Singleton] / [Scoped] / [Transient]IPlatform, directories, main thread invocation)IAndroidLifecycle, IIosLifecycle, IMacLifecycle)IShinyStartupTask, ShinyLifecycleTask)AccessState, permission handling, or PermissionExceptionIConnectivity) or battery status (IBattery)IRepository, IRepositoryEntity) — provided by Shiny.Extensions.StoresIRemoteConfigurationProvider) or Shiny.Extensions.ConfigurationINotifyReadOnlyCollection<T>, INotifyCollectionChanged<T>, BindingList<T>)| 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 |
| 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 |
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();
}
}
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 { }
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.
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.
When generating code that uses Shiny.Core, follow these conventions:
UseShiny() in MAUI apps or inherit the proper native base classes. This is required before any Shiny module works.Shiny.Extensions.DependencyInjection — [Service(ServiceLifetime.Singleton)], or the shortcuts [Singleton], [Scoped], [Transient]. The source generator emits services.AddGeneratedServices() — call it once during host build. Multiple interfaces, keyed services, and open generics are honoured automatically.[Bind] partial-property pattern from Shiny.Extensions.Stores for persisted settings instead of an INotifyPropertyChanged base class. The generator emits getters/setters that round-trip through the store with zero reflection — fully AOT/trim safe.IShinyStartupTask for code that should run immediately after the DI container is built. Register it with services.AddSingleton<IShinyStartupTask, MyTask>() or tag it [Singleton] and explicitly add the IShinyStartupTask interface.ShinyLifecycleTask for startup tasks that also need foreground/background application-lifecycle events. It composes IAndroidLifecycle.IApplicationLifecycle, IIosLifecycle.IApplicationLifecycle, IMacLifecycle.IApplicationLifecycle, and IShinyStartupTask into one base class.IAndroidLifecycle.*, IIosLifecycle.*, or IMacLifecycle.* sub-interfaces for platform-specific lifecycle hooks. Register them in DI and the lifecycle executor dispatches to them automatically.IKeyValueStore from Shiny.Extensions.Stores via [FromKeyedServices(StoreKeys.Default)] IKeyValueStore store (or StoreKeys.Secure). The store factory is also available via IKeyValueStoreFactory.Get(alias).Shiny.Stores.Default / Shiny.Stores.Secure accessors for one-off reads/writes outside DI contexts — the accessor self-bootstraps on first use after AddShinyStores() has run (call host.Services.UseShinyStores() after Build() on Blazor WASM).IPlatform to access AppData, Cache, Public directories and InvokeOnMainThread().AccessState enum and state.Assert() extension method to validate permissions before proceeding with platform operations.Changed C# events on IConnectivity and IBattery to react to network or battery state — these are no longer observable. Rx has been removed from Shiny.Core and Shiny.Jobs; only Shiny.BluetoothLE retains reactive streams. Subscribe with += handler and unsubscribe in your Dispose / page-leave hook.IRepository for entity persistence, with entities implementing IRepositoryEntity (must have an Identifier property). The default implementation is a filesystem JSON store registered by services.AddDefaultRepository() and used internally by Locations, Notifications, and HTTP Transfers.host.Services.UseShinyStores() immediately after builder.Build() so the static Shiny.Stores accessor captures the DI-resolved LocalStorageKeyValueStore (it needs IJSRuntime).[Singleton] attribute is the right default.partial class with [Bind] partial properties from Shiny.Extensions.Stores.Tasks/ or Infrastructure/ folder.Settings/ or Models/ folder.AccessState.Denied, AccessState.Disabled, and AccessState.NotSetup gracefully.IConnectivity.Changed, IBattery.Changed) over Rx — Rx is intentionally absent from Core.Shiny namespace are available when the appropriate package is referenced.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
UseShiny() must be called in the builder chain before building the MAUI app. For native apps, the host must be created and Run() called in the application startup.[Singleton]/[Scoped]/[Transient] and let the Shiny.Extensions.DependencyInjection source generator emit AddGeneratedServices(). AOT-clean, no reflection at startup, multiple interfaces handled.[Bind] partial properties for persisted settings instead of INotifyPropertyChanged plumbing. The generator emits getters/setters that round-trip through the configured IKeyValueStore.IShinyStartupTask.Start() runs synchronously on the main thread at startup.StoreKeys.Default for general preferences (backed by SharedPreferences / NSUserDefaults / ApplicationData.LocalSettings / localStorage), StoreKeys.Secure for sensitive data (Android Keystore / iOS Keychain / Windows secure storage).Shiny.Stores.Default / Shiny.Stores.Secure / Shiny.Stores.Keyed(alias) accessor for one-off reads/writes outside DI.Host.IsInitialized before accessing Host.Current in code that may run before initialization.BindingList<T> for thread-safe observable collections that can be bound to UI.Shiny.Extensions.Serialization with your own — Shiny.Jobs, Shiny.Locations, Shiny.Notifications, and Shiny.Net.Http each ship their own JsonSerializerContext for AOT safety.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
Cross-platform local notification management for .NET MAUI apps using Shiny, supporting scheduled, repeating, and geofence-triggered notifications with channels, badges, and interactive actions.
tools
GPS tracking, geofence monitoring, and motion activity recognition for .NET MAUI, iOS, and Android using Shiny.Locations