RxFirestoreKT is a great library for combining RxJava (Kotlin) and Firestore together, but what I noticed about Firestore is that it already contains many of RxJava’s advantages as it is. Firestore works asynchronously, it can map documents to POJO objects, and can keep a continuous stream of data flowing, handles errors well, and it does all this in some pretty concise code. Just what does Firestore not already have from RxJava’s features?
The thing is, these features are just what people see on the surface of RxJava. While the deeper your understanding of RxJava is, the more you’ll actually squeeze out of the RxJava arsenal going into more advanced features when you head into scheduling, computational programming and such. I myself haven’t achieved that level of understanding yet, so let me tell you how I was able to squeeze the juice out of RxJava.
1. Multithreading
val getUsersObserver = listService.getUsers() .observeOn(Schedulers.io()) .subscribe()
Vanilla Firestore handles its asynchronous operations on the background thread and sends you a callback on the main thread. By adding RxFirestore, you can choose to handle the callback on the main thread if you need access to UI elements, or keep it running on the background thread (Schedulers.io). There are even more threads you can play around like Schedulers.computation, but we’d be here for ages if I talked about each one of them.
On top of this, you can re-use the same function that emits an observable, so that you can observe it in different threads. Genius!
2. MVVM Error Handling
val getUsersObserver = listService.getUsers() .observeOn(Schedulers.io()) .doOnError { handleError(it) } .subscribe()
For those who don’t know what an MVVM architecture is, it’s a way of separating your classes so heavy lifting is done on background threads so that the main thread only needs to handle UI-related functions.
With vanilla Firestore, if you have your operation function (say, getUsers) in a service that isn’t on the main thread, you’ll have to handle your error there as well. If you want to send it back to the main thread, you’re in for some real dirt in your code.
Thanks to RxFirestore’s use of Observers, you can keep your database retrievals and your error dialogues on the threads they need to be and keep your code clean. This also helps you identify which thread any possible errors may be occurring in.
3. Chaining Operations with the godly FlatMap
val getListsObserver = listService.getUsers() .observeOn(Schedulers.io()) .doOnError { handleError(it) } .flatMap { users -> listService.getLists(users) } .subscribe { lists -> showLists(lists) }
You know that one moment when you need some input to get some lists from the database BUT WAIT you need your users’ ids and whatnot! This data is also kept in the database so you need to async task your getLists inside your getUsers then boom, you’re in brackets hell.
Well, only vanilla gets you in that hell. When you spice things up with RxFirestore, you get the godly FlatMap function which lets you chain operations like these together while keeping your code squeaky clean, all the while RxJava keeps things optimised for the best performance. The best part? No brackets hell!
Conclusion
While these are only scratching the surface of how much RxJava can add on top of Firestore, there are things I haven’t yet covered like Backpressure, Stream Termination handling, Observable sharing and all that good stuff.
You know what? RxJava is a deeeeep topic. How about we go even deeper into it to kick off 2019? Happy New Year to ya’ll!