multiplatform-settings
multiplatform-settings copied to clipboard
feature request: coroutines: Add generic getFlow / getOrNullFlow
In Operators.kt, there is a generic get/set which works for primitive types.
/**
* Get the typed value stored at [key] if present, or return null if not. Throws [IllegalArgumentException] if [T] is
* not one of `Int`, `Long`, `String`, `Float`, `Double`, or `Boolean`.
*/
public inline operator fun <reified T : Any> Settings.get(key: String): T? = when (T::class) {
Int::class -> getIntOrNull(key) as T?
Long::class -> getLongOrNull(key) as T?
String::class -> getStringOrNull(key) as T?
Float::class -> getFloatOrNull(key) as T?
Double::class -> getDoubleOrNull(key) as T?
Boolean::class -> getBooleanOrNull(key) as T?
else -> throw IllegalArgumentException("Invalid type!")
}
It would be convenient to have the same functionality for the flow/coroutine getters too, so it can be kept in sync with the supported primitive getters/setters.
For my use-cases, I implemented it as follows:
@Suppress("UNCHECKED_CAST")
@OptIn(ExperimentalSettingsApi::class)
private inline fun <reified T> ObservableSettings.getOrNullFlow(key: String): Flow<T?> = when (T::class) {
Int::class -> getIntOrNullFlow(key)
Long::class -> getLongOrNullFlow(key)
String::class -> getStringOrNullFlow(key)
Float::class -> getFloatOrNullFlow(key)
Double::class -> getDoubleOrNullFlow(key)
Boolean::class -> getBooleanOrNullFlow(key)
else -> throw IllegalArgumentException("Invalid type!")
} as Flow<T?>
inline fun <reified T> ObservableSettings.getFlow(key: String, defaultValue: T): Flow<T> =
getOrNullFlow<T?>(key).map { it ?: defaultValue }
My underlying use-case is a getMutableStateFlow for a setting, so it can easily be observed/set from compose UI.
Implementation for the curious
private inline fun <reified T> ObservableSettings.getMutableStateFlow(
key: String,
defaultValue: T,
coroutineScope: CoroutineScope,
): MutableStateFlow<T> {
val flow = getFlow(key, defaultValue)
val initialValue = runBlocking { flow.first() }
val stateFlow = MutableStateFlow(initialValue)
coroutineScope.launch {
flow.collectLatest { value ->
stateFlow.value = value
}
}
coroutineScope.launch {
// drop(1) so the default value isn't reapplied to ObservableSettings
stateFlow.drop(1).collectLatest { newValue ->
this@getMutableStateFlow[key] = newValue
}
}
return stateFlow
}