DEV Community

Cover image for Looking toward Multiplatform Settings 1.0.0
Russell Wolf
Russell Wolf

Posted on

Looking toward Multiplatform Settings 1.0.0

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!

GitHub logo russhwolf / multiplatform-settings

A Kotlin Multiplatform library for saving simple key-value data

Linux Build Status Mac Build Status Windows Build Status Test Results

Maven Central

Multiplatform Settings

This is a Kotlin library for Multiplatform apps, so that common code can persist key-value data.

Table of contents

Adding to your project

Multiplatform Settings is currently published to Maven Central, so add that to repositories.

repositories {
    mavenCentral()
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Then, simply add the dependency to your common source-set dependencies

commonMain {
    dependencies {
        // ...
        implementation("com.russhwolf:multiplatform-settings:0.9")
    }
}
Enter fullscreen mode Exit fullscreen mode

See also the sample project, which uses this structure.

Usage

The Settings interface has implementations on the Android, iOS, macOS, watchOS, tvOS, JS, JVM, and Windows platforms. (Note that the two JVM implementations and the Windows implementation are currently marked as experimental.)

Creating a Settings instance

…

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

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 or the linked issue. I'd also like to hear what else is missing from these APIs. You can leave feedback in this issue

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

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

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. 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!

Discussion (0)