Firebase authentication – Using ViewModel

In the previous chapter we have implemented the Task::class, what is an extension function of the Firebase’s Task object. We made it, to handlethe Firebase quries with Kotlin coroutines, because they are long running tasks. Secondly we have made an interface to handle the needed methods for query of the registration.

For the interface we made an implementation also, where we have the queries for the authentication and for the Firestore “users” collection, where we are going to store some more information about the users. These informations are stored during the run of the application in a User data class.

What is still missing, is the usage of the ViewModel, which will be responsible to get the data from the repository and notify the user interface about the changes and vice versa. Call the functions after the users’ interactions in the app.

GitHub

If you haven’t done the previous chapters, then from GitHub you can download the starter project for this chapter.

GitHub – register_with_email branch

What is ViewModel? 🤔

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.

ViewModel is a class that is responsible for preparing and managing the data for an Activity or a Fragment. It also handles the communication of the Activity / Fragment with the rest of the application (e.g. calling the business logic classes).

A ViewModel is always created in association with a scope (a fragment or an activity) and will be retained as long as the scope is alive. E.g. if it is an Activity, until it is finished.

In other words, this means that a ViewModel will not be destroyed if its owner is destroyed for a configuration change (e.g. rotation). The new instance of the owner will just re-connected to the existing ViewModel.

The purpose of the ViewModel is to acquire and keep the information that is necessary for an Activity or a Fragment. The Activity or the Fragment should be able to observe changes in the ViewModel.

ViewModels usually expose this information via LiveData or Android Data Binding. You can also use any observability construct from you favorite framework.

ViewModel’s only responsibility is to manage the data for the UI. It should never access your view hierarchy or hold a reference back to the Activity or the Fragment.

Source: ViewModel Overview / ViewModel

Step 1 – Create the ViewModel class

In general, we will make a ViewModel class for each screen in our app. This ViewModel class will hold all of the data associated with the screen and have getters and setters for the stored data. This separates the code to display the UI, which is implemented in our Activities and Fragments, from our data, which now lives in the ViewModel. In our case we gonna create a singleton ViewModel for the repository, because this ViewModel will be the only one, which handles the Firebase’s queries and data changes.

In the next chapter we gonna implement Koin to have a singleton ViewModel class.

So, let’s create a ViewModel class for Firebase. The ViewModel class will be located next to the UserRepository::interface so inside of the “repository” package.

Click with the right mouse button on the package of “repository”, then “New” and finally “New Kotlin file/class” option. In the popup window name our new file as “FirebaseViewModel”.

Step 2 – Create LiveData

We gonna create 3 LiveData‘s and for them 3 MutableLiveData‘s in the newly created FirebaseViewModel::class as member variables.

LiveData is a data holder class that can be observed within a given lifecycle. This means that an Observer can be added in a pair with a LifecycleOwner, and this observer will be notified about modifications of the wrapped data.

MutableLiveData is a subclass of LiveData which is used for some of it’s properties (setValue/postValue) and using these properties we can easily notify the ui when onChange() is called. Only using LiveData object we can’t do this. Because of this we gonna create the MutableLiveData as a private variable and we will set it’s value only inside of the ViewModel class. In the user interface we gonna observe only the LiveData, which will get the value from the private MutableLiveData.

In the consciousness of this we are going to create a LiveData and MutableLiveData to show a Toast message, which will notify the user, when the registration, the login or the log out was successful. The second LiveData variables will be for a spinner. It will be visible in case of a UI blocking work. The third one will contain the current user as a User object.

Next to the LiveDatas we gonna have one more variable, which will make an instance about the UserRepositoryImpl::class to reach the methods there.


LiveData and MutableLiveData

Step 3 – Scopes in ViewModels

A CoroutineScope keeps track of all coroutines it creates. Therefore, if we cancel a scope, we cancel all coroutines it created. This is particularly important if we are running coroutines in a ViewModel. If our ViewModel is getting destroyed, all the asynchronous work that it might be doing must be stopped. Otherwise, we will waste resources and potentially leaking memory. If we consider that certain asynchronous work should persist after ViewModel destruction, it is because it should be done in a lower layer of our app’s architecture.

