Getting started with Firebase Cloud Firestore and LiveData

Cloud Firestore is a NoSQL database similar to the Realtime Database. It stores data in a structure that looks like a tree, but where data is stored as documents. It’s designed to overcome all of the drawbacks of the Realtime database.

The Firebase console is where you set up and manage your apps. From there, you can view all of your projects as well as create new ones.

To add the Cloud Firestore to your app, you only need to add one more dependency. Open up build.gradle file, if you haven't got it open and add the following dependency, making sure you're using the latest version:

implementation 'com.google.firebase:firebase-firestore:20.1.0'

You need a way to talk to the Cloud Firestore. Let's create CloudFirestoreManager class that helps us to manage connection and value fetching.

private const val POSTS_COLLECTION = "posts"

class CloudFirestoreManager {
    private val database = FirebaseFirestore.getInstance()
    private val postsValues = MutableLiveData<List<Post>>()
    private lateinit var postsRegistration: ListenerRegistration

    fun addPost(content: String, onSuccessAction: () -> Unit, onFailureAction: () -> Unit) {
        val documentReference = database.collection(POSTS_COLLECTION).document()
        val post = HashMap<String, Any>()

        post[AUTHOR_KEY] = authenticationManager.getCurrentUser()
        post[CONTENT_KEY] = content
        post[TIMESTAMP_KEY] = System.currentTimeMillis()
        post[ID_KEY] = documentReference.id

        documentReference
            .set(post)
            .addOnSuccessListener { onSuccessAction() }
            .addOnFailureListener { onFailureAction() }
    }

    fun onPostsValuesChange(): LiveData<List<Post>> {
        listenForPostsValueChanges()
        return postsValues
    }

    private fun listenForPostsValueChanges() {
        postsRegistration = database.collection(POSTS_COLLECTION)
            .addSnapshotListener(EventListener<QuerySnapshot> { value, error ->
                if (error != null || value == null) {
                    return@EventListener
                }

                if (value.isEmpty) {
                    postsValues.postValue(emptyList())
                } else {
                    val posts = ArrayList<Post>()
                    for (doc in value) {
                        val post = doc.toObject(Post::class.java)
                        posts.add(post)
                    }
                    postsValues.postValue(posts)
                }
            })
    }

    fun stopListeningForPostChanges() = postsRegistration.remove()

    fun updatePostContent(key: String, content: String, 
        onSuccessAction: () -> Unit, onFailureAction: () -> Unit) {

        val updatedPost = HashMap<String, Any>()
        updatedPost[CONTENT_KEY] = content
        database.collection(POSTS_COLLECTION)
            .document(key)
            .update(updatedPost)
            .addOnSuccessListener { onSuccessAction() }
            .addOnFailureListener { onFailureAction() }
    }

    fun deletePost(key: String, onSuccessAction: () -> Unit, onFailureAction: () -> Unit) {
        database.collection(POSTS_COLLECTION)
            .document(key)
            .delete()
            .addOnSuccessListener { onSuccessAction() }
            .addOnFailureListener { onFailureAction() }
    }
}

We can use this help class like so

class MainActivity : AppCompatActivity() {
    private val cfm by lazy { CloudFirestoreManager() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onStart() {
        super.onStart()
        listenForPostsUpdates()
    }

    override fun onStop() {
        super.onStop()
        cfm.stopListeningForPostChanges()
    }

    private fun listenForPostsUpdates() {
        cfm.onPostsValuesChange()
            .observe(this, Observer(::onPostsUpdate))
    }

    private fun onPostsUpdate(posts: List<Post>) {
        Log.d("TAG", posts)
    }
}