React Native Installation

📘

While in open beta, Heap for React Native may undergo significant changes as we develop new capabilities.

Overview

Use this guide for complete steps to install Heap on React Native apps. For web installation instructions, see our web installation guide.

Please review the following list of requirements and known limitations to ensure your project is set up for this installation process.

📘

For notes on the latest SDK versions, see our React Native changelog.

Requirements and Known Limitations

iOS

  • iOS apps will capture both React Native autocaptured events, as well as events captured by the Heap iOS integration
  • The Event Visualizer is not yet available for React Native iOS applications

Android

👍

If you’re running React Native SDK version 0.9.0 and above, you can define React Native events via the Android Event Visualizer.

  • Android apps do not currently receive native events, such as Fragment Transitions

Expo Framework

Our support for Expo depends on which workflow you use:

  • If you are using the bare workflow, and you are able to complete each step of these installation instructions, it should be supported by Heap – this can vary based on your project file setup
  • If you are using the managed workflow, in which case there are no iOS or Android folders in your project, or if the app is not “ejected”, it is not supported by Heap

Installation

To install Heap for React Native:

  1. From the command line, install the NPM module by running npm install --save "@heap/react-native-heap"

  2. For autocapture, add the following plugins to your .babelrc configuration:

{
  "plugins": [
    "add-react-displayname",
    "./node_modules/@heap/react-native-heap/instrumentor/src/index.js"
  ]
}
  1. For additional information on how to capture component props, see the section below on Prop Capture

  2. To set up autocapture for Screenviews for React Navigation, wrap the AppContainer (the result of a call to React Navigation’s createAppContainer() method) with Heap.withReactNavigationAutotrack()

import Heap from '@heap/react-native-heap';

let AppNavigator = createStackNavigator(
  {
    LoginView: { screen: LoginView },
    MainMenu: { screen: MainMenu },
    SearchView: { screen: SearchView },
    ProductView: { screen: ProductView }
  },
  {
    initialRouteName: 'LoginView'
  }
)
let App = Heap.withReactNavigationAutotrack(createAppContainer(AppNavigator));

iOS

When installing React Native on iOS, we recommend using heapignore to ignore native iOS events that may include sensitive user data, or that are not relevant to your business needs (for example, things like RCTTouchHandler.) You can do this by adding the following line to the didFinishLaunchingWithOptions function in your AppDelegate.m file:

[rootView setValue:@true forKey:@"heapIgnore"];

Note that this will suppress all native iOS events within the root view of the react native app, and not just remove specific elements.

React Native >=0.60 (With Autolinking)

Run the following:

pod repo update
pod install

React Native <0.60 (Without Autolinking)

iOS Using CocoaPods

  1. If you don’t have a Podfile in the ios folder of your React Native project, follow this guide to create one
  2. Add the following to your Podfile:
    pod "react-native-heap", path: "../node_modules/@heap/react-native-heap"
  3. Then run:
    pod repo update
    pod install

iOS Without CocoaPods

All terminal commands assume you are in the top-level directory of your React Native project.

  1. Locate the Xcode project for RNHeap: open node_modules/@heap/react-native-heap/ios. You should see a Finder window containing the RNHeap.xcodeproj file.
  2. Open the Xcode project for your native app, in the ios directory of your project.
  3. Drag the RNHeap.xcodeproj file from the Finder window into the “Libraries” group of your Xcode project.
  4. Select your project name in the Xcode project navigator. Select the “Build Phases” tab for your app’s target.
  5. Expand the build phase called “Link Binary With Libraries”. In the project navigator on the left, expand the RNHeap.xcodeproj entry, and find libRNHeap.a in the “Products” group. The icon for libRNHeap.a should look like a little building with columns.
  6. Drag the libRNHeap.a file into the list of other libraries to be linked.
  7. Expand the build phase called “Copy Bundle Resources”. In the project navigator on the left, under the expanded RNHeap.xcodeproj entry, find HeapSettings.bundle.
  8. Drag the HeapSettings.bundle file into the list of bundle resources to be copied.

