Nullability and LiveData with Kotlin

13,816

Solution 1

I created an extension property. It's not super pretty, but it is pretty straightforward.

val <T> LiveData<T>.valueNN
    get() = this.value!!

Usage

spinner.loading = myLiveData.valueNN.homeState.loading

I'm not sold on appending "NN" as a good naming convention, but that's beyond the scope of the question :)

Solution 2

I little improve answer The Lucky Coder. This implementation cannot accept null values at all.

class NonNullMutableLiveData<T: Any>(initValue: T): MutableLiveData<T>() {

    init {
        value = initValue
    }

    override fun getValue(): T {
        return super.getValue()!!
    }

    override fun setValue(value: T) {
        super.setValue(value)
    }

    fun observe(owner: LifecycleOwner, body: (T) -> Unit) {
        observe(owner, Observer<T> { t -> body(t!!) })
    }

    override fun postValue(value: T) {
        super.postValue(value)
    }    
}

Solution 3

I don't know if this is the best solution but this is what I came up with and what I use:

class NonNullLiveData<T>(private val defaultValue: T) : MutableLiveData<T>() {

    override fun getValue(): T = super.getValue() ?: defaultValue

    fun observe(owner: LifecycleOwner, body: (T) -> Unit) {
        observe(owner, Observer<T> {
            body(it ?: defaultValue)
        })
    }
}

Creating the field:

val string = NonNullLiveData("")

And observing it:

viewModel.string.observe(this) {
    // Do someting with the data
}

Solution 4

You can create an extension for LifecycleOwner

fun <T> LifecycleOwner.observe(liveData: LiveData<T?>, lambda: (T) -> Unit) {
    liveData.observe(this, Observer { if (it != null) lambda(it) })
}

and then in your fragment/activity

observe(liveData) { ... }

Solution 5

You can do this

normalLiveData
  .nonNull()
  .observe(this, { result -> 
    // result is non null now
  })

There is an article about it. https://medium.com/@henrytao/nonnull-livedata-with-kotlin-extension-26963ffd0333

Share:
13,816
ligi
Author by

ligi

https://ligi.de

Updated on July 07, 2022

Comments

  • ligi
    ligi almost 2 years

    I want to use LiveData with Kotlin and have values that should not be null. How do you deal with this? Perhaps a wrapper around LiveData? Searching for good patterns here .. As an example:

    class NetworkDefinitionProvider : MutableLiveData<NetworkDefinition>() {
        val allDefinitions = mutableListOf(RinkebyNetworkDefinition(), MainnetNetworkDefinition(), RopstenNetworkDefinition())
    
        init {
            value = allDefinitions.first()
        }
    
        fun setCurrent(value: NetworkDefinition) {
            setValue(value)
        }
    }
    

    I know value will not be null when accessing - but I will always have to check for null or have these ugly !!'s around.

  • David Whitman
    David Whitman about 6 years
    The downside of this is that if somebody DOES put a null into your NonNullLiveData, you emit to your observers a value that's completely wrong (the defaultValue). Depends maybe on your situation, but I'd rather have a crash if someone puts a null into a non-null place.
  • Maks
    Maks almost 6 years
    Thats excellent, thanks for posting this! The AS linter complained about the overridden fun's not actually doing anything, which made me think of wrapping the setValue and postValue in value.let {...} to prevent null values being set into the LiveData.
  • Admin
    Admin almost 6 years
    Just playing devil's advocate, but are you absolutely sure that getValue() is guaranteed to return a non-null? Obviously with this implementation there's no way a client could set the value to null, but is there anything internally within LiveData that could possibly null the value?
  • Admin
    Admin almost 6 years
    Don't do this. Simply force-unwrapping the underlying value is dangerous because this.value can be null. Only when you enforce non-nullability in the setters can you safely force-unwrap like the other answers suggest.
  • David Whitman
    David Whitman almost 6 years
    @Weikardzaena From the OP: "I know value will not be null when accessing". Although what I did was to subclass LiveData and have a private inner MutableLiveData so there is no way that any outside classes can change the value of my LiveData to null.
  • Anigif
    Anigif over 5 years
    One of the problems with this approach is that Observer will allways be called right away when observed. In the standard LiveData implementation, it won't be called until value is set the first time, but in this example value is always set right away. @Weikardzaena AFAIK that won't be the case since value is set intially