This blog article will illustrate the use and implementation of a ViewModel in the Open Event Android app. A ViewModel basically handles all the unneeded problems a user faces when there is a configuration change. This article will take the case of implementing a SignUp ViewModel for SignUp Fragment in the app. Configuration change means that if you have your app set at portrait mode at present and suddenly due to involuntary screen rotation, your app is now being presented in landscape mode, or vice-versa.
Before MVVM, if this type of change occurred, we used to use the onSaveInstance method to persist the data we wanted. But after the introduction of MVVM, the process has been simplified.
After the introduction of a class called ViewModel, we can directly handle all configuration changes now. Let us head onto how a ViewModel is used in the Open Event Android app.
What is a ViewModel ?
As per the definition given in the documentation, a ViewModel is a class which is designed to store and manage UI-related data in a lifecycle conscious way. If the system has a drastic change in its UI related component, the data which is being fetched should be stored in some place, like it needs to be persisted so that after the Ui component gets refreshed and formed again, there is no need to fetch the data yet again !
Implementing a ViewModel:
We use ViewModels regularly in our app code. For example, these are used whenever we code for a fragment or an activity. This is because, we need to persist the logic of accessing data, and those functions are present in a view model. The LiveData persists the data and thus configuration changes are handled. Let us see an example of a SignUpViewModel in the app.
Let us head onto the code of SignUpFragmentViewModel.
class SignUpFragmentViewModel(private val authService: AuthService) : ViewModel() { … } |
We clearly observe that our ViewModel takes in an input of an AuthService object. Let’s create our LiveData variables! ( We will assume that all this takes place inside the ViewModel class )
val progress = MutableLiveData<Boolean>() val error = SingleLiveEvent<String>() val signedUp = MutableLiveData<User>() val loggedIn = SingleLiveEvent<Boolean>() |
Progress variable is of MutableLiveData type. This means, that the progress is stored in the variable and it will persist even after configuration changes, error variable stores the error faced during SignUp (if any). signedUp variable contains the User data being fetched from the server. loggedIn variable is actually a boolean which contains the data whether the user is logged in or not. We declare two more variables for our use.
var email: String? = null var password: String? = null |
Now we head onto some functions to be utilised. The signUp function code is present below.
fun signUp(signUp: SignUp, confirmPassword: String) { email = signUp.email password = signUp.password if (hasErrors(email, password, confirmPassword)) return compositeDisposable.add(authService.signUp(signUp) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { progress.value = true }.doFinally { progress.value = false }.subscribe({ signedUp.value = it Timber.d(“Success!”) }, { error.value = “Unable to SignUp!” Timber.d(it, “Failed”) })) } |
This function is utilised in SignUpFragment. The variables email and password store the actual email and password in SignUpFragmentViewModel. We then utilise a RxAndroid call to make a POST request of creating a User in the server. The signedUp variable then stores the User created in the server. If an error is faced, the error message is stored in the LiveData.
Then finally we create the login function, which is actually called once a user has signed up successfully!
fun login(signUp: SignUp) { email = signUp.email password = signUp.password compositeDisposable.add(authService.login(email.nullToEmpty(), password.nullToEmpty()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe { progress.value = true }.doFinally { progress.value = false }.subscribe({ loggedIn.value = true Timber.d(“Success!”) }, { error.value = “Unable to Login automatically” Timber.d(it, “Failed”) })) } |
In this function we clearly set the value of LiveData loggedIn to true when the User actually logs in. Thus, we can clearly see the use of a ViewModel in an app. This is how SignUpFragmentViewModel is implemented in the Open Event Android app
Resources
Tags: GSoC18, FOSSASIA, Open Event, Android, MVVM, ViewModel, LiveData