Adding Amplitude Analytics to your React Native App

Eileen
Eileen
Dec 9, 2016 · 3 min read
Amplitude Analytics

Per Facebook’s docs, "sometimes an app needs access to a platform API that React Native doesn't have a corresponding module for yet." Unfortunately, React Native doesn’t have a corresponding module for Amplitude. With that being said, this tutorial serves to help you integrate Amplitude easily into your app. :)


First things first…

  1. Register for an account on the Amplitude website
  2. Retrieve your API Key

iOS

With iOS, we’ll be building via Xcode and bridging JavaScript with native code (obj-C).

In AppDelegate.m, we’ll be initializing our API key so we don’t have to initialize it in every file that uses Amplitude:

#import "Amplitude.h"... didFinishLaunchingWithOptions: (NSDictionary *) launchOptions {// your code here  [[Amplitude instance] initializeApiKey:@"insertyourapikeyhere"];// your code here}

You will be adding two files to YourApp/ios (RNAmplitude.h and RNAmplitude.m):

RNAmplitude.h

#import "RCTBridgeModule.h"@interface RNAmplitude : NSObject <RCTBridgeModule>
@end

RNAmplitude.m

#import "RNAmplitude.h"
#import "Amplitude.h"
#import "AMPIdentify.h"
@implementation RNAmplitudeRCT_EXPORT_MODULE();- (dispatch_queue_t)methodQueue {
return dispatch_get_main_queue();
}
RCT_EXPORT_METHOD(initializeApiKey:(NSString *)apiKey) {
[[Amplitude instance] initializeApiKey:apiKey];
}
RCT_EXPORT_METHOD(setUserId:(NSString *)userId) {
[[Amplitude instance] setUserId:userId];
}
RCT_EXPORT_METHOD(setUserProperties:(NSDictionary *)properties) {
[[Amplitude instance] setUserProperties:properties];
}
RCT_EXPORT_METHOD(logEvent:(NSString *)eventwithProperties:(NSDictionary *)properties) {
[[Amplitude instance] logEvent:eventwithEventProperties:properties];
}
@end

And that’s it! You’ve just added Amplitude to your iOS app.


Android

With Android, we’ll be adding code to existing Java files in the project.

In build.gradle, add this line of code:

compile 'com.amplitude:android-sdk:2.13.0'

In your manifest file, add:

<uses-permission android:name="android.permission.INTERNET" />

You will need to add 2 files, RNAmplitude.java and RNAmplitudePackage.java, to your project:

RNAmplitude.java

package com.YourApp;import com.amplitude.api.Amplitude;
import android.app.Activity;
import android.app.Application;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.ReadableType;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Map;import java.io.*;public class RNAmplitude extends ReactContextBaseJavaModule {
private Activity mActivity = null;
private Application mApplication = null;
public RNAmplitude(ReactApplicationContext reactContext,
Application mApplication) {
super(reactContext);
this.mActivity = getCurrentActivity();
this.mApplication = mApplication;
}
@Override
public String getName() {
return "RNAmplitude";
}

@ReactMethod
public void initialize(String apiKey) {
Amplitude.getInstance().initialize(getCurrentActivity(),
apiKey).enableForegroundTracking(this.mApplication);
}
@ReactMethod
public void logEvent(String identifier, ReadableMap properties) {
try {
JSONObject jProperties =
convertReadableToJsonObject(properties);
Amplitude.getInstance().logEvent(identifier, jProperties);
} catch (JSONException e) {
return;
}
}
@ReactMethod
public void setUserId(String id) {
Amplitude.getInstance().setUserId(id);
}
public static JSONObject convertReadableToJsonObject(ReadableMap
map) throws JSONException {
JSONObject jsonObj = new JSONObject();
ReadableMapKeySetIterator it = map.keySetIterator();
while (it.hasNextKey()) {
String key = it.nextKey();
ReadableType type = map.getType(key);
switch (type) {
case Map:
jsonObj.put(key,
convertReadableToJsonObject(map.getMap(key)));
break;
case String:
jsonObj.put(key, map.getString(key));
break;
case Number:
jsonObj.put(key, map.getString(key));
break;
case Boolean:
jsonObj.put(key, map.getString(key));
break;
case Null:
jsonObj.put(key, map.getString(key));
break;
}
}
return jsonObj;
}
}

RNAmplitudePackage.java

package com.YourApp;import android.app.Activity;
import android.app.Application;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.*;
import java.io.*;
public class RNAmplitudePackage implements ReactPackage { private Application mApplication = null; public RNAmplitudePackage(Application application) {
mApplication = application;
}

@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new RNAmplitude(reactContext, this.mApplication));
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules()
{
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext)
{
return Collections.emptyList();
}
}

In MainApplication.java:

@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RNAmplitudePackage(this),
)
};

Since I initialized my api key in native code in my iOS counterpart, I’m going to be consistent and initialize my key in native code for Android. Thus, in MainActivity.java, I’m going to add the following line to onCreate():

Amplitude.getInstance().initialize(this, "insertyourapikeyhere");

Finally, the moment we’ve all been waiting for…

In your .js/.jsx files:

import { NativeModules } from 'react-native';
import { RNAmplitude as Amplitude } from 'NativeModules';
// To use Amplitude:
// Amplitude.logEvent("Some event", { "JSON Key" : "JSON Value" });

Noteworthy: you want to keep the package name and associated methods the same across obj-C and java code. That way, when you import the module into your JavaScript files, you won’t need any code or logic to differentiate between Android and iOS!

Have fun, and code on.

Feel free to ping me at ejzhong@berkeley.edu if you need help getting Amplitude amp’d up #NoPunIntended

Eileen

Written by

Eileen

Building stuff and trying to write about it

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade