How Does Android LiveData get() syntax work?

I wrote a util function for this logic:

import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
import kotlin.reflect.KProperty

fun <T> immutable(data: MutableLiveData<T>): Immutable<T> {
    return Immutable(data)
}

class Immutable<T>(private val data: MutableLiveData<T>) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): LiveData<T> {
        return data
    }
}

Then you can use in any of your ViewModel as:

private val _counter: MutableLiveData<Int> = MutableLiveData()    
val counter: LiveData<Int> by immutable(_counter)

or in short:

private val _counter = MutableLiveData<Int>()    
val counter by immutable(_counter)

In Kotlin we have multiple ways of exposing live data from ViewModel to the view.

class MyViewModel: ViewModel() {

    // Solution 1 - make MutableLiveData public
    // This approach works, but this is a bad idea because
    // view can modify the LiveData values
    val liveDataA1 = MutableLiveData<State>()

    // Solution 2 - let's make LiveData public (expose it instead of MutableLiveData)
    // Now from view perspective this solution looks fine, bu we have a problem,
    // because we need MutableLiveData within ViewModel to put/post new values to
    // the stream (we can't post values to LiveData).
    val liveDataA2 = MutableLiveData<State>() as LiveData<State>

    // Let's capture our requirements:
    // 1. We need to expose (immutable) LiveData to the view,
    // so it cannot edit the data itself.
    // 2. We need to access MutableLiveData from ViewModel to put/post new values.
    // Now, let's consider few appropriate solutions

    // Solution 3
    // Let's name mutable live data using underscore prefix
    private val _liveData3 = MutableLiveData<State>()
    val liveData3 = _liveData3 as LiveData<State>

    // Solution 4
    // We can also perform casting by specifying type for a variable
    // (we can do it because MutableLiveData extends LiveData)
    private val _liveData4 = MutableLiveData<State>()
    val liveData4: LiveData<State> = _liveData4

    // Solution 5
    // Starting from Kotlin 1.4-M.2 we can delegate call to another property
    private val _liveData5 = MutableLiveData<State>()
    val liveData5 by this::_liveData5

    // Solution 6
    // These above solutions work quite well, but we could do even better by
    // defining custom asLiveData extension function.
    private val _liveData6 = MutableLiveData<State>()
    val liveData6 = _liveData6.asLiveData()

    fun <T> MutableLiveData<T>.asLiveData() = this as LiveData<T>
    // Amount of code is similar, but notice that this approach works much better
    // with code completion.

    // Solution 7 (IMO Best)
    // We can also use alternative naming convention - use "mutableLiveData"
    // as variable for mutable live data instead of using underscore prefix
    private val mutableLiveData7 = MutableLiveData<State>()
    val liveData7 = mutableLiveData7.asLiveData()

    // BTW
    // We could also expose getLiveData8() method, but liveData is a state not an action.

    // Solution 9
    // This does not create backing field for the property
    // (more optimised but still Solution 7 is easier to use)
    private val _liveData9 = MutableLiveData<State>()
    val liveData9 get() = _liveData9 as LiveData<State>
}

get() is not related to Android.

val isRealtime: LiveData<Boolean>
    get() = _isRealtime

Here, get() is overriding the automatically-generated Kotlin getter function for the isRealtime property. So, instead of returning its own value, it returns the value of _isRealtime.

Personally, I recommend simpler syntax:

private val _isRealtime = MutableLiveData<Boolean>()
val isRealtime: LiveData<Boolean> = _isRealtime

The objective of either of these is to keep the mutability private, so consumers of this class do not accidentally update the MutableLiveData themselves.