Stateful Services
Stateful services are a simple way to maintain service state across app restarts. You write a regular singleton service, declare the properties you want to persist, and Shiny automatically reads them back from storage on startup and writes them back whenever they change — no manual save/load code.
Under the hood this is powered by Shiny’s object store binder, which wires an INotifyPropertyChanged object to an IKeyValueStore.
Requirements
Section titled “Requirements”- The service must be registered as a singleton (use
AddShinyService<T>). - The service must implement
System.ComponentModel.INotifyPropertyChanged. The easiest way is to inherit fromShiny.NotifyPropertyChanged. - Every property you want to persist must have a public get and set.
- Property types must be “simple” (primitives,
string,DateTime,Guid, enums) or JSON-serializable.
Implementation
Section titled “Implementation”using Shiny;using Shiny.Stores;
namespace MyApp.Services;
// Optional: pick which store backs the service.// Defaults to "settings". Use "secure" for sensitive data.[ObjectStoreBinder("settings")]public class MyStatefulService : NotifyPropertyChanged{ string userName = string.Empty; public string UserName { get => this.userName; set => this.Set(ref this.userName, value); }
bool isDarkMode; public bool IsDarkMode { get => this.isDarkMode; set => this.Set(ref this.isDarkMode, value); }
DateTime? lastSyncedAt; public DateTime? LastSyncedAt { get => this.lastSyncedAt; set => this.Set(ref this.lastSyncedAt, value); }}Registration
Section titled “Registration”using Shiny;
// wherever you register your services with the service collectionbuilder.Services.AddShinyService<MyStatefulService>();AddShinyService<T>() is what triggers the binding. A plain AddSingleton<T>() registration will not persist — this is the most common reason properties “silently don’t save”.
Inject your service like any other singleton. Reads come back with whatever was persisted last; writes are flushed to the store immediately.
public class SettingsViewModel{ readonly MyStatefulService state;
public SettingsViewModel(MyStatefulService state) { this.state = state; }
public void ToggleDarkMode() { // This assignment is automatically persisted this.state.IsDarkMode = !this.state.IsDarkMode; }}Picking a Store
Section titled “Picking a Store”| Store Alias | Backed By | Use For |
|---|---|---|
"settings" (default) | NSUserDefaults (iOS) / SharedPreferences (Android) | General preferences and non-sensitive state |
"secure" | iOS Keychain / Android Keystore | Auth tokens, API keys, anything sensitive |
"memory" | In-memory only | Testing, transient state that should not persist |
Apply the desired alias via [ObjectStoreBinder("secure")] on the class, or pass the store alias to the IObjectStoreBinder.Bind call if you are binding manually.