https://kotlinlang.org logo
#getting-started
Title
# getting-started
m

Mateusz Holak

11/23/2020, 3:55 PM
Hi. I have some troubles to do some transformations like extension of LiveData has one
Copy code
LiveData<X>.map {} => transform type MutableLiveData<DomanType> -> LiveData<UiType>
I would like replace LiveData with StateFlow but i need to transform domain type to UI type that i have for each layers. Which one method i should use ? The same method "map" in Flow return Flow<R> but i need StateFlow<T>. I can also do transormation when i have data from useCase and then assign MutableStateFlow to StateFlow by invoking asStateFlow but its a little worse solution than i have with LiveData right now. Thanks for any advice. I found similar question here: https://github.com/Kotlin/kotlinx.coroutines/issues/2008
a

Adam Powell

11/23/2020, 4:15 PM
Can you show some of the code you are trying to convert? In most of these situations a StateFlow as the output type of operators is not necessary or desirable. LiveData doesn't have a choice about this thanks to other design decisions, but this is largely a drawback of LiveData rather than something to emulate elsewhere.
👍 1
m

Mateusz Holak

11/23/2020, 4:24 PM
Yes, of course. This is what i achieved with MutableStateFlow: In ViewModel:
Copy code
private val _characters by lazy {
    MutableStateFlow<List<Character>>(emptyList()).also {
        getCharacters(it)
    }
}

val characters: StateFlow<List<CharacterDisplayable>> by lazy {
    _characters.map {
            characters ->
        characters.map { CharacterDisplayable(it) }
    }.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
}
In Fragment
Copy code
private fun observeCharacters() {
    lifecycleScope.launch {
        viewModel.characters.collect {
            showCharacters(it)
        }
    }
}
And this is version with LiveData
Copy code
private val _characters by lazy {
    MutableLiveData<List<Character>>().also {
        getCharacters(it)
    }
}

val characters: LiveData<List<CharacterDisplayable>> by lazy {
    _characters.map { characters ->
        characters.map { CharacterDisplayable(it) }
    }
}
Only i can tell you that is works fine but Im not sure assigns MutableStateFlow to StateFlow is correct, specially given arguments in stateIn operator because just barely i found it what mentioned Elizarov in topic that i linked in previous comment 😛 Can you make some review these snippets of codes ?
a

Adam Powell

11/23/2020, 5:34 PM
You have both the untransformed and transformed data as state in both variants. LiveData forces you to, but you likely only need one to be stateful. What other consumers of
_characters
exist in that class?
m

Mateusz Holak

11/23/2020, 6:39 PM
Im using it on Android. I need read only property publish for ui to observe it. _characters is assign when useCase is invoked, and then character point to _characters which is mutable. Only one consumers is UI from fragment binded with viewmodel. The data saved configuration changes with this example above but im not sure its correct solution or example of replacing livedata with StateFlow.
a

Adam Powell

11/23/2020, 6:51 PM
if you have no other consumers of the untransformed data, instead of assigning
_characters.value
with the original list, why not perform the transformation directly when you would otherwise assign it? i.e.
Copy code
private val _characters = MutableStateFlow<List<CharacterDisplayable>>(emptyList())
fun setCharacters(characters: List<Character>) {
    _characters.value = characters.map { CharacterDisplayable(it) }
}
val characters: StateFlow<List<CharacterDisplayable>> get() = _characters
this lets you get rid of a lot of complexity
🤩 1
m

Mateusz Holak

11/23/2020, 7:06 PM
Perfect 🙂 Why not doing that in setter of _characters. I did it conversion in the same way in useCase method. Thanks. Anyway, what do you think above way with stateIn ?
a

Adam Powell

11/23/2020, 7:06 PM
I do not think that you need stateIn at all 🙂
👍 1
m

Mateusz Holak

11/23/2020, 7:08 PM
Copy code
val characters: StateFlow<List<CharacterDisplayable>> get() = _characters.asStateFlow()
What are differences when i will have more than one consumers in that case ?
a

Adam Powell

11/23/2020, 7:18 PM
More than one consumer of the
CharacterDisplayable
list? None, except it'll be more efficient this way. 🙂 If other code needs the untransformed
List<Character>
then things change a bit, but not much. You still want to keep things simple without intermediate StateFlows where you can.
❤️ 1
m

Mateusz Holak

11/23/2020, 7:28 PM
In that case is much better, thanks a lot.
👍 1