How To Do Analytics in Your React Native App with Firebase

calin_crist

Calin-Cristian Ciubotariu

Posted on January 17, 2020

How To Do Analytics in Your React Native App with Firebase

You're building two mobile apps (iOS and Android) using React Native. The apps get approved in their stores. How do you know if your customers are enjoying your creation and find it useful? You don't unless you find a way to get insights and understand how your apps are used.

Choices

First, one has to decide what library should be used. Fortunately, there are some great ones that make it easy to integrate, like:

In this post, I'll be focusing on Firebase Analytics as it's one of the most popular. Especially because of its easy integration with Google Analytics.

Love it or hate it, Google services are still (arguably) the most popular ones for many valid reasons.

Configure Firebase Account

Before integrating Firebase for React Native, you need a Firebase project in the console (link).

This project will hold information for both iOS and Android apps.

In my case, I named the project "tutorial-demo".

Step 1/3

Step 2/3

Step 3/3

iOS project

After getting through the wizard, select the newly created project.

Now, we need to add different apps for different platforms. For iOS select the "iOS" icon.

Enter the native project's bundle ID (and App Store ID if you have one), give it a nickname and press "Register app".

Register app

Download config file

The Firebase console provides a GoogleService-Info.plist file.

This contains a set of credentials for iOS devices to use when authenticating with your Firebase project.

Download the "GoogleService-Info.plist" presented in the second step and add it to the iOS native project.

Don't forget to select the correct target if you're having multiple targets. Also, don't forget to select "Copy items if needed".

Note: Open "GoogleService-Info.plist" and enable analytics by setting "YES" the key "IS_ANALYTICS_ENABLED".

IS_ANALYTICS_ENABLED

The 3rd step is not relevant for us, as we are covered by the firebase package that will add the pods for us.

The 4th step is something that we can add later. For now, let's finish with Firebase console configurations.

Android project

The android side of things is very similar.

Go to the project homepage and select the "Android" icon.

Step 1

Step 2

Here, we have again a config file - this time called "google-services.json".

Add it to the native project inside the "app" folder from the android project folder.

Install and Configure Firebase Analytics package

For React Native, there is the official Firebase package: https://invertase.io/oss/react-native-firebase/

It contains all the Firebase services and we'll be installing and using the Analytics one.

Install the core and analytics packages:

yarn add @react-native-firebase/app
yarn add @react-native-firebase/analytics
Enter fullscreen mode Exit fullscreen mode

Assuming the React Native version is >= 0.60, the module should be automatically linked to your project.

If not, you need to manually integrate the app module into your project. See the following steps for Android and iOS for more information on manual linking.

Install the pods for the iOS app:

cd ios && pod install && cd ..
Enter fullscreen mode Exit fullscreen mode

iOS: I noticed that after integrating the firebase package I needed to do some extra steps to make it work:

  • clear the "Derived Data"
  • clean the project
  • remove the existing app from the simulator/testing device

Android: In case the build or the gradle syncing is failing - it happened to me in one occasion, this is what I modified.
I think it has to do with auto-linking failing for some reason.

android/build.gradle

 buildscript {
        // ...
     dependencies {
                // ...
         classpath 'com.google.gms:google-services:4.3.2' // <---
     }
 }
Enter fullscreen mode Exit fullscreen mode

android/app/build.gradle

 dependencies {
        // ...
     implementation 'com.google.firebase:firebase-analytics:17.2.0' // <---
        // ...
 }
 // ...

 apply plugin: 'com.android.application'        // <---
 apply plugin: 'com.google.gms.google-services' // <---
Enter fullscreen mode Exit fullscreen mode

Analytics layer

Automatic events

Just by integrating the Analytics package there are some events that are collected automatically like:

first_open, user_engagement, app_clear_data .

More details are provided here: https://support.google.com/firebase/answer/6317485

Custom events

What's cool about this package is that it provides predefined methods for different use cases depending on the nature of your app (e-commerce, games, etc.), but also bare-bones functions to customize your own event loggings.

Long story short, what we can do using react-native-firebase is:

  • Log custom events
  await analytics().logEvent("event_name", {"key_1": "value_1", "key_2": "value_2"});
Enter fullscreen mode Exit fullscreen mode
  • Log the opening of the app
  await firebase.analytics().logAppOpen();
Enter fullscreen mode Exit fullscreen mode
  • Log the sign in/sign up event
  await firebase.analytics().logLogin({
    method: 'facebook',
  });

  await firebase.analytics().logSignUp({
    method: 'facebook',
  });
Enter fullscreen mode Exit fullscreen mode

Behind the scenes, these specific events (logAppOpen, logLogin, logSignUp) are using the logEvent method specifying the key and some properties for you.

  • Set user properties
  await analytics().setUserId("id");
  await analytics().setUserProperty('email', email); // <--- DON'T DO THIS !!!
  await analytics().setUserProperties('account', {
    'subscription': 'premium'
  });
Enter fullscreen mode Exit fullscreen mode

It is highly recommended not to send any fragile and secret data to firebase (emails, passwords, names, etc.) - not even hashed.

  • Tracking screens
  await analytics().setCurrentScreen("screen_name", "screen_name");
Enter fullscreen mode Exit fullscreen mode

And these are just a bunch of them. Here are all the supported methods.

Integrating it in your project and use cases

