Following on from the posts on Dependency Injection, Logging and Configuration, in this post we’re going to look at how data can be persisted between sessions using Settings within a Windows or Uno Platform application. This series makes use of Uno.Extensions in order to initialize the services that are required by the application.
At the end of the Configuration post we had initialized the app to load configuration from appsettings.json and had registered the AppConfig
type to access the corresponding configuration section.
.UseConfiguration( configure: configBuilder => configBuilder .EmbeddedSource<App>() .Section<AppConfig>())
Settings are registered exactly the same way as configuration sections. In fact, with the Uno.Extensions, any class that is registered to access a configuration section, also registers the section so it could be used to persist information.
Let’s register another type, UserSettings
, to be used to save the last entered username.
public record UserSettings
{
public string? UserName { get; init; }
}
The UserSettings
is registered with an additional Section
method call as part of the UseConfiguration
method.
.UseConfiguration( configure: configBuilder => configBuilder .EmbeddedSource<App>() .Section<AppConfig>() .Section<UserSettings>())
With UserSettings
registered, the current value can be read using IOptions<UserSettings>
as we did in the Configuration post. In order to update the value, we need to retrieve an IWriteableOptions<UserSettings>
.
var settings = Host.Services.GetRequiredService<IWritableOptions<UserSettings>>();
var cachedUserName = settings.Value?.UserName ?? string.Empty;
await settings.UpdateAsync(oldSettings => oldSettings with { UserName = "Nick" });
The UpdateAsync method includes a callback parameter, which is invoked with the existing UserSettings
value. It’s safe to assume that this is not null – if no existing value exists, a default value is created and provided.
After the UserSettings
value has been saved, the next time IOptions<UserSettings>
is retrieved, it will hold the new UserSettings
value. However, in some cases, you may need to detect when the UserSettings
value changes, for example to update the current page with the new UserName
value. In this case, instead of requesting IOptions
you can request IOptionsMonitor
as shown in the following code.
var userSettingsMonitor = Host.Services.GetRequiredService<IOptionsMonitor<UserSettings>>();
userSettingsMonitor.OnChange(settings =>
{
Debug.WriteLine($"setttings changed - {settings.UserName}");
});
At this point you might be wondering what the difference is between configuration and settings when using Uno.Extensions? The reality is that there isn’t a difference, just in how you use the registered sections – you can register a section to be used just for reading configuration (eg from appsettings.json); you can register a section to be used for reading/writing settings; you can also register a section to be used for both configuration and settings. In the last scenario, the default value for the section will be read from configuration (ie you can role out a default value with your application). When an updated value is written, it will be used in future app sessions.
So far in this series we’ve covered how to setup the DI container using IHost/IHostBuilder/IApplicationBuilder and adding in Logging, Configuration and Settings. All of these features can be directly accessed from the Host instance. In the coming posts we’ll cover the use of navigation which in addition to navigating between views, is also responsible for creating instances of ViewModels/Models and setting them as the DataContext for the navigated view. Where the ViewModels/Models have dependencies, for example on ILogger, they will be loaded from the DI container.
1 thought on “Saving Data using Settings for Windows and Uno Platform Applications”