Looks like Transformations.map() does not update d...
# android
t
Looks like Transformations.map() does not update depending LiveData in any case. What am I doing wrong? This works
Copy code
val contract: LiveData<Contract?> = MutableLiveData()
    val authState: LiveData<AuthState?> = MutableLiveData()
    init {
        contract.observeForever { contract ->
            (authState as MutableLiveData).value = when {
                tokenRepository.tokens.accessToken.isNullOrEmpty() -> AuthState.ANONYMOUS
                contract != null -> AuthState.LOGGED_IN_WITH_CONTRACT
                else -> AuthState.LOGGED_IN_WITHOUT_CONTRACT
            }.also { authState ->
                Timber.d("Refreshing auth state to $authState after contract live data update")
            }
        }
    }
But this doesn't
Copy code
val contract: LiveData<Contract?> = MutableLiveData()
    val authState = Transformations.map(contract)
    {
        when {
            tokenRepository.tokens.accessToken.isNullOrEmpty() -> AuthState.ANONYMOUS
            contract != null -> AuthState.LOGGED_IN_WITH_CONTRACT
            else -> AuthState.LOGGED_IN_WITHOUT_CONTRACT
        }.also { authState ->
            Timber.d("Refreshing auth state to $authState after contract live data update")
        }
    }
    init {
        contract.observeForever { Timber.d("Updated contract value to ${it?.contract}")}
    }
I would expect the logs from the observer in init method be followed trom the Transformations observer always. But sometimes I get the init method's only (setting contract.value=null).
w
Is something observing
authState
?
LiveData is very lazy, in that unless you observe, none of the transformations take place (afair)
t
Hi Łukasz, thanks for your hint. You're right, i am never listening to authState but reading its value directly. Was using LiveData by habit. But would have expected getValue() to provide the recent state. Will try out if this is the problem.
w
Yeah in tests you’ll need at least
authState.observeForever { }
, then you can do
getValue()
and actually get the value
t
Yes, that's it. Strange. would not have expected that.
Copy code
// run as androidTest
class LiveDataTryoutTest {
    @get:Rule
    val rule = InstantTaskExecutorRule()

    @Test
    fun test() {
        val d = MutableLiveData<String>()
        val t = Transformations.map(d) {
            d.value?.length
        }
        d.postValue("a")
        assertEquals("a", d.value)
        assertEquals(1, t.value) // fails
    }
}
w
Yes, that’s expected. The idea is that your transformations won’t run if the view is in background for example, which makes sense
t
OK. No lifecycle, no update
👌 1
Still a bit weird. Thought postponing updates for paused lifecycles was to avoid access to unavailable views. Internal values could sill update without danger. But it's like it is. now we know we can handle that.
w
Internal values could sill update without danger.
the idea was that they would update and consume resources unnecessarily. If the value gets updated 10 times when the view is in background, it doesn’t make sense to run 10 transformations if only the last one will be delivered to the view
t
Update could still happen on access (getValue()) only.
👍 1
OK, it behaves like that, we will have to take into account. Think behaviour is intended just I had the wrong expectation.
j
@Thomas Nordmeyer It seems as the intention of LiveData is only to observe data flows and not to actually retain data, however if you decide to use it that way, I’ve made this extension for it:
Copy code
fun <T> LiveData<T>.fetch(): T? {
        var value: T? = null
        val obs = Observer<T> { t -> value = t }
        this.observeForever(obs)
        this.removeObserver(obs)
        return value
    }
👌 2