AndroidX lifecycle v2.1.0 introduced the extension property viewModelScope to the ViewModel class. It manages the coroutines in our FirebaseViewModel::class.

A ViewModelScope is defined for each ViewModel in our app by default. Any coroutine launched in this scope is automatically canceled if the ViewModel is cleared. Coroutines are useful here for when we have work that needs to be done only if the ViewModel is active. For example, if we make a network call, we should scope the work to the ViewModel so that if the ViewModel is cleared, the work is canceled automatically to avoid consuming resources.

We can access the CoroutineScope of a ViewModel through the viewModelScope property of the ViewModel.

launchDataLoad()

In this method we will show for the user the Spinner at the bottom of the screen, when they start an operation, which will block the main thread.

First it will set the “_spinner” MutableLiveData to true, so until the “block()” code is running, it will be true. In the activities we are going to implement an oberver, which will get this value, when it changes, and it will set the Spinner’s visibility to true.

Add this method to the FirebaseViewModel::class.


launchDataLoad

Step 4 – Clear the toast message

As we have talked about earlier, when a work is done, a Toast will notify the user about the result. In the user interface we gonna implement an observer for the “spinner” LiveData. After the successful notification we have to clear the LiveData, otherwise the Toast gonna be visible every time, when we open a new Activity or Fragment, where we have implemented an Observer.

So this code goes again to the FirebaseViewModel::class.


onToastShown

Step 5 – Start MainActivity

Before we start implementing the repository call in our ViewModel, we have to create one more function inside of it. This method will handle an Intent, which will start the MainActivity::class, when the registration, the login was successful.


startMainActivitiy

Step 6 – Create a User object

OK OK, one more method before the implementation of the registration. This method will create a new User object, when the registration and the login was successful.


createUserObject
Note the third argument of this function. There we have added a default value. In this case we don’t have to specify this argument, when we call it. It means, we can leave it empty.

More info here: Default and named arguments

Step 7 – Registration using email

Finally here we go, the implementation of the registration function. In this step we are going to implement the Firebase authentication. If it was successful, then we gonna save the user with the autogenerated user id from the registration in Firestore.

We will encampsulate the method first in the “launchDataLoad” method. Remember? This will show for the user on the user interface a Spinner during the UI blocking work. Inside of it we gonna use again the viewModelScope.

Check out the registerUserFromAuthWithEmailAndPassword method in the UserRepository::class. What we gonna get back from this method? A Result object.

It means in our case that we gonna use the “when expression”.

More info here: When expression

Using this we can observe what kind of data we got back from this method. So we will identify 3 cases.

      • is Result.Success
      • is Result.Error
      • is Result.Canceled

registerUserFromAuthWithEmailAndPassword
Result.Success

When the registration was successful, then we gonna get back Result.Success answer. In this case first we gonna call on the “result” variable the ” .let” lambda. With this call we can avoid null. This is a safe call, which won’t be executed, when the value of the variable is null.

Inside of the “.let” we will call the function of Firestore, which will create a document inside of the “users” collection for the fresh created user.

Result.Error

When something went wrong, then we will get back an error message. In this case we will notify the user about the error using the “toast” LiveData.

Result.Canceled

We are going to do the same if the request was canceled. We gonna inform the user about the error if do we have one.

Step 7 – New document in Firestore

As we have talked about earlier, we gonna save the user in Firestore as well. There we can set much more fields about them. For example birthday, registration date, username, full name, gender… In our case we will save only the name and the userId.

There we have added one more field, the profilePhoto. In this example we won’t upload any images to Firebase.

The logic here gonna be the same. We will get back a Result object and we will analyze it.


createUserInFirestore
Result.Success

Later on, when we gonna implement the login, we will call this function also. In the LoginActivity::class we gonna have also a Facebook and Google authentication. For them the code of the registration will be the same, what we gonna use for the login. So in this case it can happen, that the user is still not registered. If this is the case, then the app gonna save the user in Firestore as well.

In case if the user is already registered, then no new user will be created, because in Firestore can’t be stored documents with the same id.

Result.Error and Result.Canceled

In these cases we gonna only inform the user about the error. Same what we gonna do in case of the Firebase authentication.

