RxJava on the Sign In Screen

Before I dive into an example of using RxJava, I’d like to talk a little bit about my journey trying to grok RxJava. What exactly is this thing called RxJava?

RxJava is a Java VM implementation of ReactiveX (Reactive Extensions): a library for composing asynchronous and event-based programs by using observable sequences.

When I first read this it didn’t make a whole lot of sense. So I decided to read blog after blog about the topic and look at the reference material https://github.com/ReactiveX/RxJava/wiki . I realized there are so many different ways that RxJava can be used that the only way I could truly learn was by going over examples. Along the way I created a list of bullet points, that I picked out from all the material that I read, that helped to define RxJava. I highly recommend you do the same. Here is my list of notes on the topic : https://gist.github.com/lawloretienne/40959a8d95c8d0bf4675999d07df3c0c

Once I started doing this, I started becoming more and more comfortable with using RxJava. I am by no means an expert on this subject matter. However, I would like to share a recent problem I was trying to solve and how RxJava helped me solve it.

A common screen that appears in almost every app that requires you to have an account is the Sign In screen. I wanted to set up form validation so that only when all fields are valid then a Sign In button would be enabled. That way the client takes care of this responsibility and doesn’t put the onus on the server.

I set up three checks. A check for the validity of the email input field, a check for the validity of the password input field, and a check for the validity of all fields.

Below are the business rules for the checks.

Check #1

- Do not display errors for an empty email input field.
- If there currently is an error immediately clear it as the email input field changes. 
- If the user starts typing, don’t immediately check for validity but instead wait for 400 ms to pass after the last change was made to the email input field. 
- If the validity check returns true clear the error view from the email input field, otherwise display the error view on the email input field.

Check #2

- Do not display errors for an empty password input field.
- If there currently is an error immediately clear it as the password input field changes. 
- If the user starts typing, don’t immediately check for validity but instead wait for 400 ms to pass after the last change was made to the password input field. 
- If the validity check returns true clear the error view from the password input field, otherwise display the error view on the password input field.

Check #3

- Do not enable the SignIn button until both the email input field and password input field are valid.

Here is what those checks look like.

Check #1

Subscription emailSubscription = emailChangeObservable
.doOnNext(new Action1<CharSequence>() {
@Override
public void call(CharSequence charSequence) {
hideEmailError();
}
})
.debounce(400, TimeUnit.MILLISECONDS)
.filter(new Func1<CharSequence, Boolean>() {
@Override
public Boolean call(CharSequence charSequence) {
return !TextUtils.isEmpty(charSequence);
}
})
.observeOn(AndroidSchedulers.mainThread()) // UI Thread
.subscribe(new Subscriber<CharSequence>() {
@Override
public void onCompleted() {

}

@Override
public void onError(Throwable e) {
e.printStackTrace();
}

@Override
public void onNext(CharSequence charSequence) {
boolean isEmailValid = validateEmail(charSequence.toString());
if (!isEmailValid) {
showEmailError();
} else {
hideEmailError();
}
}
});

Check #2

Subscription passwordSubscription = passwordChangeObservable
.doOnNext(new Action1<CharSequence>() {
@Override
public void call(CharSequence charSequence) {
hidePasswordError();
}
})
.debounce(400, TimeUnit.MILLISECONDS)
.filter(new Func1<CharSequence, Boolean>() {
@Override
public Boolean call(CharSequence charSequence) {
return !TextUtils.isEmpty(charSequence);
}
})
.observeOn(AndroidSchedulers.mainThread()) // UI Thread
.subscribe(new Subscriber<CharSequence>() {
@Override
public void onCompleted() {

}

@Override
public void onError(Throwable e) {
e.printStackTrace();
}

@Override
public void onNext(CharSequence charSequence) {
boolean isPasswordValid = validatePassword(charSequence.toString());
if (!isPasswordValid) {
showPasswordError();
} else {
hidePasswordError();
}
}
});

Check #3

Subscription signInFieldsSubscription = Observable.combineLatest(emailChangeObservable, passwordChangeObservable, new Func2<CharSequence, CharSequence, Boolean>() {
@Override
public Boolean call(CharSequence email, CharSequence password) {
boolean isEmailValid = validateEmail(email.toString());
boolean isPasswordValid = validatePassword(password.toString());

return isEmailValid && isPasswordValid;
}
}).observeOn(AndroidSchedulers.mainThread()) // UI Thread
.subscribe(new Observer<Boolean>() {
@Override
public void onCompleted() {

}

@Override
public void onError(Throwable e) {
e.printStackTrace();
}

@Override
public void onNext(Boolean validFields) {
if (validFields) {
enableSignIn();
} else {
disableSignIn();
}
}
});

The complete example is here : https://github.com/lawloretienne/RxSignIn . It’s never too late to start learning the Rx approach. You just have to be willing to dive in, make mistakes, and learn by example.

RxJava Resources

Official Docs

https://github.com/ReactiveX/RxJava/wiki

Videos

Helpful Tools

http://rxmarbles.com/

Blog Posts

http://fernandocejas.com/2015/01/11/rxjava-observable-tranformation-concatmap-vs-flatmap/

https://www.bignerdranch.com/blog/what-is-functional-reactive-programming/

http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/

http://andraskindler.com/blog/2013/using-rxjava-in-android/

http://joluet.github.io/blog/2014/07/07/rxjava-retrofit/