This is one in a series of articles about Time Tortoise, a Universal Windows Platform app for planning and tracking your work schedule. For more on the development of this app and the ideas behind it, see my Time Tortoise category page.
Last week, I described the settings infrastructure that I’m building for Time Tortoise. This week: implementation details.
Settings and ISettings
One of my design goals for settings is that individual settings should be accessible as properties. For example, to retrieve the idle timeout setting, I could use this code:
if (duration.TotalSeconds >= _settings.IdleTimeoutSeconds)
The goal is for settings to be type-safe (not all strings), and visible to the programmer at design-time. Type _settings.
in a C# file in Visual Studio, and all available settings are shown in a list.
_settings
is of type ISettings
, and it contains one property per setting. This does mean that when I add a new setting, I have to update the class and its interface. I can’t just add it to the settings text file and have it picked up automatically at runtime. But I don’t think that’s much of a disadvantage.
Since Settings
implements an interface, unit tests can use mock settings. This allows each unit test to returns a custom value for each settings, based on the scenario being tested. No settings text file is necessary in this case.
SettingsUtility and ISettingsUtility
The SettingsUtility
class takes care of reading from the settings text file, updating an instance of the Settings
class, and exposing the Settings
class as a property.
Reading the settings file using YamlDotNet is simple:
var input = File.ReadAllText(_settingsFileName);
var deserializer = new Deserializer();
Settings = deserializer.Deserialize<Settings>(input);
Line 1 reads a text file into a string. Line 2 creates the YAML deserializer. Line 3 deserializes the YAML string into the Settings
object. Now we have an object full of settings.
The YAML format is as simple as a text file can be. If the Settings
class has a property called IdleTimeoutSeconds
, then this line in the YAML file will be deserialized into that property:
IdleTimeoutSeconds: 3
The string-to-integer conversion happens automatically.
Since SettingsUtility
implements the ISettingsUtility
interface, unit tests can provide a mock utility class that doesn’t actually read a text file from disk, and just exposes a mock Settings
class with the appropriate settings values.
Notice that I haven’t mentioned any utility functions for updating settings. That’s because my text file UI design relies on a text editor to do the updates. That saves time on app UI that I can use for other features. And the target audience for this app is expected to be comfortable with a text editor. In my case, even as a user, I would rather edit settings in a text editor than fiddle with the app UI.
View Model Changes
Since SettingsUtility
reads a file from disk, I decided the best place for it was in the Model. When a View Model class needs access to settings, it accepts a SettingsUtility
instance in its constructor (the dependency injection pattern). This allows unit tests that are testing that view model class to mock the settings classes.
Once a view model has the settings dependency available, it can use it to retrieve the settings values that it needs.
(Image credit: Didriks)