this is weird, why is it only when I add that fina...
# coroutines
o
this is weird, why is it only when I add that final
isNetworkError
field that I start to get this error? if I remove it the error will be gone…
literally just that last field
as if it can only handle 5 ?
oh actually that’s true, it only does 5…
can I not use one where its actually vararg and not just 5 ?
Copy code
val searchState = combine(
        arrayOf(
            searchText,
            cities,
            showProgressBar,
            showClearButton,
            isOtherError,
            isNetworkError
        ).asIterable()
    ) {
        SearchState(
            it[0] as String,
            it[1] as List<City>,
            it[2] as Boolean,
            it[3] as Boolean,
            it[4] as Boolean,
            it[5] as Boolean
        )
    }
lol
anything cleaner?
h
listOf?
Oh do you mean the casts? Not really except implementing the function by yourself and do the casts there..
n
Kinda looks like you are recombining `Flow`s that are derived from the same source info. Like
isOtherError
and
isNetworkError
. Since these are read by separate coroutines, they are read in parallel, with no mechanism to keep them in sync, so you could end up with a
SearchState
that doesn’t make sense, because some of the source `Flow`s haven't caught up, mixing stale data with new data. Keep data together if it's used together, even if that means creating some extra data classes. The grouping will avoid data getting out of sync and you'll have less parameters into your
combine
because the parameter count will grow with number of dependencies, not the number of properties in your resulting object.
o
I see
it sounds good but I have no idea what it means in practice
n
If you have
Copy code
class MyClass {
    val someInternalStateOrDependency: Flow<Exception> = ...

    val thisInt: Flow<Int> = someInternalStateOrDependency.map { it.theInt }
    val thatString: Flow<String> = someInternalStateOrDependency.map { it.theString }
}

val otherFlow = combine(myClass.thisInt, myClass.thatString, ...) { theInt, theString, ... -> ... }
then swap that out for
Copy code
class MyClass {
    val someInternalStateOrDependency: Flow<Exception> = ...

    val allTheDataNeededFromHere: Flow<MyData> = someInternalStateOrDependency
        .map { MyData(it.theInt, it.theString) }
}

val otherFlow = combine(myClass.allTheDataNeededFromHere, ...) { (theInt, theString), ... -> ... }
Does that make sense?
o
makes a lot of sense actually thanks!
yea, no, I don’t get it
no idea what MyClass in this case should be, or someInternalState or anything really the only thing that I recognize is the final result of how you’d use 1 field that has all of the values, but I dont know how to get there
n
The key part is that there is an upstream source shared by theInt and theString, so instead of using a flow for each, I create one flow that calculates both and puts them in a data class.
o
sure but why is it of type Exception?
n
Could be any type, that was just reflecting my guess that your error booleans might be based off an exception.
o
ah I see
so but there’s just 1 flow that should be of type MyData then
n
That’s most likely
o
not that I’ve been attempting this day and night, but I keep coming back to attempt it and I am unable to do what you’re suggesting, I keep getting confused as to what
someInternalStateOrDependency
is, can you show me how you would write it for my case? Literally? if you have time
you don’t need all of that apparently, solved it in a much simpler way
n
Honestly, I lost track of the beginning of the thread earlier and didn't remember the screen shot. An example of multiple values from the same source would be
searchText
,
showProgressBar
,
showClearButton
. They all are reflective of the
onSearchTextChanged
value. After
searchText.value = text
, your data is inconsistent since
showProgressBar
and
showClearButton
are not updated yet. This is the sort of thing where you want data that was calculated based off the same event (can be a method call like you have or an item in some dependent
Flow
) to be together. So you could do a
SearchState
data class, with a
MutableStateFlow<SearchState>
that you set at once. Relooking at your example, instead of combine, you could go with a
mySearchState: MutableStateFlow<SearchState>
and in your methods that update the state, you can call:
Copy code
val isNotEmpty = text.isNotEmpty()
mySearchState.update {
    it.copy(
        searchText = text,
        showProgressBar = isNotEmpty,
        showClearButton = isNotEmpty
    )
}
Maybe this or something similar is what you ended up with. Btw, when you do find a simpler solution that works for you, it's nice to actually post what it was, so others benefit from what you figured out.
o
That's literally what I ended up with Nick
How you so good