When completing the steps above, please note the following;

  • Steps 7-8 are only necessary when using automatic initialization, not when using manual initialization
  • Use of react-native link is not currently supported

Android

React Native >=0.60 (With Autolinking)

With React Native autolinking, Heap should work on Android without any changes needed to the native Android code.

React Native <0.60 (Without Autolinking)

In some cases, simply running react-native link will correctly set up the instrumentor on Android. However, if you encounter any issues (either with the command itself or at compile-time/run-time), you should revert any changes made by react-native link and instead try the following steps.

  1. Add the following to your settings.gradle:
include ':react-native-heap'
 project(':react-native-heap').projectDir =
   new File(rootProject.projectDir, '../node_modules/@heap/react-native-heap/android')
  1. Then add the following to app/build.gradle:implementation project(':react-native-heap')

  2. Import the package and add it to your app’s MainApplication:

@Override
 import com.heapanalytics.reactnative.RNHeapLibraryPackage;
 ...
 protected List<ReactPackage> getPackages() {
   return Arrays.<ReactPackage>asList(
     new MainReactPackage(),
     new RNHeapLibraryPackage()
   );
 }

Configuration

Automatic Initialization

📘

As of version 0.4.0 of this library, we recommend auto-initialization over manually setting your Heap app ID within app code. The manual approach is still supported, however: see the Manual Initialization section below.

The library needs to be initialized so events are sent to the right app ID. Place a heap.config.json file at the root of your application’s repository with this structure, substituting your own app IDs.

{
  "default": {
    "heapAutoInit": true
  },
  "dev": {
       "heapAutoInit": true,
       "heapAppId": "12"
   },
   "prod": {
       "heapAppId": "11",
       "heapAutoInit": true
   }
}
  • Note that different app IDs can be set for development and production. Heap can also be enabled/disabled on a per-environment basis. Values in default are used if a key is missing in either dev or prod.
  • The library distinguishes between dev and prod builds using the DEV variable.

If you are seeing runtime warnings at startup mentioning Heap: Could not find BuildConfig, add the following line to the resources section of res/values/strings.xml:

<string name="com.heapanalytics.android.buildConfigPkgName">com.your_package_name</string>

and replace com.your_package_name with the package name from the manifest tag in AndroidManifest.xml.

Manual Initialization

If you’d like finer-grained control over when the Heap library initializes, call Heap.setAppId with an app ID. (Most users won’t need to do this.)

import Heap from '@heap/react-native-heap';
Heap.setAppId('my-app-id');

In addition, find the MainActivity.java file, which is React Native’s Android-specific entry point. This file is typically present in a subfolder of android/app/src/main/java/. Add the following imports:

import com.heapanalytics.reactnative.RNHeap;
import android.os.Bundle;

Next, add an onCreate method containing the following. If an onCreate method already exists, simply add the RNHeap.init line to it.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    RNHeap.init(getApplicationContext(), "<app-id>");
}

Ignoring Sensitive Data and PII

On React Native, autocaptured data will include interactions with certain types of components, as well as capturing target text from components if there is any to capture. Additionally, commonly-used props for popular UI libraries, like React Native Elements or NativeBase, may also be captured as a part of the component hierarchy event property. The result is that some care is needed when dealing with components that contain sensitive information or other PII.

The Heap for React Native SDK (version 0.4.0 and greater) offers several methods for ensuring that PII is not sent from a React Native app to Heap, using React components that may be used within JSX code or via higher-order components.

Target Text Autocapture toggle

Admins can control the capture of sensitive information that lives on the target text of elements by turning on the Target Text Autocapture toggle on the Account > Manage > Privacy & Security page.

Please note that disabling target text entirely might make defining events more difficult. Also, if you have this setting enabled while using the APIs listed below, then the most privacy-conscious setting will win.

HeapIgnore and HeapIgnoreTargetText Functional Components

