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:
- AWS Amplify's Analytics
- Segment
- Firebase Analytics
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".
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".
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".
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.
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
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 ..
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' // <---
}
}
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' // <---
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"});
- Log the opening of the app
await firebase.analytics().logAppOpen();
- Log the sign in/sign up event
await firebase.analytics().logLogin({
method: 'facebook',
});
await firebase.analytics().logSignUp({
method: 'facebook',
});
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'
});
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");
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;
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 (
// ...
);
}
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 ( ... )
}
- 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;
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 (
// ...
);
}
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
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
This behaviour persists until you explicitly disable Debug mode by specifying the following command-line argument:
adb shell setprop debug.firebase.analytics.app .none.
Now run the apps again and you should see some action in the Firebase console:
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.
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
Top comments (1)
Adding the react native component:
yarn add @react-native-firebase/app
generates a bunch of incompatibility errors with the various pods.
I am using Xcode12beta4. I am newbie to firebase.
[!] CocoaPods could not find compatible versions for pod "Firebase/CoreOnly":
In snapshot (Podfile.lock):
Firebase/CoreOnly (= 6.30.0)
In Podfile:
RNFBApp (from
../node_modules/@react-native-firebase/app
) was resolved to 8.3.1, which depends onFirebase/CoreOnly (~> 6.28.1)
You have either:
pod repo update
or withpod install --repo-update
.Firebase/CoreOnly
inside your development podRNFBApp
. You should runpod update Firebase/CoreOnly
to apply changes you've made.