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
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?)
you can save an instance to Settings
by calling
settings.encodeValue(User.serializer(), "user", user)
and retrieve it with
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
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.
// 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 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
}
now you can shorten it to
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 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!
Top comments (0)