The Heap for React Native SDK provides two convenient functional components for restricting the data that is autocaptured by Heap.

  1. The HeapIgnore component is the most restrictive component, and can be used to stop Heap from capturing any events below the ignored component in the hierarchy.

For example, consider a component that has an extremely sensitive piece of information in some button text:

import React, { Component } from 'react';

class MySensitiveScreen extends Component {
  render() {
    const safeProp = this.props.profile.randomUuid;
    const superSensitiveProp = this.props.profile.creditCardNum;
    const { styles } = this.props.styles;

    return (
      <View style={styles.container}>
        <TouchableHighlight onPress={this._onPressButton} underlayColor="white">
          <View style={styles.button}>
            <Text style={styles.buttonText}>{superSensitiveProp}</Text>
          </View>
        </TouchableHighlight>
        <TouchableHighlight onPress={this._onPressButton} underlayColor="white">
          <View style={styles.button}>
            <Text style={styles.buttonText}>{safeProp}</Text>
          </View>
        </TouchableHighlight>
      </View>
    );
  }
}    

export default MySensitiveScreen;

As written, Heap would capture any interactions with both of the buttons and set Target Text event properties with the content of the Text components. We can re-write this component to use HeapIgnore:

import React, { Component } from 'react';
import { HeapIgnore } from '@heap/react-native-heap';

class MySensitiveScreen extends Component {
  render() {
    const safeProp = this.props.profile.randomUuid;
    const superSensitiveProp = this.props.profile.creditCardNum;
    const { styles } = this.props.styles;

    return (
      <View style={styles.container}>
        <HeapIgnore>
          <TouchableHighlight onPress={this._onPressButton} underlayColor="white">
            <View style={styles.button}>
              <Text style={styles.buttonText}>{superSensitiveProp}</Text>
            </View>
          </TouchableHighlight>
        </HeapIgnore>
        <TouchableHighlight onPress={this._onPressButton} underlayColor="white">
          <View style={styles.button}>
            <Text style={styles.buttonText}>{safeProp}</Text>
          </View>
        </TouchableHighlight>
      </View>
    );
  }
}    

export default MySensitiveScreen;

By wrapping the TouchableHighlight that contains the button with sensitive text in a HeapIgnore component, you can prevent Heap from capturing any interactions from the button. Interactions with the other button, which has safe target text, will still be sent to Heap. The HeapIgnore component can be used to ignore events very broadly, as well as very specifically. For example, wrapping the entire App component in HeapIgnore would ignore all autocaptured events from the entire application. For more specific use cases, the HeapIgnore component can be used to ignore events from a specific button.

  1. Sometimes, however, it is important to be able to capture the fact that a user interacted with a button or other component, and you just want to ensure that the target text itself is not captured.

In these cases, the HeapIgnoreTargetText component is extremely useful:

import React, { Component } from 'react';
import { HeapIgnoreTargetText } from '@heap/react-native-heap';

class MySensitiveScreen extends Component {
  render() {
    const safeProp = this.props.profile.randomUuid;
    const superSensitiveProp = this.props.profile.creditCardNum;
    const { styles } = this.props.styles;

    return (
      <View style={styles.container}>
        <HeapIgnoreTargetText>
          <TouchableHighlight onPress={this._onPressButton} underlayColor="white">
            <View style={styles.button}>
              <Text style={styles.buttonText}>{superSensitiveProp}</Text>
            </View>
          </TouchableHighlight>
        </HeapIgnoreTargetText>
        <TouchableHighlight onPress={this._onPressButton} underlayColor="white">
          <View style={styles.button}>
            <Text style={styles.buttonText}>{safeProp}</Text>
          </View>
        </TouchableHighlight>
      </View>
    );
  }
}    

export default MySensitiveScreen;

In this version of our example component, Heap will capture all of the touchableHandlePress events that occur for both buttons. For the button with the safeProp text, events will contain a Target Text event property with the value of safeProp. However, for the button with the sensitive text, the Target Text event property will be suppressed, and no PII or sensitive information will be captured.