Step 8 – The validation methods

The implementation of the FirebaseViewModel::class has been finished for this chapter. Now open the RegisterActivity::class. We will continue the tutorial there.

We have to define some rules for the registration. Remember? We have implemented in our user interfaces the fields using TextInputLayout’s togheter with TextInputEditText’s.

They have an option to show for the user an error message if the format of the typed text is not, what we want to have.

In the user interface, so in the activity_register.xml and activity_login.xml files we have set the error messages to true.

app:errorEnabled=“true”

You can play with the rules, but currently we gonna use these rules. Copy and paste them after the “onCreate” method in you RegisterActivity::class.


The validation methods

Step 8 – setOnClickListener

We are getting closer and closer to the end. In this step first we gonna call the “setOnClickListener” lambda method on the “btn_register_login” Button. This will be called, when the user taps on the “Register” Button.

Instance of the FirebaseViewModel

Before the implementation of this method, we have to create an instance about the FirebaseViewModel::class. As a member variable create it as a lateinit var.

private lateinit var firebaseViewModel: FirebaseViewModel

Then in the onCreate() method, after the line of …

setContentView(R.layout.activity_register)

… initialize our ViewModel like this.


firebaseViewModel
registerUserFromAuthWithEmailAndPassword

Inside of the setOnClickListener{} lambda method we gonna check first the content of the name, email and password fields using an if-statement, then we gonna call on the firebaseViewModel variable the registerUserFromAuthWithEmailAndPassword() method.


setOnClickListener

Run the app

Now we can run the app. For this you can use a virtual device or a real one too. I use my real phone.

After a successful build and run, fill out all of the fields.

When the app works for you perfectly too, then if you click on the “Register” button, then you should be redirected to the MainActivity:class. Now, if you open the project in Firebase, you should see the registered user in the Authentication section:

… and in the Firestore database too:

Step 9 – Observation

Toast

What still is missing, is to notify the user about the registration. If you remember, in the FirebaseViewModel::class we have set the “toast” LiveData, when we get back the result.

To get the notification, we gonna implement an Observer object in the RegisterActivity::class, and inside of the Observer we gonna have a Toast. This Toast will show the message from the register methods.

So just add this line to the RegisterActivity::class.


The Observer
Spinner

As we have talked about already, when the user clicks on the “Register” button, then a spinner will be shown for the user, that they have to wait until the UI heavy work will be finished. For this we have implemented also a LiveData. So we have to make for it only an Observer in the RegisterActivity::class.

Paste this code after the Observer of the Toast!


AndroidManifest.xml

Run again the app

If you run again the app, and you gonna try to register using the same email address, then you will be notified, that this email is already in use.

Just open the project in Firebase, then the authentication, and if you move the mouse to the line of the registered email, then at the right side of the row 3 dots gonna be visible. Click on them and delete the email from Firebase.

To continue the Step 9, add the FirebaseViewModel instance to the MainActivity::class as well. If you are done with it, then add the previous observer the MainActivity::class also. After that we will see, that the registration and login was successful.

Why to use Koin?

In the next chapter we gonna refactore our code to use Koin as a dependency injection framework.

Why?

We have implemented the Observer for the toast LiveData in the MainActivity::class and in the RegisterActivity::class as well. Because of this we gonna be notified in every case. But if we do have the Toast only in the MainActivity::class then we won’t get the notification. Why? It is because, when we move to the next activity, then the preious ones will be destroyed, where we have the instance of the ViewModel, so it will be destroyed as well. This instance should notify us. So the instance of the ViewModel in the MainActivity::class won’t know anything about the ViewModel in the RegisterActivity::class.

In Koin we gonna implement in a Modul the FirebaseViewModel::class as a single instance. It means, that we will have only one instance in the whole app.

This will be the topic of the next chapter.

GitHub

If you are lost after this long chapter, then you can check the source code on our GitHub page.

GitHub – the_viewmodel branch

Questions

I hope the description was understandable and clear. But if you have still questions, then leave me comments below! 😉

Have a nice a day! 🙂

 


 

Follow and like us:
Click to rate this post!
[Total: 0 Average: 0]

Leave a Reply

Your email address will not be published. Required fields are marked *