Now, to demo these events, let's do an old-fashioned class that we will be used to centralize the analytics code. An advantage of this approach would be that we can use multiple analytics solutions/packages by updating just one file. (Of course, it doesn't need to be a class but here we are :) )

    import analytics, { firebase } from '@react-native-firebase/analytics';

    class Analytics {
      static init() {
        if (firebase.app().utils().isRunningInTestLab) {
          analytics().setAnalyticsCollectionEnabled(false);
        } else {
          analytics().setAnalyticsCollectionEnabled(true);
        }
      }

      static onSignIn = async userObject => {
        const { id, email } = userObject;
        await Promise.all([
          analytics().setUserId(id),
          analytics().setUserProperty('email', email), // <--- DON'T DO THIS !!!
          this.logEvent("sign_in")
        ]);
      };

      static onSignUp = async userObject => {
        const { id, email } = userObject;
        await Promise.all([
          analytics().setUserId(id),
          analytics().setUserProperty('email', email),  // <--- DON'T DO THIS !!!
          analytics().setUserProperty('created_at', new Date()),
          this.logEvent("sign_up")
        ]);
      };

      static setCurrentScreen = async screenName => {
        await analytics().setCurrentScreen(screenName, screenName);
      };

      static logEvent = async (eventName, propertyObject = {}) => {
        await analytics().logEvent(eventName, propertyObject);
      }

      static onSignOut = async () => {
        await analytics().resetAnalyticsData();
      };
    }

    export default Analytics;
Enter fullscreen mode Exit fullscreen mode

Track Sign ins

What's left is to use our Analytics static methods where they belong in the code

    // src/screens/Login.js

    // ...imports

    const Login = () => {

      const { navigate } = useNavigation();

      const [email, setEmail] = useState('');
      const [password, setPassword] = useState('')

      const loginAction = async () => {

        //  validate inputs...
        //  api call for signing in...

        navigate('SignedIn');
        await Analytics.onSignIn({ id: "1", email })
      }

      return (
         // ...
      );

    }
Enter fullscreen mode Exit fullscreen mode

Tracking screens

Here we have multiple options depending on what do we need.

Either track them separately in each component after they're being mounted or making use of events other packages we might have in our project. One example could be the beloved and frequently used react-navigation.

  • useEffect hook inside the components.
const HomepageScreen = () => {

  useEffect(() => {
    Analytics.setCurrentScreen('Homepage');
  }, []);

  return ( ... )
}
Enter fullscreen mode Exit fullscreen mode
  • navigation state changes directly on the app container.
// App.js

const AppContainer = createAppContainer(Navigator);

const App = () => {

    // Helper method
  const getActiveRouteName = navigationState => {
    if (!navigationState) {
      return null;
    }
    const route = navigationState.routes[navigationState.index];
    // dive into nested navigators
    if (route.routes) {
      return getActiveRouteName(route);
    }
    return route.routeName;
  };

  return (
    <Provider store={store}>
      <AppContainer
        onNavigationStateChange={(prevState, currentState, action) => {
          const currentRouteName = getActiveRouteName(currentState);
          const previousRouteName = getActiveRouteName(prevState);

          if (previousRouteName !== currentRouteName) {
            Analytics.setCurrentScreen(currentRouteName);
          }
        }}
      />
    </Provider>
  )
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Custom events

An example would be to track in an image sharing app either the users are more inclined to use the camera or the camera roll.

    // src/screens/Camera.js

    // ...imports
    const CameraScreen = ({ ... }) => {

      const { navigate } = useNavigation();

      const takePicture = async () => {
        if (this.camera) {
            // ...
          Analytics.logEvent("add_image", {
            "take_picture": true,
            "camera_roll": false
          });

          navigate('ImagesList');
        }
      }

      return (
        // ...
      );
    }
Enter fullscreen mode Exit fullscreen mode

Last step: see it working

Everything is installed, configured and implemented. Let's see if we get something from the app to the Firebase Console.

Nothing?...

Well, there is a delay of about 1 hour between logging and seeing the events on the dashboard.

The good news is that there is something we can do to test it quickly - with a latency of about 1 second.

It is called DebugView.

iOS

For the iOS project, we can pass an argument on the Run process by editing the scheme.

The argument is called -FIRDebugEnabled

FIRDebugEnabled

For the Release builds, we should specify the argument -FIRDebugDisabled.

Android

To enable Debug mode on Android, just run:

adb shell setprop debug.firebase.analytics.app package_name
Enter fullscreen mode Exit fullscreen mode

This behaviour persists until you explicitly disable Debug mode by specifying the following command-line argument:

adb shell setprop debug.firebase.analytics.app .none.
Enter fullscreen mode Exit fullscreen mode

Now run the apps again and you should see some action in the Firebase console:

Debug view

From my experience: if for some mystical reason it doesn't work on iOS, what did the trick for me is to manually link the libraries in Xcode, like in the picture below.

Integrate libs

Conclusion

And that's pretty much it.

What you can do from here is release your app and gather valuable information. Pair it with data from Google Analytics and you have the power (and the data to back it up) to decide what's the best next move.

For the full code here's the Github link: https://github.com/calincrist/imageSharingApp.

Previous blog post:
How To Do Authentication using AWS Amplify in iOS

💖 💪 🙅 🚩
calin_crist
Calin-Cristian Ciubotariu

Posted on January 17, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related