NOTE: HeapIgnoreTargetText still sends through component properties that have been configured for capture. For example, <Button title={sensitiveProp}/> would still capture the title property as a part of the autocaptured event.

Suppressing Target Text is an extremely useful way to avoid sending PII, but can result in the elimination of important metadata for defining virtual events. In cases where some additional metadata is required for specifically defining an event, it is recommended to add a testID property, which will be captured automatically by Heap. These testIDs are typically used for automated testing using tools like Detox or Appium.

HeapIgnore Higher-Order Component

In some cases, it is easier to use a Higher-Order Component (HOC) for applying ignore functionality. For example, if you wanted to ignore all interactions for all instances of a component, but always contain sensitive data, HOCs can be very useful. As an example, we might have a button that frequently contains sensitive information as its text. We can ensure that those interactions will not be captured by exporting an ignored button using the HOC.

import React, { Component } from 'react';
import Heap from '@heap/react-native-heap';

class SensitiveButton extends Component {
  render() {
    const safeProp = this.props.profile.randomUuid;
    const superSensitiveProp = this.props.profile.creditCardNum;
    const { styles } = this.props.styles;

    return (
      <TouchableHighlight onPress={this._onPressButton} underlayColor="white">
        <View style={styles.button}>
          <Text style={styles.buttonText}>{superSensitiveProp}</Text>
        </View>
      </TouchableHighlight>
    );
  }
}

const IgnoredSensitiveButton = Heap.withHeapIgnore(SensitiveButton);

export default IgnoredSensitiveButton;

Then we can use this in other locations within the app. Using our previous example:

import React, { Component } from 'react';
import { IgnoredSensitiveButton } from './components/SensitiveButton'
import { Heap } from '@heap/react-native-heap';

class MySensitiveScreen extends Component {
  render() {
    const { styles } = this.props.styles;

    return (
      <View style={styles.container}>
        <IgnoredSensitiveButton />
        <IgnoredSensitiveButton />
      </View>
    );
  }
}

export default MySensitiveScreen;

In this case, the button with sensitive data can be re-used throughout the app, but Heap will ignore all interactions with those buttons, due to the use of the HeapIgnore HOC.

For more information on Heap's data privacy options, see How do I use Heap to comply with data privacy legislation?

Troubleshooting

The react-native-heap module cannot be built as a dynamically linked framework. If your app uses use_frameworks! in your Podfile, you will need to add an additional pre-install step to the Podfile:

pre_install do |installer|
    pod = installer.pod_targets.find { |p| p.name == 'react-native-heap'}
    def pod.static_framework?
        true
    end
end

Manual Tracking Usage Examples

The following code snippet is an example of how to set up manual tracking:

// Import Heap.
import Heap from '@heap/react-native-heap';

// Identify your user.
Heap.identify('123456');
Heap.addUserProperties({ name: 'John', age: 54 });

// Add event properties (these persist across sessions).
Heap.addEventProperties({ isLoggedIn: true });

// You can remove a specific property or clear everything.
Heap.removeEventProperty('isLoggedIn');
Heap.clearEventProperties();

// To track an event, use:
Heap.track('signed-up', { isPaid: true, amount: 20 });

Autocaptured Events

Native Events (iOS Only)

In addition to React Native-specific events, the React Native Heap integration autocaptures events from the native iOS library. Native Android events, such as fragment transitions, are not currently captured.

Screenviews

Heap captures screenviews through integrations with popular navigation frameworks. Currently, only React Navigation 3.0+ is supported. Screenviews are not captured by default, and autocapture can be activated by wrapping the React Navigation AppContainer with Heap.withReactNavigationAutotrack(). For example:

import Heap from '@heap/react-native-heap';

