Mapped LiveData returns null on getValue() call

192 Views Asked by At

I was building a viewModel in my project and and I wrote a LiveData set like this

private val _user = MutableLiveData<User>()
val user= _user 

val id = _user.map {
    if ( something ) {
        value 
    } else {
        null
    }
}

the problem I faced is that when I observe "id" in a Fragment/Activity , I can recieve the updates from "id" LiveData but if I try to get current value from "id" via "id.value" it returns null even if the "_user" liveData is updated.

Is there any reason for that ? am I missing something ?

2

There are 2 best solutions below

0
On

I encountered the same problem as you, and solved it by below code.

val EmptyObservers = mutableMapOf<Class<*>, Observer<*>>()

@MainThread
@Suppress("UNCHECKED_CAST")
inline fun <reified T> LiveData<T>.safeGetValue(): T? {
    return if (this is MediatorLiveData && !hasActiveObservers()) {
        val emptyObserver = EmptyObservers.getOrPut(T::class.java) {
            Observer<T> {  }
        } as Observer<T>
        observeForever(emptyObserver)
        value.also { removeObserver(emptyObserver) }
    } else {
        value
    }
}

Usage example:

data class User(val name: String, val id: Long)

private val _user: MutableLiveData<User?> = MutableLiveData()
val user: LiveData<User?> = _user

val id: LiveData<Long?> = Transformations.map(_user) {
    it?.id
}

fun getId() { 
    val userId = id.safeGetValue()
}
0
On

You are using operator = to assign value, this assignment will run on object creation and at this time _user has no value, id will be null forever whether _user is updated or not because it's a one-time assignment

Instead, use getter method, so the value is recalculate each time you call id:

val id
    get() = _user.map {
        if ( something ) {
            value 
        } else {
            null
        }
}