⏱ Reading time: 7 min

As done in the first part, I’ve a question for you! There are a lot of possibilities to implement an Android app with database features, but what do you think if I told you that there’s a method with which you can add a database syncable across all the platform and also handle data from a web page? Yes, your right… Google and Firebase! Again! 🥳

I’m talking about Firebase Cloud Firestore. Cloud Firestore is a flexible, scalable NoSQL cloud database with the capabilities to sync data across all attached platform. In addition offer offline support both for mobile and for web that work regardless of network latency or Internet connectivity.

The Cloud Firestore data model supports flexible, hierarchical data structures. Store your data in documents, organized into collections. Documents support many different data types, from simple strings and numbers, to complex, nested objects and subcollections.

Tip: If you are totally new in non-relational database model, suggest you to give a look to the official Firestore documentation regarding the database model.

Querying is expressive, efficient, and flexible. You can retrieve data at the document level without needing to retrieve the entire collection, or any nested subcollections.

In this second part you will learn how to read/write the role of an employee from Cloud Firestore in your Android app.

Enable Cloud Firestore

In order to enable Cloud Firestore open the Firebase Console and select the project created in the first part of the tutorial.

Now click on Database section and you will see the top of the page like this:

Then click on Create Database.

You can now choose if you want to keep the database public or set usage rules, those rules are editable in a second time. Choose Start in locked mode, click on Enable and start inserting data in your database clicking on Add collection.

Firebase will present you a mask like:

The Collection ID is the main collection name (like table name in SQL database) then in our case we can insert roles.

After the Collection ID you have to insert the Document ID. This will be the name of the role (e.g. administrator), then insert the role name in the Document ID box. Finally, as you can see in the picture below, it’s already possible to insert Document data using Field.

In Field box insert users and select array as field type, finally insert all the UIDs you want for administrator role and once finished click on save.

If you want insert some other roles you have to click on Add document in the roles column and then start adding the users list as described above.

The final structure that will be obtained is similar to this:

Tip: To improve security of your database go to the Rules tab. In our example, we want to allow read/write to authenticated user then we have to use these security rules:

allow read: if request.auth.uid != null;
allow write: if request.auth.uid != null;

Like in the image above.

Great you’re ready to use Cloud Firestore in your Android app! Hurray! 💪

Coding Time 😎

First thing to do is add the Cloud Firestore dependency to Gradle with

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

then Sync Gradle and get ready to code! 💪

Let’s start from read operation!

In the previous part we’ve implemented the MainActivity that we will use to retrieve and show the role of the employee. We can create a method getUserRoleWithQuery which will retrieve data from Firebase Cloud Firestore using Cloud Firestore Queries.

First of all you can add to the BaseActivity a variable used to access the current user info for example the UID, like:

protected val currentUser: FirebaseUser?
    get() = auth.currentUser

Then in your MainActivity add the method getUserRoleWithQuery:

currentUser?.uid?.let { uid ->
   firestore
       .collection("roles")
       .whereArrayContains("users", uid)
       .get()
       .addOnCompleteListener { task ->
           task.result?.run {
               when (documents.size) {
                   1 -> {
                       val role  = documents[0].reference.path.split("/")[1]
                       user_role_text_view.text = "Your role is: $role"
                   }
                   else -> showError()
               }
           }
       }
       .addOnFailureListener { showError() }
} ?: showError()
Notes
  • firestore is an instance of FirebaseFirestore (FirebaseFirestore.getInstance()).
  • Method collection identify where to query for data.
  • Method whereArrayContains query for an array which contains a specific value (for example search in “users” array our users UID).
  • documents[0].reference.path identify the role path (for example roles/administrator) so we split this path to get only the role name.
  • We’re asserting that a user can assume only a role so if documents size is greater than one, something went wrong with the call or the currentUser is null i show an error.

Let’s finish with write operation!

Now your app can read the role from your database populated from Firebase Web page. However, why stop here if you can handle also the creation of a new user/role in your database directly from your app?

Before all you have to add three extensions and the onComplete listener implementation (for authentication create user method) in your SignInActivity so you can handle all the new user register flow.

The first extensions i’ve added in the example is Task<Void>.addCompleteListener() used to add the complete listener for add/update user request. It’s very simple and the result of my implementation is:

private fun Task<Void>.addCompleteListener() {
   addOnCompleteListener {
       MainActivity.newInstance(this@SingInActivity).let(::startActivity)
       finish()
   }.addOnFailureListener {
       FirebaseAuth.getInstance().signOut()
       Toast.makeText(baseContext, "Error saving user", Toast.LENGTH_SHORT).show()
   }
}
User add method

The second extensions i’ve added isDocumentReference.addNewUser(uid: String) so, when a role doesn’t exist in our database, we can add it with related user. In order to add both the role and the related user you have to use the set method present in the DocumentReference class which we are extending:

private fun DocumentReference.addNewUser(uid: String) {
   val userMap = HashMap<String, MutableList<String>>()
   userMap["users"] = mutableListOf(uid)
   set(userMap).addCompleteListener()
}

The set method accept a map as params then we create an HashMap where we store the array of user for the role. As you can see in the map we store a list of uid associated to the users key used by Firestore to create and name your array populated with your list.

Users array update

Third and last extension is DocumentReference.updateArray(uid: String) used to add a new user to the users array when inserted role already exist. To do so you can use the update method of DocumentReference:

private fun DocumentReference.updateArray(uid: String) {
   update("users", FieldValue.arrayUnion(uid)).addCompleteListener()
}

In this case you will use the update method that accept the array name you want to update and type of operation you are executing. As we want to keep the users already added to our array we must use the FieldValue.arrayUnion. As a result of this operation, Firestore will add new uids in array maintaining old users uid.

Finally you have to implement your onComplete listener. Most important thing here is check if inserted role exist in your Firestore database or not and select the correct extension to call like:

currentUser?.uid?.let { uid ->
   val role = roles_edit_text.text.toString()

   firestore
       .collection("roles")
       .document(role)
       .get()
       .addOnSuccessListener { docSnapshot ->
           if (docSnapshot.exists()) {
               docSnapshot.reference.updateArray(uid)
           } else {
               docSnapshot.reference.addNewUser(uid)
           }
       }
}

Now you have all the required method to start the registration flow using FirebaseAuth.

Final Step!

Last method to use is createUserWithEmailAndPassword of FirebaseAuth that will register effectively new user on Firebase. Hence you can call this method like:

auth.createUserWithEmailAndPassword(
   email_edit_text.text.toString(),
   password_edit_text.text.toString()
).addOnCompleteListener(this)

Conclusions

Great, all done! You have now a fully working app with authentication and database linked together.

Our journey exploring Firebase Authentication and Cloud Firestore is now over, thanks for reading and stay tuned for more article! 😎

You can find all the code in this Github repository! 🤩