let AppNavigator = createStackNavigator(
  {
    LoginView: { screen: LoginView },
    MainMenu: { screen: MainMenu },
    SearchView: { screen: SearchView },
    ProductView: { screen: ProductView }
  },
  {
    initialRouteName: 'LoginView'
  }
)
let App = Heap.withReactNavigationAutotrack(createAppContainer(AppNavigator));

React Native-Specific Captured Properties:

  • Type: For React Navigation, the event type will be reactNavigationScreenview.
  • path: The path property will contain the the name of the view as defined with React Navigation. Example: LoginView.
  • type: The type of screenview – for standard navigation operations, this will be Navigation/NAVIGATE. This property will not be defined on events if the screen was viewed as the initial route.

Touchables

Heap will capture touches on any component that is backed by any of the Touchable components, like TouchableHighlight, TouchableOpacity, etc. This will effectively capture touch interactions with a wide variety of components, but will not distinguish between gestures on those components. Heap captures the component hierarchy as a part of this event, which should enable basic specification of events.

React Native-Specific Captured Properties:

  • Type: For Touchables, the event type will be TouchableHandlePress.
  • touchableHierarchy: This contains the component hierarchy of associated with the event. Example:
AppContainer;|App;|Provider;|Connect(RouterComponent);|RouterComponent;|Router;|App;|NavigationContainer;|KeyboardAwareNavigator;|Navigator;|StackView;|Transitioner;|withOrientation;|StackViewLayout;|ScreenContainer;|Container;|Card;|Screen;|AnimatedComponent;|SceneView;|NavigationContainer;|Navigator;|_default;|NavigationContainer;|KeyboardAwareNavigator;|Navigator;|StackView;|Transitioner;|withOrientation;|StackViewLayout;|ScreenContainer;|Container;|Card;|Screen;|AnimatedComponent;|SceneView;|NavigationContainer;|KeyboardAwareNavigator;|Navigator;|StackView;|Transitioner;|withOrientation;|StackViewLayout;|ScreenContainer;|Container;|Card;|Screen;|AnimatedComponent;|SceneView;|Wrapped;|Connect(ProductScreen);|ProductScreen;|KeyboardView;|KeyboardAvoidingView;|ScrollView;|Button;[title=Save];|TouchableOpacity;|
  • touchState: Touch state of the component at the time of the event. This is a string representation of an internal state enumeration. Example: RESPONDER_ACTIVE_PRESS_IN
  • targetText: If there is a textual element to the component being touched, Heap will capture that associated text. Heap attempts to infer the target text, but may not always be able to extract it automatically. Currently, the text must be a descendent of the Touchable component.

Component Props

Heap attempts to autocapture commonly used props for standard components, as well as popular component libraries and UI kits. For example, most React Native apps will have a component called Button, which often has a prop called title. When a Touchable event is triggered from a Button, the title prop will be automatically included in the event by augmenting the touchbleHierarchy event property.

For example, a Button component labeled with the text “Save” would result in a touchableHierarchy event prop like this (note Button;[title=Save];):

AppContainer;|App;|Provider;|Connect(RouterComponent);|RouterComponent;|Router;|App;|NavigationContainer;|KeyboardAwareNavigator;|Navigator;|StackView;|Transitioner;|withOrientation;|StackViewLayout;|ScreenContainer;|Container;|Card;|Screen;|AnimatedComponent;|SceneView;|NavigationContainer;|Navigator;|_default;|NavigationContainer;|KeyboardAwareNavigator;|Navigator;|StackView;|Transitioner;|withOrientation;|StackViewLayout;|ScreenContainer;|Container;|Card;|Screen;|AnimatedComponent;|SceneView;|NavigationContainer;|KeyboardAwareNavigator;|Navigator;|StackView;|Transitioner;|withOrientation;|StackViewLayout;|ScreenContainer;|Container;|Card;|Screen;|AnimatedComponent;|SceneView;|Wrapped;|Connect(ProductScreen);|ProductScreen;|KeyboardView;|KeyboardAvoidingView;|ScrollView;|Button;[title=Save];|TouchableOpacity;|

