Looking toward Multiplatform Settings 1.0.0
Tags: kotlin, kotlinmultiplatform
Published: 2022-07-25 00:02:18


The first version of Multiplatform Settings was released in May 2018. At that time I imagined I would leave it in some form of prerelease 0.x state until Kotlin/Native and Kotlin Multiplatform were fully stable. But it's been over four years now, and plenty of other libraries in the ecosystem have gone 1.0. With the Kotlin Multiplatform Mobile beta on the horizon, I've decided it's about time for Multiplatform Settings to have a stable release.

In preparation for that, I recently released version `1.0.0-alpha01`. This includes a number of breaking changes in order to make the API a bit more consistent and intuitive. I've done my best to provide migration aids in the form of `@Deprecated` annotations which in most cases will allow users to update automatically. But if for some reason you're not ready to do that, version `0.9` was also released recently with almost all the same functionality and none of the breakage.

If you've never heard of Multiplatform Settings or you'd like to take a closer look, be sure check it out on Github!

{% embed https://github.com/russhwolf/multiplatform-settings %}

## Breaking Changes

Here's a quick tour of breaking changes and other updates so you know what to expect.

### Implementation class renames

In the early days, there was generally a single implementation of the `Settings` class per platform, so I gave them names like `AndroidSettings`, `AppleSettings`, and `JsSettings`. This naming scheme couldn't really hold as more implementations like `KeychainSettings` and `DataStoreSettings` were added. For the sake of consistency, for 1.0 the platform-based names are all being changed to mark the backing API that the implementation uses. The full list of renames is below.

| Old name | New name |
| --- | --- |
| AndroidSettings | SharedPreferencesSettings |
| AppleSettings | NSUserDefaultsSettings |
| JvmPreferencesSettings | PreferencesSettings |
| JvmPropertiesSettings | PropertiesSettings |
| WindowsSettings | RegistrySettings|
| MockSettings | MapSettings |

### Update listener changes

The original `ObservableSettings` interface was based around an untyped `addListener()` function, with the consumer responsible for reading out the current value at the observed key when the listener was called. This was awkward to use, so  typed extension functions like `addIntListener()` were added later to do this work for you, but the core API remained. However, I don't see much use-case for the untyped API, and it makes it more difficult to convert between `ObservableSettings` and `FlowSettings`, which only includes typed APIs. So for 1.0 the untyped `addListener()` will be removed and the extension functions moved into the `ObserableSettings` interface. 

If you maintain a custom `ObservableSettings` implementation, the migration hopefully shouldn't be too bad because you can leave your untyped `addListener()` as an internal implementation detail and just call it from the typed methods. This is also what most of the library classes are doing now.

### Default values

Going back to the very first release of the library, there are many places across the Multiplatform Settings API surface that take a `defaultValue` parameter which is returned if the key in question isn't present. These `defaultValue` parameters almost always have default parameter values, so you could call `settings.getInt("key")` which would do the same as `settings.getInt("key", defaultValue = 0)`. More recently, nullable getters were added so you can do things like `settings.getIntOrNull("key")` which will return `null` if `"key"` isn't set. This makes for a slightly confusing API where you might be surprised that `getInt("key")` returns `0` instead of `null`, so I've opted to remove the default parameter value from the non-nullable getters. This is slightly more verbose in the event that you wanted the library's default values but I think it makes for fewer surprises.

### Removing `multiplatform-settings-coroutines-native-mt`

When I first published the coroutines interop APIs, I created two separate modules in an attempt to accomodate people using both the mainline and `native-mt` coroutines versions. With the new Kotlin/Native memory model on the horizon and understanding that the `native-mt` branch of `kotlinx-coroutines` will no longer be maintained, I removed the `multiplatform-settings-coroutines-native-mt` module in `1.0.0-alpha01`. If you were using it, you should migrate to `multiplatform-settings-coroutines` instead.

### Removing experimental markers

Some, but not all, things that were previously marked as `@ExperimentalSettingsApi` or `@ExperimentalSettingsImplementation` are no longer annotated to indicate that they will be stable in 1.0. This includes the `ObservableSettings` and `SettingsListener` interfaces, and the `PreferencesSettings` and `PropertiesSettings` implementations. 

Some items remain experimental in 1.0 to indicate that they still aren't well-tested or to allow for the possibility of breaking changes in the future. These include the coroutine and serialization interop APIs, as well as `RegistrySettings` on Windows and `KeychainSettings` on Apple platforms. I'm eager for more feedback on all of these things so that they too can become stable, so please let me know what is and isn't working well for you.

## The road ahead  

After the 1.0 release, the next goal will be stabilizing the APIs and implementations that are still experimental. You can help by providing feedback, fixing bugs, improving testing, or suggesting (and possibly helping implement) other changes.

### Coroutines integration

For the `multiplatform-settings-coroutines` APIs, I want to know if the introduction of `SuspendSettings` and `FlowSettings` is helpful. I worry a little that it's a lot of API to accomodate one implementation (`DataStoreSettings`), but the alternative is wrapping a lot of `runBlocking` which feels worse. I like the current state of things where you can use the non-coroutine interfaces with internal `runBlocking` if you want by calling `toBlockingSettings()` or `toBlockingObservableSettings()`, because it makes the blocking explicit and opt-in. But I'd like to hear if it works for you too. You can leave feedback in [this issue](https://github.com/russhwolf/multiplatform-settings/issues/119)

### Serialization integration

For the `multiplatform-settings-serialization` APIs, I want to know if people find this integration useful. I've already had some good feedback around adding `remove()` and `contains()` analogs for serializable values, and there's a PR open that serves as a proof-of-concept. I'd love to hear feedback on [that PR](https://github.com/russhwolf/multiplatform-settings/pull/117) or the [linked issue](https://github.com/russhwolf/multiplatform-settings/issues/81). I'd also like to hear what else is missing from these APIs. You can leave feedback in [this issue](https://github.com/russhwolf/multiplatform-settings/issues/120)


### Apple Keychain integration

The `KeychainSettings` implementation is experimental because it hasn't been heavily battle-tested yet. If you've tried it, please let me know if it meets your security needs. If you or someone you know is well-versed in the keychain APIs, I'd love a review of whether the implementation is doing everything it needs to in terms of security. You can leave any of this feedback in the [related issue](https://github.com/russhwolf/multiplatform-settings/issues/121)

### Windows implementation

The `RegistrySettings` implementation on Windows is experimental largely because I want to add `ObservableSettings` support, and I want to reserve the possibility of breaking changes until that's in place. If you have ideas about this or want to help out, let me know in the [related issue](https://github.com/russhwolf/multiplatform-settings/issues/51)

### Linux implementation

At the moment, there is no desktop Linux `Settings` implementation. I've created various prototypes over the years, but it's not a development environment I spend much time in so I have no insight into what's useful. You can see some of the things I've tried in the PRs linked from [this issue](https://github.com/russhwolf/multiplatform-settings/issues/113). The feedback that's most important to me is what backing API would most useful from an interop perspective if you have some shared and some platform-specific code. It doesn't have to be one of the ones I've already tried.

## What's next for 1.0?

Ok that was a lot of text. Thanks for sticking with me as I braindump the state of the library and the roadmap to 1.0 and beyond.

I think I'd like to aim the final 1.0 release for around the Kotlin 1.8 timeframe, which will be sometime this fall. So I'd love to hear what folks think of these changes, and of the library overall. If there's anything else you think should change while the library is still in the pre-1.0 stage, now is the time to let me know!