DEV Community

loading...
Cover image for Multiplatform Settings version 0.7 is out!

Multiplatform Settings version 0.7 is out!

russhwolf profile image Russell Wolf ・5 min read

Today I’ve released version 0.7 of 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
Maven Central


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 and Leonid Startsev 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

@Serializable
class User(val nickname: String?)
Enter fullscreen mode Exit fullscreen mode

you can save an instance to Settings by calling

settings.encodeValue(User.serializer(), "user", user)
Enter fullscreen mode Exit fullscreen mode

and retrieve it with

val user: User = settings.decodeValue(
    User.serializer(), 
    "user", 
    defaultValue
)
val nullableUser: User? = settings.decodeValueOrNull(
    User.serializer(), 
    "user"
)
Enter fullscreen mode Exit fullscreen mode

or you could do both via a delegate api

var user by settings.serializedValue(User.serializer(), "user")
Enter fullscreen mode Exit fullscreen mode

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.

// Common
expect val settings: FlowSettings

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

// iOS
actual val settings: FlowSettings = AppleSettings(/*...*/).toFlowSettings()
Enter fullscreen mode Exit fullscreen mode

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

settings.addListener {
    val value = settings.getInt("myInt")
    // do something with value
}
Enter fullscreen mode Exit fullscreen mode

now you can shorten it to

settings.addIntListener("myInt") { value ->
    // do something with value
}
Enter fullscreen mode Exit fullscreen mode

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 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 or Kotlin Slack, file a Github issue, or start a thread on the brand new Multiplatform Settings Github Discussions board. And of course, be on the lookout for more updates to come!

Discussion (0)

pic
Editor guide