Skip to content

Commit

Permalink
docs: writable config tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
lukeblevins committed Jun 4, 2023
1 parent 8164adc commit 702da39
Showing 1 changed file with 129 additions and 4 deletions.
133 changes: 129 additions & 4 deletions doc/Learn/Tutorials/Configuration/HowTo-WritableConfiguration.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,137 @@
---
uid: Learn.Tutorials.Configuration.HowToWritableConfiguration
---

# How-To: Writable Configuration

This how-to is currently under construction. It will be available soon.
**Writable Configuration** extends the standard, [read-only](xref:Learn.Tutorials.Configuration.HowToConfiguration) pattern by allowing for programmatic writing of configuration values at runtime. This is useful for scenarios where you want to persist user preferences or other trivial information that may be changed over time. `Uno.Extensions.Configuration` extends the `IOptionsSnapshot<T>` interface from [Microsoft.Extensions.Options](https://docs.microsoft.com/dotnet/api/microsoft.extensions.options) to support this.

A special interface called `IWritableOptions<T>` is registered as a service when you use the `UseConfiguration()` extension method. In this tutorial, we will walk through how to use the `UpdateAsync()` method on this interface to modify values of a specific configuration section. For a refresher on configuration sections, see [Sections](xref:Overview.Configuration#sections).

> [!NOTE]
> It is common to see this referred to as the _settings_ pattern in certain documentation. This is because the `IOptions<T>` interface is often used to represent settings that can be changed by the user.
## Step-by-steps

### 1. Prepare for writing configuration values

* Ensure your project has `Uno.Extensions.Configuration` installed as a NuGet [package](https://www.nuget.org/packages/Uno.Extensions.Configuration/).

* To enable configuration, you first need to call `UseConfiguration()` on the `IHostBuilder` instance:

```csharp
private IHost Host { get; }

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
var appBuilder = this.CreateBuilder(args)
.Configure(host => {
host
.UseConfiguration()
});

Host = appBuilder.Build();
...
```

* Use the `EmbeddedSource<T>()` extension method to load configuration information from a JSON file called `appsettings.json` embedded inside the `App` assembly:

```csharp
private IHost Host { get; }

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
var appBuilder = this.CreateBuilder(args)
.Configure(host => {
host
.UseConfiguration(configure: configBuilder =>
configBuilder
.EmbeddedSource<App>()
);
});

Host = appBuilder.Build();
...
```

### 2. Define a configuration section

* To model the configuration section you want to write to, author a new class or record with related properties:

```csharp
public partial record ToDoApp
{
public bool? IsDark { get; init; }
public string? LastTaskList { get; init; }
}
```

* For instance, the `IsDark` property could be used to toggle between light and dark themes, while `LastTaskList` could be used to persist the last task list the user was viewing.

* Register the newly-defined configuration section by calling `Section<T>()` on `IConfigBuilder`:

```csharp
private IHost Host { get; }

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
var appBuilder = this.CreateBuilder(args)
.Configure(host => {
host
.UseConfiguration(configure: configBuilder =>
configBuilder
.EmbeddedSource<App>()
.Section<ToDoApp>()
);
});

Host = appBuilder.Build();
...
```

### 3. Write configuration values

* From any view model registered with the dependency injection (DI) container, you can now inject an instance of `IWritableOptions<T>` to write configuration values:

```csharp
public class SettingsViewModel
{
private readonly IWritableOptions<ToDoApp> _appSettings;

public SettingsViewModel(IWritableOptions<ToDoApp> appSettings)
{
_appSettings = appSettings;
}
...
```

* To update the `IsDark` property, create a method that calls `UpdateAsync()` on the injected instance:

```csharp
public class SettingsViewModel
{
private readonly IWritableOptions<ToDoApp> _appSettings;

public SettingsViewModel(IWritableOptions<ToDoApp> appSettings)
{
_appSettings = appSettings;
}

public async Task ToggleTheme()
{
await _appSettings.UpdateAsync(settings => settings with {
IsDark = !settings.IsDark
});
}
...
```

* Note that the `with` expression is used to create a new instance of the `ToDoApp` class with the updated value. This is because the `UpdateAsync()` method expects a function that returns a new instance of the class.

## Work in progress 🚧
* The configuration section that was registered is not required to exist in any source beforehand. Sections like these will be created automatically when you call `UpdateAsync()`.

### Have questions or feedback?
## See also

* Help us shape the documentation for this topic by providing feedback on the Uno.Extensions [repo](https://github.com/unoplatform/uno.extensions/discussions/categories/general)
* [Configuration](xref:Overview.Configuration)
* [Microsoft.Extensions.Configuration](https://docs.microsoft.com/dotnet/api/microsoft.extensions.configuration)
* [`IOptionsSnapshot<T>`](https://docs.microsoft.com/dotnet/api/microsoft.extensions.options.ioptionssnapshot-1)

0 comments on commit 702da39

Please sign in to comment.