0

How can I get the latest value of a Flow? I don't have a StateFlow where I need that latest value. This is the condensed scenario:

There is a repository exposing a StateFlow

val repositoryExposedStateFlow: StateFlow<SomeType> = MutableStateFlow(...)

Additionally there are mappers transforming that StateFlow like

val mappedFlow: Flow<SomeOtherType> = repositoryExposedStateFlow.flatMapLatest { ... }

mappedFlow is no StateFlow anymore, but just a Flow. Thus, I cannot get the latest/current value as I can when there's StateFlow.

Anyhow, I need the latest value in that Flow at some point. Since this point is not in a ViewModel, but some Use Case implementation, I cannot simply perform a stateIn and hold the latest value in the ViewModel all the time the ViewModel is alive -- otherwise I had to pass on the value to all Use Cases. Actually, within a Use Case I trigger a network refresh which leads to emitting of new values on the StateFlow and thus on the mappedFlow, too.

In the Use Cases I have CoroutineScopes though. So I came up with

suspend fun <T> Flow<T>.getState(): T {
    return coroutineScope {
        val result = stateIn(
            scope = this
        ).value
        coroutineContext.cancelChildren()

        result
    }
}

Without using coroutineContext.cancelChildren() the method will never return, because coroutineScope blocks the caller until all child coroutines have finished. As stateIn never finishes, I manually cancel all children.

Apparently this is a bad thing to do.

But how can I solve this problem in a better way? In my perception the problem arises from StateFlow mapping resulting in regular Flow instances.

Edric
  • 24,639
  • 13
  • 81
  • 91
ceedee
  • 371
  • 1
  • 11
  • 1
    "How can I get the latest value of a Flow?" -- you don't, unless you cache it yourself somewhere. "I need the latest value in that Flow at some point" -- perhaps the solution is to revisit this assumption. – CommonsWare Oct 21 '22 at 15:16
  • Yes, it feels like I'm doing something wrong at another place. See the discussion with @Tenfour04 where we're trying to find that out. – ceedee Oct 21 '22 at 15:54

1 Answers1

0

Yes, all you need is to call first() on the flow. Since it is backed by a StateFlow upstream, the first() call will get the current value of that backing StateFlow, run it through whatever transformations happen from the downstream operators, and return that value.

This effectively gets you the same result as your attempt above.

The downside is that all the downstream operators must be run, so it is potentially expensive.

This is only possible if there is an upstream StateFlow. Otherwise, there is no concept of a latest value for you to be able to retrieve.

I would challenge your need to get the latest value, though. Typically, you collect flows, so you're already working with a current value. Flows are intended for reactive programming.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • Thanks for pointing out `first()`! Back to your challenge: on the one hand I want to display the latest state of the `repositoryExposedStateFlow` in the UI, so I need a reactive construct. On the other hand, when invoking a UI action I trigger some network request which will cause emitting new data on `repositoryExposedStateFlow`, but in this UI action I need to wait until that new data arrived in the Flow. – ceedee Oct 21 '22 at 15:52
  • The only other approach I can think of is to terminate that UI action and store some state that is combined with upcoming updates of `repositoryExposedStateFlow`. Seems like some overkill to me, so I was looking for an alternative. -- Hope I could explain my case well enough. – ceedee Oct 21 '22 at 15:52
  • I'm not sure I understand. "but in this UI action I need to wait until that new data arrived in the Flow" sounds reactive too. But if there really is no way around it, I don't understand why you don't use `stateIn` on the downstream flow. If you want latest state, that's the easiest way to hold it, so it wouldn't be overkill. – Tenfour04 Oct 21 '22 at 16:06
  • Probably I will have to create an example project, otherwise it could be difficult to get to the ground of things. Will post a repo link :-) – ceedee Oct 21 '22 at 17:07