Reactive Programming Made Simple Part 2
Let’s quickly recap what we covered in part 1 of this tutorial: Reactive programming revolves around the idea of streams. These streams have three key parts: Observables (beginning), Operators (middle), and Observers (end). And we can send data through a stream by first creating an observable and then subscribing to it with an observer. Got it? Then on to part 2!
Sending Data Down the Stream:
Let’s think of a few use cases where we would benefit from using reactive streams. We already talked about a button click. It’s a scenario where we set up logic for reacting to a user’s action regardless of whether it happens a minute or two hours from now. How about something else that we can’t guarantee will happen instantly? How about that pesky internet and its imperfect connections for all users?
Network requests are a perfect opportunity for this. If a user’s internet is slow, you don’t want to accidentally skip past the part of your app that handle’s a requests response. You need to be sure that the logic that takes place using the response happens after the response is actually available!
That’s a solid use case for your app, but before we start an example, let’s quickly go back to the big O we haven’t talked about yet: Operators
Operators are the middle of the pipe. They allow us to manipulate and transform data, what may enter the pipe as a String, could come out as an Integer or a User object. With the RxJava and RxSwift libraries we have a plethora of operators to choose from. And what’s great about our tube is there’s no limit to how long it can be. That means we can chain as many operators together as we want.
The map Operator:
There are a number of ReactiveX operators that can be used to transform a stream. This post would be far too long if we covered them all, but documentation and diagrams are available. Instead let’s look at just two here to get an idea of how they work.
The map operator is one that applies a function of your choosing to each item in the stream. In other words, you define a functions logic, and every piece of data that is emitted from the observable will go through that logic. Let’s create a new observable like we did in part one of this series and add a map operator to the stream.
Observable<String> observable = Observable
.just(4, 3, 5)
.map(x -> 2 * 5);
What this means is any piece of data that goes through the operator will be multiplied by two. (note: If you’re unfamiliar with lambdas, here’s a good resource. They’re pretty essential for writing code like this is a clean, readable manner). When this whole observable is set up and subscribed to, the pieces of data (4, 3, and 5) will each go through the stream and hit the map operator. When this happens one at a time they take on the role of x, and then after being transformed continue along the stream. So the end of this stream outputs the data 8, 6, 10.
The filter operator:
Another common operator is filter, and this one does exactly what it sounds like. As data in the stream passes through this operator it is either allowed to continue or is filtered out. This is done with a Predicate if-statement that, or in simpler lambda terms checks a boolean. Let’s add a filter to the observable.
Observable<String> observable = Observable
.just(4, 3, 5)
.map(x -> 2 * x)
.filter(x -> x > 8);
This operator will check if x is greater than 4, and if so allow the piece of data to continue along the stream. Otherwise it fails the check and is removed from the stream (i.e. the observer at the end of the stream never sees it). Subscribing to this observable now would only output 8 since there’s only one piece of data that passes the test. See why this is? 4, 3, and 5 aren’t being checked in this filter, their new map adjusted values of 8, 6, 10 are.
Putting It to Use in Real Life:
Now we know a few ways to manipulate data once we have access to it, so let’s go back to the networking discussion from above. We want to use RxJava or RxSwift to set up the observable pattern and take action after we get response back from a slow network. Let’s say we’re building an app that gets a user’s total count of friends of Facebook (yes I know Facebook already tells you this, it’s an dummy app). We’ve built a method getNumberOfFriends that takes in a username and then gives back a very simple response of a single number. We then want to take that number and check if it’s above 100. If so we’ll display it on the app’s screen.
Observable<Integer> friendNetworkRequest = getNumberOfFriends(“user1”)
Note that we’re not going into the logic of how the request is set up as that’s not the focus of this blog. But a great way to work with ReactiveX and networking is with the Android library Retroift.
.filter(friendCount -> friendCount > 100)
.subscribe(friendCount -> displayCountToScreen(friendCount))
We’ve set up our observable from the network request and create the appropriate operator and observer in the stream. Then after the network request finishes the data will go through this stream and invoke the function displayCountToScreen (which updates the UI).
If you set this up in your code and run it…it crashes! What on earth…why bother follow this tutorial if it just builds you up for failure? Well, we actually set everything up correctly, but we’re still missing a crucial part of ReactiveX, and that’s Schedulers.
In short, our code is crashing because we’re not allowed to make network requests on the main thread and we can’t update the UI on a background thread. Our stream needs some way to know what it should do on which thread. We’ll go over this final essential piece reactive programming in part 3!