Add Crashlytics to your React Native iOS app

One of the coolest things about React Native is that if you use it together with tools like CodePush or AppHub you can update your app instantly and avoid those painful 1-week app store submissions. That also means that you can push a hotfix on the fly, but the question is: how do you know if your app is crashing? (Unfortunately not all projects have a QA team)

That said, if you have never heard about Crashlytics before, you’re probably doing it wrong…

If you already have the Crashlytics SDK installed, you can skip to Part 2

Crashlytics is now part of Fabric, an SDK offered by Twitter with a set of tools that will help you a lot while developing mobile apps.

Adding it to your mobile app is a no-brainer, and trust me, that’s not a personal preference since it has been named the #1 most implemented performance SDK.

There are 2 important features that you should use from it:

  • Crash Reporting —It will record every single crash and its stack trace. This is way better than the iTunes Connect crash reports, which only include the info of users that opted in to share information with developers while setting up a new iPhone. It’s also not updated in real-time (you can read more about this here).
  • Crash Logs — (A.K.A. CLS_LOG) If you’re familiar with Objective-C, you have probably been using “NSLog” while you’re developing your app. You should use CLS_LOG instead. There’s no difference at all when you’re debugging (whatever you’re logging will still show up in the console) but the cool part is that when a user crashes your app, all the information will be sent to Crashlytics’s servers the next time the user launches your app, including all the content that you’ve logged through CLS_LOG. So if you log information for most of the actions/events in your app, you can read the logs later and reproducing the crash should be simple.

If you have a regular native iOS app, the integration is pretty easy: You just add the SDK to your project, initialize the SDK in your AppDelegate.m file, and that’s it. However, this won’t be enough if you’re using React Native because most of the crashes will happen in Javascript and this is the only thing that you will see:

That doesn’t look very useful, huh?

Now that you’re more familiar with Crashlytics and its features we will show you how to add it to your React Native iOS app from scratch and you will be able to see everything you log using console.log (JS) in your Crashlytics logs!

PART 1 — Add the Crashlytics SDK to your XCode Project

You have two options here:

a) Download the SDK from the Fabric website.

b) Install the SDK from CocoaPods.

NOTE: We’re not going to cover b) because most of RN apps don’t need to use CocoaPods and there’s already a good tutorial on the Fabric website.

1 — The first step is to create and confirm your Fabric account.
2 — Then follow the steps and click on xcode or just click this link to download the Fabric Plugin. 
3 — Unzip and run the Fabric app, login with your credentials and select your XCode Project from the list.

4 — Finally, click install Crashlytics and follow the steps:

  • Add a Run script Build Phase, paste the command and then launch the app.
  • Once it builds, drag and drop the icon from the Fabric App into the Project Navigator).

5 — The SDK is already set up. Now it’s time to initialize Crashlytics in your app code.

Add the following lines at the top of your AppDelegate.m:

#import <Fabric/Fabric.h>
#import <Crashlytics/Crashlytics.h>

And finally add this line at the top of application didFinishLaunchingWithOptions, just like this:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Fabric with:@[[Crashlytics class]]];

Then build and run again, and Fabric.app will verify your installation. And that’s it! You already have Crashlytics working with your iOS app.
With this setup, you will know when your app crashes, but if you’re using React Native, you still won’t know where it crashed.

PART 2 — Override RN logging function and add CLS_LOG

As I’ve explained before, since we’re using React Native, there’s one more step left, to override the custom logging function , so we can send the logs from JS to Crashlytics.

In order to do that you need to add three snippets of code to your AppDelegate.m file:

  1. Add the RCTLog header file:
#import <Fabric/Fabric.h>
#import <Crashlytics/Crashlytics.h>
//Add the following lines
#import <asl.h>
#import "RCTLog.h"

This file manages all the Logging related functions of React Native and asl.h is going to be used in the logging function.

2. Change the log threshold level and override logging function 
(inside application didFinishLaunchingWithOptions:)

[Fabric with:@[[Crashlytics class]]];
//Add the following lines
RCTSetLogThreshold(RCTLogLevelInfo);
RCTSetLogFunction(CrashlyticsReactLogFunction);

Here we are doing two things:

First, lowering the threshold log level, because when your app is running in release mode, React Native will only log at the highest level which is RCTLogLevelError. But we want to log everything we put on console.log and that runs at RCTLogLevelInfo.

The second line is telling React Native that we want to use our own logging function instead of the default, which we will add in the next step.

3. Add our own logging function before the @end of the AppDelegate.m file:

.

This is almost an exact copy of the default logging function of React Native, except for lines 13–15.

As you can see, we’ve wrapped the different ways of logging depending on the mode that the application is running. While the app is on debug mode, it will just print it on the Xcode console, but if we’re in release mode (like regular users of your app) it’ll log using CLS_LOG.

Something that really worked for us while implementing this on the delivery.com iOS App was adding a log in every single component view (for example, in componentDidMount ) and user action like this:

//Example component that wraps an View of the app
class myView extends Component{  
   render(){
<View>
<TouchableHighlight onPress={()=>this._handleLogout} >
<Text>Log out</Text>
</TouchableHighlight>
</View>
}
   componentDidMount(){
console.log(‘myView loaded’);
}

handleLogout(){
console.log('User logged out');
}
}

If somehow this user session crashes, we will look at the logs and we’ll see that the user loaded this view, and then tapped on “logout.” That’s very useful information and hopefully you can reproduce that crash, find the problem and push a hotfix in a few minutes.

To find that in the logs you need to go to your Crashlytics dashboard and click on the issue RCTAssert.m (that’s where RN app crashes when there’s a fatal error on the JS side):

then you need to click on more details and finally click on the switch placed to the right and set it to logs. Then you’ll finally see something like this:

This is way better!

Here you can find the JS stack trace and also all the information that we logged from using console.log

Note: If you’re minifying your JS bundle, you’ll need to generate the source map at the same directory in order to access the JS stack trace. You can do that by adding the option “ — sourcemap-output filename.map” to the react-native bundle command.

Things that we’re still trying to figure out…

  • Prevent crashes from grouping together.

Since all the RN crashes happen on the same line in the native code (RCTAssert.m line 133), Crashlytics therefore groups them together, which is annoying to navigate when you’re looking for different crashes under the same issue.

(If you have any idea about how to deal with this, please leave a comment!)


From here you should be all set. If you run into any problems along the way, feel free to reach out to us on Twitter at Jesse Sessler and Bruno Barbieri and on Discord at jemise111 or brunobar79.