dave08
04/25/2023, 12:11 PMStateFlow
from a callback (the first state is queried and the callback is just to listen for changes to that first state - but I need to unregister from the callback when the flow is cancelled...)? I know there's stateIn
, but I'd rather do it w/o having to pass a coroutine context there...CLOVIS
04/25/2023, 12:13 PMstateIn
. It's not possible to do it without stateIn
, because a callback is cold, and a StateFlow is hot (= it maintains its own state even when no one is looking at it). That means it has to run somewhere, which is in a coroutine (thus it must have a CoroutineScope
)dave08
04/25/2023, 12:17 PMMutableStateFlow
and manage the registration to the callback there... when it's run I would return the StateFlow
, but then I'd have a problem with cancellation not unregistering.dave08
04/25/2023, 12:17 PMdave08
04/25/2023, 12:18 PMcoroutineScope { launch { } }
, to run it on the coroutine scope of the caller function (this function can be suspend)dave08
04/25/2023, 12:19 PMCLOVIS
04/25/2023, 12:19 PMdave08
04/25/2023, 12:20 PMdave08
04/25/2023, 12:20 PMdave08
04/25/2023, 12:21 PMdave08
04/25/2023, 12:22 PMCLOVIS
04/25/2023, 12:22 PMonStart { emit("Your default value") }
on any cold flow (it will still be a cold flow, but it will have that first value instantly available) → https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/on-start.htmldave08
04/25/2023, 12:23 PMCLOVIS
04/25/2023, 12:23 PMCLOVIS
04/25/2023, 12:23 PMfun yourFlow() = flow {
…
}.onStart { … }
dave08
04/25/2023, 12:25 PMsomeFlow: Flow<X>
as a parameter, which doesn't tell the implementer of that flow that a first value is required... it's like using a regular List as an input parameter to a function when you mean a NonEmptyList...dave08
04/25/2023, 12:26 PMCLOVIS
04/25/2023, 12:26 PMStateFlow
? In your version, it is not stateful, as its state only exists while the call to the other function is active, after which it becomes frozen foreverdave08
04/25/2023, 12:29 PMfun provider() = MutableStateFlow<X>().apply {
state = X()
// register listener to return X updates
this.asStateFlow()
}
suspend fun receiver(states: StateFlow<X>) {
// I have an assurance that a first value is available already
states.collect {
//// process...
}
}
// usage:
receiver(provider())
// In tests I'm forced to make a provider with a first value too...
CLOVIS
04/25/2023, 12:31 PMCLOVIS
04/25/2023, 12:34 PMdave08
04/25/2023, 12:35 PMdave08
04/25/2023, 12:35 PMCLOVIS
04/25/2023, 12:37 PMCLOVIS
04/25/2023, 12:37 PMonStart
and adds some documentationdave08
04/25/2023, 12:38 PMdata class NonEmptyFlow<T>(val firstState: T, val flow: Flow<T>)
🤒CLOVIS
04/25/2023, 12:39 PM@JvmInline
value class NonEmptyFlow<T> internal constructor(private val flow: Flow<T>) : Flow<T> by flow
fun <T> Flow<T>.withDefault(block: suspend () -> T) = this
.onStart { emit(block()) }
.let { NonEmptyFlow(it) }
CLOVIS
04/25/2023, 12:42 PMflow.withDefault { 1 }.withDefault { 2 }.toList()
, it will print [2, 1, <everything else>]
, so "default" is probably not the right namedave08
04/25/2023, 12:43 PMby flow
, so a value class
wouldn't help?CLOVIS
04/25/2023, 12:44 PMFlow<T>
, then yes, for sureCLOVIS
04/25/2023, 12:44 PMdave08
04/25/2023, 12:45 PMgildor
04/25/2023, 3:41 PMdave08
04/25/2023, 3:47 PMgildor
04/25/2023, 3:49 PMCLOVIS
04/25/2023, 3:51 PMstateIn(GlobalScope)
because you lose all control over the running operation / memory freeinggildor
04/25/2023, 3:52 PMgildor
04/25/2023, 3:53 PMgildor
04/25/2023, 3:55 PMgildor
04/25/2023, 3:57 PM