Production Error Handling in Ionic

How to deal with errors in production and keep your users on side


Nobody likes apps that crash or stop working properly. Handling and recovering from errors is obviously an important task for any developer; we should not assume that everything will run smoothly.

In this post we’re talking about what to do on top of your regular error handling - the last resort.

Ionic and Unhandled Errors

Ionic’s production error handling is, well, lacking. An unhandled exception or promise rejection in your JS code leaves your application in an undefined state. It may still look ok, but your users will experience very strange behaviour and errors that simply don’t make sense.

Implement an ErrorHandler

The best way to implement last-resort error handling in Ionic and Angular is to implement your own ErrorHandler class.

import { ErrorHandler } from '@angular/core';

export class MyErrorHandler implements ErrorHandler {
  constructor() {}

  async handleError(error) {
    console.error(error);
  }
}

Angular will automatically call handleError whenever an exception is thrown.

Hooking up the custom error handler to Angular simply requires providing it at boot time. You probably want to keep Ionic’s default error handler in development, and switch the error handler in for production.

const ERROR_HANDLER = environment.production ? MyErrorHandler : IonicErrorHandler;

@NgModule({
  // the rest of your module bootstrapping
  providers: [
    { provide: ErrorHandler, useClass: ERROR_HANDLER }
  ]
});

What should we do?

The exact requirements will depend on your application, but a good starting point is to pop up an alert to the user, apologising, and asking them to reload the app. We also pop up the Ionic splash screen while the app is reloading.

import { ErrorHandler, Inject } from '@angular/core';
import { AlertController } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';

export class MyErrorHandler implements ErrorHandler {
  constructor(
    @Inject(AlertController) private alerts: AlertController,
    @Inject(SplashScreen) public splashScreen: SplashScreen
  ) {}

  async handleError(error) {
    const alert = this.alerts.create({
      title: 'An Error Has Occurred',
      subTitle: 'Unfortunately, the app needs to be restarted',
      enableBackdropDismiss: false,
      buttons: [
        {
          text: 'Restart',
          handler: () => {
            this.splashScreen.show();
            window.location.reload();
          }
        }
      ]
    });
    alert.present();
  }
}

Reloading the app is drastic, but remember this is a last resort, on top of our regular error handling.

Log to a 3rd party service

Handling the error is only the first step in the process. What we really want is to fix the error. There’s no need to wait for your user to complain - we can log the error to a 3rd party service. You’ll get a much nicer error report, with a stack trace and logs, making finding and fixing the error much easier.

For this example we’re going to log to Rollbar*, but there are lots of other services available. You could even write your own.

Angular Rollbar

For this example we’ll use angular-rollbar to simplify the process. First up we’ll add the module to our project:

@NgModule({
  imports: [
    RollbarModule.forRoot({
      accessToken: 'YOUR ROLLBAR CLIENT TOKEN'
    })
  ],
});

Next up we’ll set up the RollbarService to be injected into our ErrorHandler and and log the error.

import { ErrorHandler, Inject } from '@angular/core';
import { AlertController } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { RollbarService } from 'angular-rollbar';

export class MyErrorHandler implements ErrorHandler {
  constructor(
    @Inject(AlertController) private alerts: AlertController,
    @Inject(SplashScreen) public splashScreen: SplashScreen,
    @Inject(RollbarService) public rollbar: RollbarService
  ) {}

  async handleError(error) {
    this.rollbar.error(error);
    this.presentAlert();
  }
}

Now our error will be logged for future debugging, and we’ve presented an error to the user. They probably still aren’t happy about the error, but at least you’re honest with them.

Conclusion

And that’s it. We can catch unhandled errors in production, log them to a 3rd party service, and notify the user that something’s gone wrong. The app will restart and will never be left in an undefined state.

Again, this is a last resort, and should compliment proper, fine grained error handling.

*I have no vested interest or relationship with Rollbar, but we do use their service from time to time.