Multiplatform Settings version 0.7 is out!
Tags: kotlin, kotlinmultiplatform, news
Published: 2020-12-27 00:37:48

Today I’ve released version 0.7 of [Multiplatform Settings](https://github.com/russhwolf/multiplatform-settings)! There's a lot of new features in this one, including integrations with some of the kotlinx libraries which have been incubating in PRs for a long time, so I'm excited to hear what people think.

---

## TL;DR links

[Release Notes](https://github.com/russhwolf/multiplatform-settings/releases/tag/v0.7)
[Maven Central](https://search.maven.org/artifact/com.russhwolf/multiplatform-settings/0.7/pom)

---

Read on for more details on what's new.

## Serialization

I've had the idea in my head for a while that I'd like to generalize the existing delegate APIs to enable storing non-primitive data. Thanks to some suggestions from [Eugenio Marletti](https://twitter.com/workingkills) and [Leonid Startsev](https://github.com/sandwwraith) I came to realize how that could be done using kotlinx.serialization, by using its custom format APIs. There's been a PR open for a while which I iterated on as the serialization library adjusted those APIs and approached it's first stable release. Now that the library has reached 1.0, even if most of the encoder/decoder APIs are still experimental, it feels like the right time to release the integration with Multiplatform Settings.

With this, if you have a serializable class like 

```kotlin
@Serializable
class User(val nickname: String?)
```
you can save an instance to `Settings` by calling

```kotlin
settings.encodeValue(User.serializer(), "user", user)
```
and retrieve it with
```kotlin
val user: User = settings.decodeValue(
    User.serializer(), 
    "user", 
    defaultValue
)
val nullableUser: User? = settings.decodeValueOrNull(
    User.serializer(), 
    "user"
)
```
or you could do both via a delegate api
```kotlin
var user by settings.serializedValue(User.serializer(), "user")
```

Of course, it's no substitute for a structured database like sqlite if you need atomicity for more complex data, but for simple use-cases I hope people find it helpful. I also think it's also a neat demonstration of some less well-known parts of kotlinx.serialization, so I hope it can inspire other interesting custom formats as well.

## Coroutines

Similar to serialization, there's been a PR up for a while with `Flow` extensions on `ObservableSettings`. That's finally merged in 0.7, with two different artifacts depending on whether you use the `native-mt` branch of coroutines. I'm not sure having separate artifacts is totally necessary, but I figured it might help avoid people being forced to change which coroutines version they use just to interact with Settings. 

### Datastore

Until recently I thought `Flow` APIs would be the only interesting coroutine functionality worth adding to the library, but then the Android team released Jetpack DataStore. This includes a fully suspend-based key-value store intended to replace SharedPreferences. The existing `Settings` API isn't a great fit for `DataStore` since everything would need to be wrapped in `runBlocking` calls internally, which could easily catch consumers by surprise.

To better handle this with fewer surprises, I've added `SuspendSettings` and `FlowSettings` interfaces, which are essentially coroutine-based analogs of `Settings` and `ObservableSettings` (including that `FlowSettings` extends `SuspendSettings`). There's a `DataStoreSettings` which is the only direct implementation of `FlowSettings`, but there are also converters `Settings.toSuspendSettings()` and `ObservableSettings.toFlowSettings()` which wrap the non-coroutine interfaces in the suspending ones. This lets you use a single suspendable API from common if you're using DataStore on Android.

```kotlin
// Common
expect val settings: FlowSettings

// Android
actual val settings: FlowSettings = DataStoreSettings(/*...*/)

// iOS
actual val settings: FlowSettings = AppleSettings(/*...*/).toFlowSettings()
```

This feels like a lot of machinery just to interop with one API, but it seems important if DataStore is going to become the new key-value API of choice on Android. I've been watching its development closely and I'm interested to see if other async key-value libraries follow. If they do, Multiplatform Settings will be ready.

## Keychain

I've been asked numerous times about secure storage. Android provides [EncryptedSharedPreferences](https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences) as part of `androidx.security`, which can be passed as a delegate to `AndroidSettings` just like any other `SharedPreferences` implementation, but there's no simple equivalent way to get encrypted storage on iOS or any other existing Settings platform. In 0.7 there's a new `KeychainSettings` implementation available on Apple platforms, which stores data in the Keychain instead of in User Defaults. 

I'm not an expert in security or in the keychain APIs, so I'd appreciate feedback as to whether this fits your use-cases and security needs.

## Other little things

### Typed listeners

In a quality-of-life improvement that I meant to throw in a long time ago and forgot about, there are now extension functions to allow typed listeners for `ObservableSettings`. Where before you had to write code like
```kotlin
settings.addListener {
    val value = settings.getInt("myInt")
    // do something with value
}
```
now you can shorten it to
```kotlin
settings.addIntListener("myInt") { value ->
    // do something with value
}
```
### Sample updates

Previously, the sample project got a little too cute with method references and other syntax shortcuts and it made it hard to see what typical usage of the library looks like. I've refactored it to focus more on the core APIs so it's easier to poke around and understand what's going on. I'd like to do more here in the future, since there's a lot of the library currently not touched by the sample app.

### Desktop Linux

I've had a [PR open](https://github.com/russhwolf/multiplatform-settings/pull/54) for a while now with a desktop Linux Settings implementation based on QDBM. There's no good reason to use QDBM except that I originally prototyped it in the kotlin macos target which includes NDBM with very similar DBM-based APIs. I could merge it but I really have no idea if anyone would want to use it, or if wrapping some other API would be better. I'd very much like to hear from the desktop Linux dev community about what would be useful here since this is the only desktop platform currently missing a first-party implementation.

---

Hopefully that was a helpful tour of the major new developments in Multiplatform Settings 0.7. I don't get a huge amount of feedback, so please do reach out and let me know what is and isn't working for you in the library, and if there's other features you're looking for. You can leave a comment on this blog, reach out to me on [Twitter](https://twitter.com/russhwolf/) or [Kotlin Slack](https://kotlinlang.slack.com/archives/D2VU3UHU0), file a [Github issue](https://github.com/russhwolf/multiplatform-settings/issues/new), or start a thread on the brand new [Multiplatform Settings Github Discussions board](https://github.com/russhwolf/multiplatform-settings/discussions). And of course, be on the lookout for more updates to come!