Hi guys, whats the negative points of doing that: ...
# coroutines
j
Hi guys, whats the negative points of doing that:
Copy code
val state: StateFlow<String> = stateFlow(initialValue = "Loading...") {
        val result = getData("state")
        getAnotherData(result)
    }

    private fun <T> stateFlow(
        initialValue: T,
        dispatcher: CoroutineDispatcher = <http://Dispatchers.IO|Dispatchers.IO>,
        callback: suspend CoroutineScope.() -> T,
    ): StateFlow<T> {
        val scope = CoroutineScope(SupervisorJob() + dispatcher)
        val stateFlow = MutableStateFlow(initialValue)
        scope.launch(dispatcher) {
            stateFlow.update { callback() }
        }
        return stateFlow.asStateFlow()
    }
The idea is to “clone”
liveData
builder,
k
For one, unless you always explicitly pass in a dispatcher then this code won’t be properly testable since it has a static dependency on
<http://Dispatchers.IO|Dispatchers.IO>
.
If your callback is ever something that requires being unregistered on completion, then your abstraction has no way of unregistering.
callbackFlow { … }.stateIn(scope, initial)
would be able to invoke a cleanup when the channel gets cancelled and thus unregister your callback.
j
The idea is to have dispatcher as optional. I was also thinking about of use
ViewModel.stateFlow
to use
viewModelScope
to launch coroutines
k
viewModelScope
is now discouraged by Google because is has a static dependency on
Dispatchers.Main
and makes testing impossible without
Dispatchers.setMain
and
Dispctahcers.resetMain
.
A similar situation to your potential static dependency on
<http://Dispcatchers.IO|Dispcatchers.IO>
.
Oh also your function breaks structured concurrency
A better builder that avoids this problem would be something like:
Copy code
private fun <T> stateFlow(
  initial: T,
  scope: CoroutineScope,
  callback: ProducerScope<T>.() -> Unit
): StateFlow<T> = callbackFlow(callback).stateIn(scope, initial)
But at this point the builder isn’t very useful when you could just call those two functions manually
j
Got it. The initial idea is to avoid boilerplate:
Copy code
val state2: StateFlow<String> = flow<String> {
        val result = getData("state")
        getAnotherData(result)
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(),
        initialValue = "Loading..."
    )
👍 1
Do you have an suggestion to avoid it?
k
If you're worried about the small amount of boilerplate, use the example I gave above.
1
l
Boilerplate can almost always be simplified/moved as a function in Kotlin.