RxSwift Hooks — a look into Reactive global error handling
Error handling is awesome. Error handling is necessary. Error handling can become painful, can make you cry and can cause a lot of headaches. But you can overcome it all. Hooks in RxSwift is a great tool to leave all this pain behind. It’s powerful and wholesome. Also, underused.
But we’re here to change that!
Individual error handling is pretty straightforward in RxSwift as it provides a variety of options to choose from. Most of you are familiar with the following:
loginButton.rx.tap
.flatMap { [unowned self] _ in self.login() }
.subscribe(onError: { [weak self] error in
if error is LoginError {
self?.showCustomAlert()
}
})
.disposed(by: bag)
loginButton.rx.tap
.flatMap { [unowned self] _ in self.login() }
.do(onError: { [weak self] error in
if error is CustomError {
self?.showCustomAlert()
}
})
.subscribe()
.disposed(by: bag)
These two approaches will produce the same result by handling each of their cases individually. But what if there is a way to handle all of these cases in one place? Because always writing out onError seems quite counterproductive, right?
What is “Hooks”?
No, not this one! :)
Hooks offers a default error handling mechanism with a static interface for cases when you don’t provide your own onError handler.
Hooks will apply throughout your whole application where error handling is missing, all errors generated by any stream will pass through here. So you could show an alert, for example.
But let’s pump the brakes here, because there’s a catch in my last proposal. Figuratively and literally. There’s an important distinction between handling errors e.g: subscribe(onError: ), catchError. and handling errors as a side effect e.g: do(onError: ). In case of they’re being handled as a side effect, they’ll make their ways to the Hooks handler, while if you’re managing through catchError or subscribe(onError: ) that wouldn’t be the case.
Another important part here is the recordCardStackOnError. It will make sure you get a detailed call stack, making it much easier to trace and locate the source of errors as well. You should see something like this:
I made a simple app to show you the difference between case by case error handling and general error handling. The sample application has three buttons:
The Login button doesn’t define any local error handling logic, and by not doing so, it’ll be handled by Hooks.
As I’ve mentioned before the middle button has a defined handling method, and it’s not managed as a side effect; therefore, you’re greeted with a custom error alert.
Last but not least, the Do button, which has implemented error handling, BUT only as a side effect, therefore it’ll reach the general Hooks.
Now let’s examine the ViewController!
ViewController.swift
As you can see here, the Subscribe button has its own error handling context, we’re checking if the caught error is a particular type, in our case CustomError, then we‘ll show an alert view.
Now let’s take a look at our Hooks implementation:
AppDelegate.swift
Here, we’re enabling call stack recording and setting up the default error handler. Moreover, we’re checking if the generated error is a specific GeneralError and if that’s the case, we’ll present the user with another alert.
Conclusion
I think that Hooks is a really powerful and hands-on tool. It makes the developer’s life a whole lot easier by streamlining error handling. This is especially important in the case of modularised projects, where things could quickly get out of hand. Furthermore, if you would like to send logs to any other logging service, such as Firebase, thanks to it, there’s no need to import that dependency into every module of your application. Another use case would be to use it for network errors and if there’s a need to handle individual scenarios, you’ll have the local context to do so without losing sight of the bigger picture.
You can check out and play with the example repo here:
RxHooksExample
At Supercharge, we are a next-generation innovation partner working with our clients to create transformative digital solutions. If you liked this article, check out some of Supercharge’s other articles on our blog, and follow us on LinkedIn, and Facebook. If you’re interested in open positions, follow this link.