Solution 1 :
Like what Doug mentioned as a comment, you should instead return a Result
class from your repository. This is very easy in Kotlin.. but requires more work in Java.
First, create an abstract class Result
, with two subclasses, Success
and Error
.
abstract class Result {}
class Success extends Result {
private User mUser; // getters/setters omitted for brevity
public Success(User user) { mUser = user }
}
class Error extends Result {
private Error mError; // getters/setters omitted for brevity
public Error(Error error) { mError = error }
}
You can include more details in these classes to better understand what went wrong, but I’m keeping things simple.
Then, in your ViewModel, you should have 2 LiveData values. One for a success, and one for an error. So your AuthViewModel
will look like:
public LiveData<User> authenticatedUserLiveData;
public LiveData<Error> userSignInErrorLiveData;
public void signInWithEmail(String email, String password){
Result result = authRepository.firebaseSignInWithEmail(email, password);
if (result instanceof Success) {
authenticatedUserLiveData.value = result.getUser();
} else if (result instanceof Error) {
userSignInErrorLiveData.value = result.getError();
}
}
Finally, you just need to make your AuthRepository
return a Result
, so the signature will look like:
public Result firebaseSignInWithEmail(String email, String password)
And replace the line authenticatedUserMutableLiveData.setValue(user);
with return new Success(user);
and replace the logging line with return new Error(authTask.getException);
Hope this helps!
Problem :
I come from web development so my mobile development with Java isn’t on the right track yet.
I am currently developing an android app using Firebase, for university and I am trying to use clean architecture, and as everything new to me I am coming across some issues.
I’m looking for information on how to handle errors from input in MVVM architecture.
For example, say I am trying to login to Firebase using email and password, the View
gets the input text and calls the AuthViewModel
method signInWithEmail(String email, String password)
, which calls the AuthRepository
method firebaseSignInWithEmail()
.
Code
SignInFragment
private void signInWithEmail() {
String emailInput = binding.inputEmail.getText().toString();
String passwordInput = binding.inputPassword.getText().toString();
authViewModel.signInWithEmail(emailInput, passwordInput);
authViewModel.authenticatedUserLiveData.observe(getViewLifecycleOwner(), authenticatedUser ->{
if(authenticatedUser != null){
goToHome();
}
});
}
AuthViewModel
public LiveData<User> authenticatedUserLiveData;
public void signInWithEmail(String email, String password){
authenticatedUserLiveData = authRepository.firebaseSignInWithEmail(email, password);
}
AuthRepository
public MutableLiveData<User> firebaseSignInWithEmail(String email, String password) {
MutableLiveData<User> authenticatedUserMutableLiveData = new MutableLiveData<>();
firebaseAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(authTask -> {
if(authTask.isSuccessful()){
FirebaseUser firebaseUser = firebaseAuth.getCurrentUser();
if (firebaseUser != null) {
String uid = firebaseUser.getUid();
String name = firebaseUser.getDisplayName();
String firebaseUserEmail = firebaseUser.getEmail();
User user = new User(uid, name, firebaseUserEmail);
authenticatedUserMutableLiveData.setValue(user);
}
} else {
Log.w(TAG, "signInWithEmail:failure", authTask.getException());
}
});
return authenticatedUserMutableLiveData;
}
What I am trying to accomplish
The “correct” way of getting the ApiException
from the AuthRepository
, whcih is currently only being logged,
Log.w(TAG, "signInWithEmail:failure", authTask.getException());
and passing it to my AuthViewModel
to display the error on a TextView
, and not to enter the goToHome()
.
What I am thinking
I know viewmodel should only act as intermidiary and never have much logic of it’s own.
My first taught was to check if the returned MutableLiveData
from the repository method was null, but that left me without the ApiException since the LiveData
is of type User
, so I cannot return the ApiExcepiton.
Comments
Comment posted by developer.android.com/jetpack/docs/guide#addendum
Simply emit an object that contains either success or failure states. You can use the pattern described in the documentation:
Comment posted by article
If you are interested in a clean Firebase authentication with Google, you can check this
Comment posted by ImInYourCode
It gives me unexpected return value for
Comment posted by Ryan
Did you change the signature to return a
Comment posted by ImInYourCode
Yes I did change it
Comment posted by ImInYourCode
Out of the
Comment posted by Ryan
I’m a bit confused by what you mean.. if what you mean is it cannot return from within the