Android Firestore Document addSnapshotListener as LiveData (lifecycle aware)

May 14, 2019
DocumentSnapshotLiveData

Extend LiveData

class DocumentSnapshotLiveData(
    private val docRef: DocumentReference
) : LiveData<Resource<DocumentSnapshot>>(), EventListener<DocumentSnapshot> {

    private var registration: ListenerRegistration? = null

    override fun onEvent(snapshot: DocumentSnapshot?, e: FirebaseFirestoreException?) {
        // setValue
        value = if (e != null) {
            Resource(e)
        } else {
            Resource(snapshot!!)
        }
    }

    override fun onActive() {
        super.onActive()
        registration = docRef.addSnapshotListener(this)
    }

    override fun onInactive() {
        super.onInactive()
        registration?.also {
            it.remove()
            registration = null
        }
    }
}
class Resource<T> private constructor(
    private val data: T?,
    private val error: Exception?
) {

    val isSuccessful: Boolean
        get() = data != null && error == null

    constructor(data: T) : this(data, null)

    constructor(exception: Exception) : this(null, exception)

    fun data(): T {
        if (error != null) {
            throw IllegalStateException("Check isSuccessful first: call error() instead.")
        }
        return data!!
    }

    fun error(): Exception {
        if (data != null) {
            throw IllegalStateException("Check isSuccessful first: call data() instead.")
        }
        return error!!
    }
}

Usage

fun getLiveData(val userId: String): DocumentSnapshotLiveData {
    val docRef = firestore.collection("users").document(userId)
    return DocumentSnapshotLiveData(docRef)
}
val userId = ...

getLiveData(userId).observe(owner, Observer { result ->
    if (result.isSuccessful) {
        // do something
        val doc = result.data()
    }
    else {
        Timber.e(result.error())
    }
})

NOTE: Refer to Firestore addSnapshotListener with LifecycleOwner.

Kotlin Extension

fun DocumentReference.asSnapshotLiveData(): DocumentSnapshotLiveData {
    return DocumentSnapshotLiveData(this)
}
fun getLiveData(val userId: String): DocumentSnapshotLiveData {
    return firestore.collection("users").document(userId)
        .asSnapshotLiveData()
}

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.