Native integration setup

The BugSnag Performance SDK for React Native can either process and deliver spans on its own or it can be “attached” to the native Android and iOS SDKs for enhanced functionality if you are interested in how the native part of your app behaves.

With the native integration configured, you can:

  • Add system metrics captured by the native SDK to JavaScript-based spans;
  • See native app starts and other native-based spans on your dashboard, including adding custom spans in your native code;
  • Get access to native spans from your JavaScript code to produce cross-layer traces and to end native spans from your JavaScript code;
  • See full app start and view load traces that include both native and JavaScript spans and timings.

Installation

With the BugSnag React Native Performance SDK already installed, follow the native integration guides to add performance monitoring to your Android and iOS projects:

SDK Minimum version Docs
bugsnag-android-performance v1.11.0 Integration guide »
bugsnag-cocoa-performance v1.11.1 Integration guide »

Next, install the @bugsnag/plugin-react-native-span-access package:

JavaScript

yarn add @bugsnag/plugin-react-native-span-access
# or
npm install --save @bugsnag/plugin-react-native-span-access

Android

Add the following dependencies to your Module Gradle Settings, usually found at <project_dir>/android/app/build.gradle or build.gradle.kts:

dependencies {
    // ...
    implementation project(':bugsnag_plugin-react-native-span-access')
}
dependencies {
    // ...
    implementation(project(":bugsnag_plugin-react-native-span-access"))
}

iOS

run pod install in your iOS project directory to ensure the plugin is linked correctly:

npx pod-install

Basic configuration

To use the native integration, the BugSnag React Native Performance SDK is “attached” to the native SDK, reading its configuration from the native layer.

In index.js, import and attach the BugSnag performance client as follows, replacing the existing call to start (if present):

import BugsnagPerformance from '@bugsnag/react-native-performance'

BugsnagPerformance.attach()

Configuration options that are common to the React Native and native SDKs should be set in native code and will be picked up by the React Native SDK when you call attach. Any configuration that only applies to @bugsnag/react-native-performance can be passed to the attach method:

BugsnagPerformance.attach({
  codeBundleId: '12345',
})

Use the Configuration options page to see which options are available to the attach method.

Cross-layer span access

To assist in tracking custom spans which you wish to reference on both the native and JavaScript layers of your React Native app, you can use the BugsnagNativeSpansPlugin and BugsnagJavascriptSpansPlugin plugins to allow each SDK to access spans created by the other.

See the named span access guide for detailed setup and usage instructions.

Integrated app starts

For improved cross-layer performance monitoring, you can configure React Native app starts to be children of native view loads using the BugsnagReactNativeAppStartPlugin. This allows developers to see a full breakdown of the time taken to load a React Native view from the native layer through to the JavaScript component being rendered.

For full React Native apps, this means the app start time will be included in the native app start span. For hybrid/brownfield apps, this means the React Native initialization time will be included in each native view load span.

Configuration

Add the BugsnagReactNativeAppStartPlugin to your native Android and iOS setup code:

Android

import com.bugsnag.reactnative.performance.nativespans.BugsnagReactNativeAppStartPlugin;
PerformanceConfiguration config = PerformanceConfiguration.load(this);
config.addPlugin(new BugsnagReactNativeAppStartPlugin());
BugsnagPerformance.start(config);
import com.bugsnag.reactnative.performance.nativespans.BugsnagReactNativeAppStartPlugin
val config = PerformanceConfiguration.load(this);
config.addPlugin(new BugsnagReactNativeAppStartPlugin());
BugsnagPerformance.start(config);

iOS

#import "BugsnagReactNativeAppStartPlugin.h"

BugsnagReactNativePerformanceConfiguration *config =
    [BugsnagReactNativePerformanceConfiguration loadConfig];
[config addPlugin:[[BugsnagReactNativeAppStartPlugin new]]];
[BugsnagReactNativePerformance startWithConfiguration:config];
import BugsnagNativeSpans

let config = BugsnagReactNativePerformanceConfiguration.loadConfig()
config.addPlugin(BugsnagReactNativeAppStartPlugin())
BugsnagReactNativePerformance.start(configuration: config)

By default, the root view in a React Native iOS app is created in a way that is incompatible with the native BugSnag view load instrumentation. To allow this to be instrumented, a custom view controller implementation is required.

This limitation only applies to regular React Native apps where the React Native screen is loaded immediately. Hybrid/brownfield apps that load the React Native view via a custom view controller are not affected.

First, create a custom view controller:

// ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (nonatomic, copy, nullable) UIView *(^viewFactory)();

@end
// ViewController.swift
import UIKit

class ViewController: UIViewController {
    var viewFactory: (() -> UIView)?

    override func loadView() {
        if let viewFactory = viewFactory {
            self.view = viewFactory()
        }
    }
}

If using Objective-C, you will also need to implement the view controller:

// ViewController.m
#import "ViewController.h"

@implementation ViewController

- (void)loadView {
    if (self.viewFactory) {
        self.view = self.viewFactory();
    }
}

@end

Finally, update your AppDelegate to use this custom view controller for the React Native root view:

// AppDelegate.mm
- (UIViewController *)createRootViewController
{
  return [ViewController new]; // Your custom view controller class
}

- (void)setRootView:(UIView *)rootView
    toRootViewController:(UIViewController *)rootViewController
{
    if ([rootViewController isKindOfClass:[ViewController class]]) {
        ((ViewController *)rootViewController).viewFactory = ^UIView *{
            return rootView;
        };
    } else {
        [super setRootView:rootView toRootViewController:rootViewController];
    }
}
// AppDelegate.swift
override func createRootViewController() -> UIViewController {
    return ViewController() // Your custom view controller class
}

override func setRootView(
  _ rootView: UIView,
  toRootViewController rootViewController: UIViewController
) {
    if let viewController = rootViewController as? ViewController {
        viewController.viewFactory = {
            return rootView
        }
    } else {
        super.setRootView(rootView, toRootViewController: rootViewController)
    }
}

Next steps

  • Enable system metrics capturing in your native Android and iOS SDKs to get these metrics for your JavaScript spans.
  • Use the named span access plugins to help you access spans in other layers.