If an event does not have sufficient specificity between autocaptured props and component hierarchies, it may be necessary to configure the prop capture further in the app code. For more details, see the Configuring Prop Capture section just below this one.

Configuring Prop Capture

Heap comes configured with an initial set of prop-capture configurations for commonly-used components, and will eventually support common props for popular UI kits like NativeBase and React Native Elements. If capture of a non-default property is required, this can be achieved by adding a small piece of configuration code to the app. Prop capture configurations can be assigned to both stateless functional and stateful components.

In order to achieve this, Heap will check for a specially-named heapOptions property that contains a set of properties to include or exclude. The structure of the property is as follows:

heapOptions = {
  eventProps: {
    include: [ '<inc_prop_1>', '<inc_prop_2>', ..., '<inc_prop_n>' ],
    exclude: [ '<exc_prop_1>', '<exc_prop_2>', ..., '<exc_prop_n>' ],
  }
}

Properties in the include list will be included in the event. Properties in the exclude list will be explicitly excluded if they would have been otherwise included by either the include list or a built-in prop configuration.

Stateless, Functional Component Example:

import React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';

const ProductItem = ({name, price, onPress}) => {
  return (
      <TouchableOpacity onPress={onPress}>
        <View>
          <Text>{name}</Text>
          <Text>{price}</Text>
        </View>
      </TouchableOpacity>
  );
}

ProductItem.heapOptions = {
  eventProps : { include: [ 'name', 'price' ] }
};

export { ProductItem };

Stateful Component Example

import React, { Component } from 'react';
import { Text, TouchableOpacity, View } from 'react-native';

export default class ProductItem extends Component {
  heapOptions = {
    eventProps : { include: [ 'name', 'price' ] }
  };    

  render() {
    return (
      <TouchableOpacity onPress={onPress}>
        <View>
          <Text>{name}</Text>
          <Text>{price}</Text>
        </View>
      </TouchableOpacity>
    );
  }
}

Use of a custom prop configuration will result in additional props being captured within the touchableHierarchy event property (note ProductItem;[name=Foo];[price=$1.00]):

AppContainer;|App;|Provider;|Connect(RouterComponent);|RouterComponent;|Router;|App;|NavigationContainer;|KeyboardAwareNavigator;|Navigator;|StackView;|Transitioner;|withOrientation;|StackViewLayout;|ScreenContainer;|Container;|Card;|Screen;|AnimatedComponent;|SceneView;|NavigationContainer;|Navigator;|_default;|NavigationContainer;|KeyboardAwareNavigator;|Navigator;|StackView;|Transitioner;|withOrientation;|StackViewLayout;|ScreenContainer;|Container;|Card;|Screen;|AnimatedComponent;|SceneView;|NavigationContainer;|KeyboardAwareNavigator;|Navigator;|StackView;|Transitioner;|withOrientation;|StackViewLayout;|ScreenContainer;|Container;|Card;|Screen;|AnimatedComponent;|SceneView;|Wrapped;|Connect(ProductsScreen);|ProductsScreen;|FlatList;|VirtualizedList;|ScrollView;|CellRenderer;|ProductItem;[name=Foo];[price=$1.00];|TouchableOpacity;|

Capturing Properties from Nested Objects and Arrays

Prop capture configs also support capture of props from within more complex objects. For example, if the ProductItem component utilized an item prop instead of name and price, we could capture the props like this:

import React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';

const ProductItem = ({item, onPress}) => {
  return (
      <TouchableOpacity onPress={onPress}>
        <View>
          <Text>{item.name}</Text>
          <Text>{item.price}</Text>
        </View>
      </TouchableOpacity>
  );
}

ProductItem.heapOptions = {
  eventProps : { include: [ 'item.name', 'item.price' ] }
};

export { ProductItem };

Best Practices for Event Definition

For information on capturing and managing definitions in React Native, see the following:

Changelog

For notable updates to the Heap React library, see the React Changelog.

Updated 19 days ago

React Native Installation


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.