Skip to content

Instantly share code, notes, and snippets.

@ZakTaccardi
Created May 9, 2020 01:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ZakTaccardi/dfe2679238b673ead610e9ed4bd8b49c to your computer and use it in GitHub Desktop.
Save ZakTaccardi/dfe2679238b673ead610e9ed4bd8b49c to your computer and use it in GitHub Desktop.
NoInitialStateFlow.kt
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
/**
* A variant of [StateFlow] that allows an initial state
*/
interface NoInitialStateFlow<T> : Flow<T> {
/**
* Throws an exception if called when this [NoInitialStateFlow] has not been initialized with a state yet.
* @see [StateFlow.value]
*/
val value: T
/**
* Returns the [value], or `null` if this [NoInitialStateFlow] has not been initialized yet
*/
val valueOrNull: T?
/**
* Returns the [value] immediately if this [NoInitialStateFlow] has been initialized, or suspends until
* it is initialized
*/
suspend fun value(): T
}
/**
* A variant of [MutableStateFlow] that allows an initial state to be set after construction
*/
interface MutableNoInitialStateFlow<T> : NoInitialStateFlow<T> {
/**
* See [MutableStateFlow.value]
*/
override var value: T
}
/**
* Constructor for [MutableNoInitialStateFlow]
*/
@Suppress("FunctionName")
fun <T> MutableNoInitialStateFlow(): MutableNoInitialStateFlow<T> = NoInitialStateFlowInternalImpl()
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
@Suppress("FunctionName")
internal fun <T> NoInitialStateFlowInternalImpl(): MutableNoInitialStateFlow<T> {
val stateFlowDeferred = CompletableDeferred<MutableStateFlow<T>>()
val flow = flow {
emitAll(
stateFlowDeferred.await()
)
}
.conflate()
return object : MutableNoInitialStateFlow<T>, Flow<T> by flow {
override var value: T
get() = if (stateFlowDeferred.isCompleted) {
stateFlowDeferred.getCompleted().value
} else {
error("Called `.value()` before it was set. Maybe try `.valueOrNull()`?")
}
set(value) {
if (stateFlowDeferred.isCompleted) {
stateFlowDeferred.getCompleted().value = value
} else {
stateFlowDeferred.complete(MutableStateFlow(value))
}
}
override val valueOrNull: T?
get() = if (stateFlowDeferred.isCompleted) {
stateFlowDeferred.getCompleted()
.value
} else {
null
}
override suspend fun value(): T = stateFlowDeferred.await()
.value
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment