React Native (Bridge) (23.02.X)

Follow

This document will guide you through the process of Countly SDK installation and it applies to version 23.02.X
Countly is an open source SDK, you can take a look at our SDK code in the Github repo

Click here, to access the documentation for older SDK versions.

This is the Countly SDK for React Native applications. It features bridging, meaning it includes all the functionalities that Android and iOS SDKs provide rather than having those functionalities as React Native code.

Supported Platforms: This SDK supports iOS and Android platforms.

You can take a look at our example application in this Github repo. It shows, how the majority of the SDK functionality can be used. After you have cloned the repo, then run the following commands from the root folder:

npm install                         # Install dependencies
cd ios                              # Move to ios directory
pod install                         # Download and install pods
cd ../ # Move to parent directory react-native run-android # OR # Run the android project react-native run-ios # Run the iOS project

Adding the SDK to the project

Run the following snippet in the root directory of your React Native project to install the npm dependencies and link native libraries

# Include the Countly Class in the file that you want to use.
npm install --save https://github.com/Countly/countly-sdk-react-native-bridge.git
# OR 
npm install --save countly-sdk-react-native-bridge@latest

# Linking the library to your app
cd ios 
pod install
cd ..

SDK Integration

Minimal setup

We will need to call two methods (initWithConfig and start) in order to set up our SDK. These methods should only be called once during the app's lifecycle and should be done as early as possible. Your main app component's componentDidMountmethod may be a good place.

import Countly from 'countly-sdk-react-native-bridge';
import Countly from 'countly-sdk-react-native-bridge/CountlyConfig';

if(!await Countly.isInitialized()) {
// create Countly config object const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY"); await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config. Countly.start(); // start session tracking }

Please check here for more information on how to acquire your application key (APP_KEY) and server URL.

After initWithConfig and start have been called once, you may use the commands in the rest of this document to send additional data and metrics to your server.

If you are in doubt about the correctness of your Countly SDK integration you can learn about the verification methods from here.

Enable logging

The first thing you should do while integrating our SDK is enable logging. If logging is enabled, then our SDK will print out debug messages about its internal state and encountered problems.

Call setLoggingEnabled on the config object to enable logging:

  // create Countly config object
  const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
// ... countlyConfig.setLoggingEnabled(true); // Enable countly internal debugging logs await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config.

For more information on where to find the SDK logs you can check the documentation here.

Device ID

You may provide your own custom device ID when initializing the SDK using the method below.

  // create Countly config object
const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY"); // ... countlyConfig.setDeviceId(DEVICE_ID); // Set device ID await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config.

SDK data storage

For iOS: SDK data is stored in Application Support Directory in a file named "Countly.dat" For Android: SDK data is stored in SharedPreferences. A SharedPreferences object points to a file containing key-value pairs and provides simple methods to read and write them.

Crash reporting

The Countly SDK has the ability to collect crash reports, which you may examine and resolve later on the server.

Automatic crash handling

With this feature, the Countly SDK will generate a crash report if your application crashes due to an exception and will send it to the Countly server for further inspection.

If a crash report cannot be delivered to the server (e.g. no internet connection, unavailable server, etc.), then the SDK stores the crash report locally in order to send the report again, when the connection to server is restored.

You will need to call the following method before calling initWithConfig in order to activate automatic crash reporting.

  // create Countly config object
  const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
  // ...
  countlyConfig.enableCrashReporting(); // Enable crash reports
  await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config.

Automatic crash report segmentation

You may add a key/value segment to crash reports. For example, you could set which specific library or framework version you used in your app. You may then figure out if there is any correlation between the specific library or another segment and the crash reports.

Use the following function for this purpose:

var segment = {"Key": "Value"};
Countly.setCustomCrashSegments(segment);

Handled exceptions

You might catch an exception or similar error during your app’s runtime.

You may also log these handled exceptions to monitor how and when they are happening with the following command:

Countly.logException(stack, nonfatal, customSegments);

The method logExceptiontakes a string for the stack trace, a boolean flag indicating if the crash is considered fatal or not, and a segments dictionary to add additional data to your crash report.

Below are some examples that how to log handled/nonfatal and unhandled/fatal exceptions manually.

1. Manually report handled exception

Countly.logException("STACK_TRACE_STRING", true);

2. Manually report handled exception with segmentation

Countly.logException("STACK_TRACE_STRING", true, {"_facebook_version": "0.0.1"});

3. Manually report fatal exception

Countly.logException("STACK_TRACE_STRING", false);

4. Manually report fatal exception with segmentation

Countly.logException("STACK_TRACE_STRING", false, {"_facebook_version": "0.0.1"});

Crash breadcrumbs

Throughout your app, you can leave crash breadcrumbs which would describe previous steps that were taken in your app before the crash. After a crash happens, they will be sent together with the crash report.

Following the command adds crash breadcrumb:

Countly.addCrashLog(String record) 

Native C++ Crash Reporting

If you have C++ libraries in your React Native Android app, the React Native Bridge SDK allows you to record possible crashes in your Countly server by integrating the sdk-nativedeveloped within our Android SDK. Find more information here.

As this feature is optional, you will need to do some changes in your react native android project files, to make it available.

Go to YOUR_REACT_NATIVE_PROJECT_PATH/android/app/build.gradleand add the package dependency (please change the LATEST_VERSION below by checking our Maven page, currently 20.11.6):

dependencies {
    implementation 'ly.count.android:sdk-native:LATEST_VERSION'    
}

Then call Countly.initNative() method as early as possible in your react native android project to be able to catch setup time crashes, it should be preferably in the "onCreate" callback of the Application class.

You may find MainApplication.java at this path:

YOUR_REACT_NATIVE_PROJECT_PATH/android/app/src/main/java/com/PROJECT_NAME

// import this in your Application class
import ly.count.android.sdknative.CountlyNative; 

// call this function in "onCreate" callback of Application class
CountlyNative.initNative(getApplicationContext());

getApplicationContext() is needed to determine a storage place for minidump files.

Sending crash dump files to the server will be taken care of by the SDK during the next app initialization. We also provide a Gradle plugin that automatically uploads symbol files to your server (these are needed for the symbolication of crash dumps). Integrate it into your React Native project as explained in the relevant Android documentation page.

This is what the debug logs will look like if you use this feature:

$ adb logcat -s Countly:V countly_breakpad_cpp:V

# when Countly.initNative() is called 

D/countly_breakpad_cpp(123): breakpad initialize succeeded. dump files will be saved at /Countly/CrashDumps

# when a crash occurs (you may trigger one by Countly.testCrash())

D/countly_breakpad_cpp(123): DumpCallback started
D/countly_breakpad_cpp(123): DumpCallback ==> succeeded path=/Countly/CrashDumps/30f6d9b8-b3b2-1553-2efe0ba2-36588990.dmp

# when app is run again after the crash 

D/Countly (124): Initializing...
D/Countly (124): Checking for native crash dumps
D/Countly (124): Native crash folder exists, checking for dumps
D/Countly (124): Crash dump folder contains [1] files
D/Countly (124): Recording native crash dump: [30f6d9b8-b3b2-1553-2efe0ba2-36588990.dmp]

Events

An Event is any type of action that you can send to a Countly instance, e.g. purchases, changed settings, view enabled, and so on. This way it's possible to get much more information from your application compared to what is sent from the SDK to the Countly instance by default.

Here are the detail about properties which we can use with event:

  • key identifies the event
  • count is the number of times this event occurred
  • sum is an overall numerical data set tied to an event. For example, total amount of in-app purchase event.
  • duration is used to record and track the duration of events.
  • segmentation is a map of key-value pairs, that can be used to track additional information.
Data passed should be in UTF-8

All data passed to the Countly server via the SDK or API should be in UTF-8.

Recording Events

We will be recording a purchase event below. Here is a quick summary of the information with which each usage will provide us:

  • Usage 1: how many times the purchase event occurred.
  • Usage 2: how many times the purchase event occurred + the total amount of those purchases.
  • Usage 3: how many times the purchase event occurred + from which countries and application versions those purchases were made.
  • Usage 4: how many times the purchase event occurred + the total amount, both of which are also available, segmented into countries and application versions.

1. Event key and count

var event = {"eventName":"Purchase","eventCount":1};
Countly.sendEvent(event);

2. Event key, count, and sum

var event = {"eventName":"Purchase","eventCount":1,"eventSum":"0.99"};
Countly.sendEvent(event);

3. Event key and count with segmentation(s)

var event = {"eventName":"purchase","eventCount":1}; 
event.segments = {"Country" : "Germany", "app_version" : "1.0"}; 
Countly.sendEvent(event);

4. Event key, count, and sum with segmentation(s)

var event = {"eventName":"purchase","eventCount":1};
event.segments = {"Country" : "Germany", "app_version" : "1.0","eventSum":"0.99"};
Countly.sendEvent(event);

Those are only a few examples of what you can do with events. You may extend those examples and use Country, app_version, game_level, time_of_day, and any other segmentation that will provide you with valuable insights.

Timed events

It's possible to create timed events by defining a start and a stop moment.

String eventName = "Event Name";

//start some event
Countly.startEvent(eventName);
//wait some time

//end the event 
Countly.endEvent(eventName);

You may also provide additional information when ending an event. However, in that case, you have to provide the segmentation, count, and sum. The default values for those are "null", 1 and 0.

String eventName = "Event Name";

//start some event
Countly.startEvent(eventName);

var event = { "eventName": eventName, "eventCount": 1, "eventSum": "0.99" };
event.segments = { "Country": "Germany", "Age": "28" };

//end the event while also providing segmentation information, count and sum
Countly.endEvent(event);

You may cancel the started timed event in case it is not relevant anymore:

String eventName = "Event name";

//start some event
Countly.startEvent(eventName);
//wait some time

//cancel the event 
Countly.cancelEvent(eventName);

View tracking

You may track custom views with the following code snippet:

Countly.recordView("View Name")

While manually tracking views, you may add your custom segmentation to them like this:

var viewSegmentation = { "Country": "Germany", "Age": "28" };
Countly.recordView("View Name", viewSegmentation);

To review the resulting data, open the dashboard and go to Analytics > Views. For more information on how to use view tracking data to its fullest potential, click here.

001.png

Device ID management

When the SDK is initialized the first time and no custom device ID is provided, a random one will be generated. For most use cases that is enough as it provides a random identity to one of your apps users.

For iOS: the device ID generated by the SDK is the Identifier For Vendor (IDFV). For Android: the device ID generated by the SDK is the OpenUDID or Google Advertising ID.

To solve other potential use cases, we provide 3 ways to handle your device id:

  • Changing device ID with merge
  • Changing device ID without merge
  • Using a temporary ID

Changing device ID with and without merge

You may configure or change the device ID anytime using the method below.

Countly.changeDeviceId(DEVICE_ID, ON_SERVER);

You may either allow the device to be counted as a new device or merge it with the existing data on the server. If onServer is set to true, the old device ID on the server will be replaced with the new one, and data associated with the old device ID will be merged automatically. Otherwise, if onServer is set to false, the device will be counted as a new device on the server.

Temporary Device ID

You may use a temporary device ID mode for keeping all requests on hold until the real device ID is set later.

To enable this when initializing the SDK, use the method below.

  // create Countly config object
  const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
  //...
  countlyConfig.setDeviceId(Countly.TemporaryDeviceIDString); // Set temporary device ID
  await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config.

To enable a temporary device ID after initialization, use the method below.

Countly.changeDeviceId(Countly.TemporaryDeviceIDString, ON_SERVER);

Note: When passing Countly.TemporaryDeviceIDString for the deviceID parameter, the argument for the onServerparameter does not matter.

As long as the device ID value is Countly.TemporaryDeviceIDString, the SDK will be in temporary device ID mode and all requests will be on hold, but they will be persistently stored.

When in temporary device ID mode, method calls for presenting feedback widgets and updating remote config will be ignored.

Later, when the real device ID is set using the Countly.changeDeviceId(DEVICE_ID, ON_SERVER); method, all requests which have been kept on hold until that point will start with the real device ID.

Retrieving the device id

You may want to see what device id Countly is assigning for the specific device. For that, you may use the following calls.

let currentDeviceId = await Countly().getCurrentDeviceId();

You can use getDeviceIDType method which returns a value identifying the the current device ID type. The possible type are:

  • DEVELOPER_SUPPLIED - device ID was supplied by the host app.
  • SDK_GENERATED - device ID was generated by the SDK.
  • TEMPORARY_ID - the SDK is in temporary device ID mode.

To determine the specific type, you would compare the return value to SDK defined constants.

let deviceIdType = await Countly.getDeviceIDType();
if(deviceIdType == DeviceIdType.SDK_GENERATED) {
  //this is a SDK generated device ID
} else if(deviceIdType == DeviceIdType.DEVELOPER_SUPPLIED) {
  //this is a device ID that was provided by the developer
} else if(deviceIdType == DeviceIdType.TEMPORARY_ID) {
  //the SDK is in temporary ID mode
}

Push Notifications

Please first check our Push Notifications documentation to see how you can use this feature. Since Android and iOS handles notifications differently (from how you can send them externally to how they are handled in native code), we need to provide different instructions for these two platforms.

General Setup

Android and iOS devices require different setups to enable the push notifications. Android uses the setPushNotificationChannelInformation, whereas iOS uses the setPushTokenType method on the CountlyConfig object. Both of these methods must be called before initializing the SDK.

setPushTokenType and setPushNotificationChannelInformation methods require the minimum SDK version of 23.2.4.

For previous versions, please use pushTokenType instead.

iOS Android
// create Countly config object
const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
// Set the token types: DEVELOPMENT, PRODUCTION or ADHOC
countlyConfig.setPushTokenType(Countly.messagingMode.DEVELOPMENT);
// Initialize the countly SDK with config.
await Countly.initWithConfig(countlyConfig);
    

When you are ready to initialize Countly Push, call Countly.askForNotificationPermission() after initWithConfig, using the method below. This method will ask for permission, and send push token to Countly server.

Countly.askForNotificationPermission();

Push Notification Customization

Notification Accent Color

setPushNotificationAccentColor requires the minimum SDK version of 23.2.4.

Currently, push notification customization is only supported for Android devices. You can only select the accent color of your notifications and provide a custom notification sound.

You can provide a color in hexadecimal color system to the CountlyConfig object with the setPushNotificationAccentColor method before the SDK initialization.

// Set notification accent color
countlyConfig.setPushNotificationAccentColor("#000000");

Custom Sound

Currently custom sound feature is only available for Android.

To use a custom sound for your notifications in Android you should provide a path to your sound file and pass it as the first parameter of the askForNotificationPermission method.

// CUSTOM_SOUND_PATH is an optional parameter and currently only supported for Android.
Countly.askForNotificationPermission("CUSTOM_SOUND_PATH");

We will use this custom sound path to create a soundUri and set the sound of notification channel.

If you would like to use a custom sound in your push notifications, they must be present on the device. They cannot be linked from the Internet.

We recommend to add the custom sound file in your Android project res/raw folder. Always create a "raw" folder by right clicking on Resources (res) folder and select New -> Android Resource Directory.
Your custom sound path will be like this after adding it in res/raw folder:
"android.resource://PACKAGE_NAME/raw/NAME_OF_SOUND_WITHOUT_EXTENSION";

For more information about custom push notification sounds in Android check this link:

https://support.count.ly/hc/en-us/articles/360037754031-Android#custom-notification-sound

Android Setup

Step 1: For FCM credentials setup please follow the instruction from this URL https://support.count.ly/hc/en-us/articles/360037754031-Android#getting-fcm-credentials.

Step 2: Make sure you have google-services.json from https://firebase.google.com/

Step 3: Make sure the app package name and the google-services.json package_name matches.

Step 4: Place the google-services.json file inside android/app

Step 5: Use google services latest version from this link https://developers.google.com/android/guides/google-services-plugin

Step 6: Add the following line in the file android/build.gradle

buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.3.2'
    }
}

Step 7: Add the following line in file android/app/build.gradle

// Add this at the bottom of the file
apply plugin: 'com.google.gms.google-services'

Note: You need to do some additional steps to handle multiple messaging services. If you are using other plugins for push notifications, please follow the instructions from this URL:
Handling multiple FCM services

Additional Intent Redirection Checks

Intent Redirection Vulnerability is an issue that lets your app allow malicious apps to access private app components or files. Google removes apps from Google Play that is susceptible to Intent Redirection Vulnerability. For Push Notifications, we are also using Intent Redirection in our SDK, so for that reason, we have also implemented additional Intent Redirection protection.

By default additional intent redirection is enabled for intent redirect security, you can disable the additional intent redirection:

// create Countly config object
const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY"); // ... countlyConfig.configureIntentRedirectionCheck([], [], false); // Disable intent redirection security await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config.

If these are enabled then the SDK will enforce additional security checks. More info can be found here

If, for some reason, the 'activity name' does not start with the 'application package name' (for e.g if you are using Android Product/Build Flavors to create multiple apps with the same code base), then you need to provide the additional allowed class and package names for Intent Redirection manually.

You can set the allowed package and class names for Intent Redirection using this call:

// create Countly config object
const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
// ... countlyConfig.configureIntentRedirectionCheck(["MainActivity"], ["com.countly.demo"]); // configure redirection check await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config.

iOS Setup

Push notifications are enabled by default for iOS, but if you wish to disable them, you can define the macro "COUNTLY_EXCLUDE_PUSHNOTIFICATIONS" in the project's preprocessor macros setting. The location of this setting may vary depending on the development environment you are using.

For example, in Xcode, you can define this macro by navigating to the project settings, selecting the build target, and then selecting the "Build Settings" tab. Under the "Apple LLVM - Preprocessing" section, you will find the "Preprocessor Macros" where you can add the macro "COUNTLY_EXCLUDE_PUSHNOTIFICATIONS" to the Debug and/or Release fields. This will exclude push notifications from the build.

For iOS push notification integration please follow the instructions from here

For React Native you can find CountlyNotificationService.h/m file under Pods/Development Pods/CountlyReactNative/CountlyNotificationService.h/m

Pro Tips to find the files from deep hierarchy:

  • You can filter the files in the navigator using a shortcut ⌥⌘J (Option-Command-J), in the filter box type "CountlyNotificationService" and it will show the related files only.
  • You can find the file using the shortcut ⇧⌘O (Shift-Command-O) and then navigate to that file using the shortcut ⇧⌘J (Shift-Command-J)

You can drag and drop both .h and .m files from Pod to Compile Sources.

Countly_RN_PUSH.png

Handling push callbacks

To register a Push Notification callback after initialising the SDK, use the method below.

Countly.registerForNotification(function(theNotification){
console.log(JSON.stringify(theNotification));
});

In order to listen to notifications received and the click events, add the code below in AppDelegate.m

Add header files

#import "CountlyReactNative.h"
#import <UserNotifications/UserNotifications.h>

Add this call [CountlyReactNative startObservingNotifications]; in didFinishLaunchingWithOptions: method to handle push notification receive and action callbacks when SDK is not initialized.

// For push notification received and action callbacks.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[CountlyReactNative startObservingNotifications];
}

Before @end add these method

// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
  [CountlyReactNative onNotification: userInfo];
  completionHandler(0);
}

// When app is killed.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler{
  [CountlyReactNative onNotificationResponse: response];
  completionHandler();
}

// When app is running.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
  [CountlyReactNative onNotification: notification.request.content.userInfo];
  completionHandler(0);
}

Data Structure Received in Push Callbacks

Here is an example of how the data will be received in push callbacks:004.png

Data Received for Android platform:

{
"c.e.cc": "TR",
"c.e.dt": "mobile",
"Key": "value",
"c.i": "62b59b979f05a1f5e5592036",
"c.l": "https:\/\/www.google.com\/",
"c.m": "https:\/\/count.ly\/images\/logos\/countly-logo-mark.png?v2",
"c.li": "notify_icon",
"badge": "1",
"sound": "custom",
"title": "title",
"message": "Message"
}

Data Received for iOS platform:

{
"c": {
"i": "62b5b945cabedb0870e9f217",
"l": "https:\/\/www.google.com\/",
"e": {
"dt": "mobile",
"cc": "TR"
},
"a": "https:\/\/count.ly\/images\/logos\/countly-logo-mark.png"
},
"aps": {
"mutable-content": 1,
"alert": {
"title": "title",
"subtitle": "subtitle",
"body": "Message"
},
"badge": 1,
"sound": "custom"
},
"Key": "value"
}

User Location

Countly allows you to send geolocation-based push notifications to your users. By default, the Countly Server uses the GeoIP database to deduce a user's location.

Set User Location

If your app has a different way of detecting location, you may send this information to the Countly Server by using the countlyConfig.setLocation orCountly.setLocation methods.

We recommend using the countlyConfig.setLocation method before initialization to send location. This includes:

  • countryCode a string in ISO 3166-1 alpha-2 format country code
  • city a string specifying city name
  • location a string comma-separated latitude and longitude
  • IP a string specifying an IP address in IPv4 or IPv6 formats
// create Countly config object
const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
//...
var countryCode = "us";
var city = "Houston";
var latitude = "29.634933";
var longitude = "-95.220255";
var ipAddress = "103.238.105.167";

countlyConfig.setLocation(countryCode, city, latitude + "," + longitude, ipAddress);
// Set location
await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config.

Geolocation recording methods may also be called at any time after the Countly SDK has started. To do so, use the setLocation method as shown below.

// Example for setLocation
var countryCode = "us";
var city = "Houston";
var latitude = "29.634933";
var longitude = "-95.220255";
var ipAddress = "103.238.105.167";

Countly.setLocation(countryCode, city, latitude + "," + longitude, ipAddress);

Disable Location

To erase any cached location data from the device and stop further location tracking, use the following method. Note that if after disabling location, the setLocationis called with any non-null value, tracking will resume.

//disable location tracking
Countly.disableLocation();

Remote Config

Remote config allows you to modify how your app functions or looks by requesting key-value pairs from your Countly server. The returned values may be modified based on the user profile. For more details, please see the Remote Config documentation.

Manual Remote Config download

There are three ways for manually requesting a remote config update:

  • Manually updating everything
  • Manually updating specific keys
  • Manually updating everything except specific keys.

Each of these requests also has a callback. If the callback returns a non-null value, the request will encounter an error and fail.

The remoteConfigUpdate replaces all stored values with the ones from the server (all locally stored values are deleted and replaced with new ones). The advantage is that you can make the request whenever it is desirable for you. It has a callback to let you know when it has finished.

Countly.remoteConfigUpdate(function(data){
console.log(data);
});

Or you might only want to update specific key values. To do so, you will need to call updateRemoteConfigForKeysOnly with the list of keys you would like to be updated. That list is an array with the string values of those keys. It has a callback to let you know when the request has finished.

Countly.updateRemoteConfigForKeysOnly(){
Countly.updateRemoteConfigForKeysOnly(["aa", "bb"],function(data){
console.log(data);
});

Or you might want to update all the values except a few defined keys. To do so, call updateRemoteConfigExceptKeys. The key list is an array with string values of the keys. It has a callback to let you know when the request has finished.

Countly.updateRemoteConfigExceptKeys(["aa", "bb"],function(data){
console.log(data);
});

When making requests with an "inclusion" or "exclusion" array, if those arrays are empty or null, they will function the same as a simple manual request and will update all the values. This means it will also erase all keys not returned by the server.

Getting Remote Config values

To request a stored value, call getRemoteConfigValueForKey or getRemoteConfigValueForKeyP with the specified key.

Countly.getRemoteConfigValueForKey("KeyName", function(data){
console.log(data);
});

var data = await Countly.getRemoteConfigValueForKeyP("KeyName");

Clearing Stored Remote Config values

At some point, you might like to erase all the values downloaded from the server. You will need to call one function to do so.

Countly.remoteConfigClearValues();

User Feedback

There are a different ways of receiving feedback from your users: the Star-rating dialog, the Ratings widget, and the Surveys widgets (Surveys and NPS®).

The Star-rating dialog allows users to give feedback as a rating from 1 to 5. The Ratings widget allows users to rate using the same 1 to 5 emoji-based rating system as well as leave a text comment. The Surveys widgets (Surveys and NPS®) allow for even more targeted feedback from users.

Ratings

Star Rating Dialog

The Star-rating integration provides a dialog for getting user feedback about an application. It contains a title, a simple message explaining its purpose, a 1 through 5-star meter for getting users rating, and a dismiss button in case the user does not want to give a rating.

This star rating has nothing to do with Google Play Store ratings and reviews. It is simply for getting brief feedback from your users to be displayed on the Countly dashboard. If the user dismisses the star-rating dialog without giving a rating, the event will not be recorded.

Countly.showStarRating();

The star-rating dialog's title, message, and dismiss button text may be customized through the setStarRatingDialogTexts method.

const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
// ... countlyConfig.setStarRatingDialogTexts("Custom title", "Custom message", "Custom dismiss button text"); // Set dialog texts await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config.

Rating Widget

The rating widget displays a server-configured widget to your user devices.

002.png

All the text fields in the example above can be configured and replaced with a custom string of your choice.

In addition to a 1 through 5 rating, it is possible for users to leave a text comment and an email, should the user like to be contacted by the app developer.

Showing the rating widget in a single call involves a two-set process: before it is shown, the SDK tries to contact the server to get more information about the dialog. Therefore, a network connection to it is needed.

To display the widget after initializing the SDK, you will first need to get the widget ID from your server, as shown below.

003.png

Then, call the function to show the widget popup using the widget ID below.

Countly.presentRatingWidgetWithID("WidgetId", "Button Text", function(error){
if (error != null) {
console.log(error);
}
});

Feedback Widget

It is possible to display 2 kinds of Surveys widgets: NPS and Surveys. Both widgets are shown as webviews and they both use the same code methods.

Before any Surveys widget can be shown, you need to create them in your Countly Dashboard.

When the widgets are created, you need to use 2 calls in your SDK: one to get all available widgets for a user and another to display, a chosen widget.

To get your available widget list, use the call below.

Countly.getFeedbackWidgets(function(retrivedWidgets, error){
if (error != null) {
console.log("Error : " + error);
}
else {
console.log(retrivedWidgets.length)
}
});

From the callback, get the list of all available widgets that apply to the current device ID.

The objects in the returned list look like this:

{ 
"id" : "WIDGET_ID",
"type" : "WIDGET_TYPE",
"name" : "WIDGET_NAME",
}

To determine what kind of widget that is, check the "type" value. The potential values are survey and nps.

Then use the widget type and description (which is the same as provided in the Dashboard) to decide which widget to show.

After you have decided which widget you want to display, call the function below.

Countly.presentFeedbackWidgetObject(RETRIEVED_WIDGET_OBJECT, "CLOSE_BUTTON_TEXT", function() {
console.log("WidgetshownCallback");
},
function() {
console.log("WidgetclosedCallabck");
})

User Profiles

You can provide Countly any details you may have about your user or visitor. This will allow you to track each specific user on the "User Profiles" tab, available in Countly Enterprise Edition. For more information, please review the User Profiles documentation.

User details can be sent to your Countly instance in two separate ways depending on your needs and use case. The first option is to set the user data in bulk by using Countly.userDataBulk call. This allows you to bundle up all your user detail information in a single request to the server and helps minimize the traffic to your server. If you have access to multiple user details of your visitors at a given time, using this method would be the preferred option

The second option is using Countly.userData calls to send user details to your server as separate requests. This can be useful in situations where you gain access to user information one at a time and you would like to report that info to your server on the fly.

Using Countly.userDataBulk call requires you to call Countly.userDataBulk.save() call,  manually, to trigger sending data to your server, as a signifier marking the end of user details you want to record. In the case of Countly.userData though, you will not need to use any other methods to initiate the data transmission. It will handle that logic internally.

Note: There is some inconsistency in underlying iOS and Android code when using the bulk mode. This problem surfaces when modifying the same key multiple times. For iOS it keeps the last value for a key on all push/pull user property calls, for Android it will try to combine them. For now the work around is that you need to call the “Countly.userDataBulk.save();” after every push/pull user property call. This does eliminate some of the potential gains of this mode, but it does result in the expected result server-side.

Snippets below show examples of using these calls in various situations. After you send a user’s data, it can be found in your Dashboard under Users > User Profiles.

Setting Predefined Values

Predefined user properties are a set of default keys that are commonly used in visitor data collection.

Bellow you can see how this can be set using the regular user property access mode and using the bulk mode:

Regular mode Bulk mode
var options = {};
options.name = "Nicola Tesla"; options.username = "nicola"; options.email = "info@nicola.tesla"; options.organization = "Trust Electric Ltd"; options.phone = "+90 822 140 2546"; options.picture = "http://www.trust.electric/images/people/nicola.png"; options.picturePath = ""; options.gender = "M"; options.byear = 1919;
Countly.setUserData(options);

Setting Custom Values

Custom user properties are any arbitrary values that you would like to store under your user's profile. These values can be internal IDs, registration dates, horoscopes, or any other value that is not included in the predefined user properties.

Regular mode Bulk mode
var options = {};

options.customeValueA = "nicola";
options.customeValueB = "info@nicola.tesla";
// ...

Countly.setUserData(options);

Modifying Data

Additionally, you can modify these custom values in various ways like increasing a number, pushing new values to an array, etc.  You can see the whole range of operations below.

Regular mode Bulk mode
Countly.userData.setProperty("keyName", "keyValue"); //set custom property
Countly.userData.setOnce("keyName", 200); //set custom property only if property does not exist
Countly.userData.increment("keyName"); //increment value in key by one
Countly.userData.incrementBy("keyName", 10); //increment value in key by provided value
Countly.userData.multiply("keyName", 20); //multiply value in key by provided value
Countly.userData.saveMax("keyName", 100); //save max value between current and provided
Countly.userData.saveMin("keyName", 50); //save min value between current and provided
Countly.userData.setOnce("setOnce", 200);//insert value to array of unique values
Countly.userData.pushUniqueValue("type", "morning");//insert value to array of unique values
Countly.userData.pushValue("type", "morning");//insert value to array which can have duplicates
Countly.userData.pullValue("type", "morning");//remove value from array

Application Performance Monitoring

The Performance Monitoring feature allows you to analyze your application's performance on various aspects. For more details, please review the Performance Monitoring documentation.

Here is how you can utilize the Performance Monitoring feature in your apps:

First, you need to enable the Performance Monitoring feature:

const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
// ... countlyConfig.enableApm(); // Enable APM features. await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config.

With this, the Countly SDK will start measuring some performance traces automatically, including app foreground time, app background time. Additionally, custom traces and network traces can be manually recorded.

App Start Time

For the app start time to be recorded, you need to call the appLoadingFinished method. Make sure this method is called after initWithConfig.

const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
// ... await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config. Countly.appLoadingFinished(); // Call the appLoadingFinished method

This calculates and records the app launch time for performance monitoring. It should be called when the app is loaded and it successfully displayed its first user-facing view. E.g. componentDidMount: method or wherever is suitable for the app's flow. The time passed since the app has started to launch will be automatically calculated and recorded for performance monitoring. Note that the app launch time can be recorded only once per app launch. So, the second and following calls to this method will be ignored.

Custom trace

You may also measure any operation you want and record it using custom traces. First, you need to start a trace by using the startTrace(traceKey) method:

Countly.startTrace(traceKey);

Then you may end it using the endTrace(traceKey, customMetric)method, optionally passing any metrics as key-value pairs:

String traceKey = "Trace Key";
Map<String, int> customMetric = {
  "ABC": 1233,
  "C44C": 1337
};
Countly.endTrace(traceKey, customMetric);

The duration of the custom trace will be automatically calculated upon ending.

Trace names should be non-zero length valid strings. Trying to start a custom trace with the already started name will have no effect. Trying to end a custom trace with already ended (or not yet started) name will have no effect.

You may also cancel any custom trace you started using the cancelTrace(traceKey)method:

Countly.cancelTrace(traceKey);

Additionally, if you need you may cancel all custom traces you started, using the clearAllTraces()method:

Countly.clearAllTraces(traceKey);

Network trace

You may record manual network traces using the recordNetworkTrace(networkTraceKey, responseCode, requestPayloadSize, responsePayloadSize, startTime, endTime) method.

A network trace is a collection of measured information about a network request. When a network request is completed, a network trace can be recorded manually to be analyzed via Performance Monitoring later using the following parameters:

- networkTraceKey: A non-zero length valid string - responseCode: HTTP status code of the received response - requestPayloadSize: Size of the request's payload in bytes - responsePayloadSize: Size of the received response's payload in bytes - startTime: UNIX timestamp in milliseconds for the starting time of the request - endTime: UNIX timestamp in milliseconds for the ending time of the request

Countly.recordNetworkTrace(networkTraceKey, responseCode, requestPayloadSize, responsePayloadSize, startTime, endTime);

User consent

Being compliant with GDPR and other data privacy regulations, Countly provides ways to toggle different Countly tracking features on or off depending on a user's given consent. For more details, please review the Compliance Hub plugin documentation.

Currently, available features with consent control are as follows:

  • sessions - tracking when, how often, and how long users use your app
  • events - allows sending events to the server
  • views - allows tracking which views user visits
  • location - allows sending location information
  • crashes - allows tracking crashes, exceptions, and errors
  • attribution - allows tracking from which campaign did the user come.
  • users - allows collecting and providing user information, including custom properties.
  • push - allows push notifications,
  • star-rating - allows sending the results from Rating Feedback widgets.
  • feedback - allows showing the results from Survey and NPS® Feedback widgets.
  • apm - allows application performance monitoring.
  • remote-config - allows downloading remote config values from your server.

Setup During Init

The requirement for consent is disabled by default. To enable it, you will have to call setRequiresConsent with true before initializing Countly.

const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
// Enable consent requirement
Countly.setRequiresConsent(true);

By default, no consent is given. That means that if no consent is enabled, Countly will not work and no network requests related to its features will be sent. 

To give consent during initialization, you have to call setConsentEnabledon the config object with an array of consent values.

const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
countlyConfig.giveConsent(["events", "views", "star-rating", "crashes"]);

The Countly SDK does not persistently store the status of given consents except push notifications. You are expected to handle receiving consent from end-users using proper UIs depending on your app's context. You are also expected to store them either locally or remotely. Following this step, you will need to call the giveConsent method on each app launch depending on the permissions you managed to get from the end-users.

Ideally you would give consent during initialization.

Changing Consent

The end-user can change their mind about consents at a later time.

To reflect these changes in the Countly SDK, you can use the removeConsent or giveConsent methods.

// To add/remove consent for a single feature (string parameter)
Countly.giveConsent("events");
Countly.removeConsent("events");

// To add/remove consent for a subset of features (array of strings parameters)
Countly.giveConsent(["events", "views", "star-rating", "crashes"]);
Countly.removeConsent(["events", "views", "star-rating", "crashes"]);

You can also either give or remove consent to all possible SDK features:

// To add/remove consent for all available features
Countly.giveAllConsent();
Countly.removeAllConsent();

Security and privacy

Parameter tampering protection

You can set optional salt to be used for calculating checksum of request data, which will be sent with each request using &checksum field. You need to set exactly the same salt on the Countly server. If salt on Countly server is set, all requests would be checked for the validity of &checksum field before being processed.

const countlyConfig = new CountlyConfig("https://try.count.ly", "YOUR_APP_KEY");
// ... countlyConfig.enableParameterTamperingProtection("salt"); // Enable tamper protection salt await Countly.initWithConfig(countlyConfig); // Initialize the countly SDK with config.

Make sure not to use salt on the Countly server and not on the SDK side, otherwise, Countly won't accept any incoming requests.

Pinned Certificate (Call this method before initialization)

Terminal

openssl s_client -connect try.count.ly:443 -showcerts

Run the above command and copy the content inside the begin certificate and the end certificate.

Example files: Android and iOS. Note that Android needs the certificate string, while iOS needs the entire .cer file.

Android

cd AwesomeProject
ls
count.ly.cer
mkdir -p ./android/app/src/main/assets/
cp ./count.ly.cer ./android/app/src/main/assets/

iOS

open ./ios/AwesomeProject.xcworkspace
Right click on AwesomeProject and select `New Group` (ScreenShot 1).
Name it `Resources`.
Drag and Drop count.ly.cer file under that folder (ScreenShot 2).
Make sure copy bundle resources has your certificate (Screenshot 4).
Screenshot_2020-07-07_at_11.39.02_AM.png
ScreenShot_Pinned_Certificate_1.png

Screenshot_Pinned_Certificate_2.png

Screenshot_2020-07-07_at_11.39.40_AM.png

JavaScript

Countly.pinnedCertificates("count.ly.cer");

Note that count.ly.cer is the name of the file. Replace this file with the one you have.

Using Proguard

If you are using Countly Messaging in your Android application, it is recommended to obfuscate the Countly Messaging classes using Proguard. To do so, please follow the instructions below:

  1. Locate the app/proguard-rules.pro file within the /android/app/ folder.

  2. Add the following lines to the file:

-keep class ly.count.android.sdk.** { *; }
  1. If Proguard is not yet configured, you must first enable shrinking and obfuscation in the build file. To do so, locate the build.gradle file within the /android/app/ folder.

  2. Add the following lines in bold to the build.gradle file:

...

buildTypes {
        release { // Enables code shrinking, obfuscation, and optimization for only your project's release build type.
            ...
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
...

By following these steps, the Countly Messaging classes will be obfuscated using Proguard and your application will be better protected against reverse engineering.

Other features

Custom Metrics

Minimum Countly SDK Version

This feature is only supported by the minimum SDK version 20.11.7.

For overriding default metrics or adding extra ones that are sent with begin_session requests, you can use pass 'customMetric' Object to the function Countly.setCustomMetrics(customMetric), 'customMetric' should be an Object with keys and values that are both Strings only. Note that the custom metrics should be set before initialization.

var customMetric = {"key": "value"};
Countly.setCustomMetrics(customMetric);

For more information on the specific metric keys used by Countly, check here.

Example to override 'Carrier' and 'App Version'

var customMetric = {"_carrier": "custom carrier", "_app_version": "2.1"};
Countly.setCustomMetrics(customMetric);

Attribution

This feature is available for the Enterprise Edition.

There are 2 forms of attribution: direct Attribution and indirect Attribution.

Direct Attribution

You can pass "Campaign type" and "Campaign data". The "type" determines for what purpose the attribution data is provided. Depending on the type, the expected data will differ, but usually that will be a string representation of a JSON object.

You can use recordDirectAttribution to set attribution values during initialization.

const campaignData = 'JSON_STRING';
const config = CountlyConfig(SERVER_URL, APP_KEY);
config.recordDirectAttribution('CAMPAIGN_TYPE', campaignData);

You can also use recordDirectAttribution function to manually report attribution later:

const campaignData = 'JSON_STRING';
Countly.recordDirectAttribution('CAMPAIGN_TYPE', campaignData);

Currently this feature is limited and accepts data only in a specific format and for a single type. That type is "countly". It will be used to record install attribution. The data also needs to be formatted in a specific way. Either with the campaign id or with the campaign id and campaign user id.

const campaignData = '{cid:"[PROVIDED_CAMPAIGN_ID]", cuid:"[PROVIDED_CAMPAIGN_USER_ID]"}';
Countly.recordDirectAttribution('countly', campaignData);

Indirect Attribution

This feature would be used to report things like advertising ID's. For each platform those would be different values. For the most popular keys we have a class with predefined values to use, it is called "AttributionKey".

You can use recordDirectAttribution to set attribution values during initialization.

const attributionValues = {};
if(Platform.OS.match('ios')){
attributionValues[AttributionKey.IDFA] = 'IDFA';
} else {
attributionValues[AttributionKey.AdvertisingID] = 'AdvertisingID';
}

const config = CountlyConfig(SERVER_URL, APP_KEY);
config.recordIndirectAttribution(attributionValues);

You can also use recordIndirectAttribution function to manually report attribution later

const attributionValues = {};
if(Platform.OS.match('ios')){
attributionValues[AttributionKey.IDFA] = 'IDFA';
} else {
attributionValues[AttributionKey.AdvertisingID] = 'AdvertisingID';
}

Countly.recordIndirectAttribution(attributionValues);

In case you would be accessing IDFA for ios, for iOS 14+ due to the changes made by Apple, regarding Application Tracking, you need to ask the user for permission to track the Application.

Forcing HTTP POST

If the data sent to the server is short enough, the SDK will use HTTP GET requests. In the event you would like an override so that HTTP POST may be used in all cases, call the setHttpPostForced function after you have called initWithConfig. You may use the same function later in the app’s life cycle to disable the override. This function has to be called every time the app starts, using the method below.

  
// enabling the override
Countly.setHttpPostForced(true);
  
// disabling the override
Countly.setHttpPostForced(false);

Checking if initWithConfig has been called

In the event you would like to check if initWithConfig has been called, use the function below.

Countly.isInitialized().then(result => console.log(result)); // true or false

Checking if onStart has been called

For some applications, there might be a use case where the developer would like to check if the Countly SDK onStart function has been called. To do so, use the call below.

Countly.hasBeenCalledOnStart().then(result => console.log(result)); // true or false 

Interacting with the internal request queue

When recording events or activities, the requests don't always get sent immediately. Events get grouped together. All the requests contain the same app key which is provided in the initWithConfig function.

There are two ways to interact with the app key in the request queue at the moment.

1. You can replace all requests with a different app key with the current app key:

//Replaces all requests with a different app key with the current app key.
Countly.replaceAllAppKeysInQueueWithCurrentAppKey();

In the request queue, if there are any requests whose app key is different than the current app key, these requests app key will be replaced with the current app key. 2. You can remove all requests with a different app key in the request queue:

//Removes all requests with a different app key in request queue.
Countly.removeDifferentAppKeysFromQueue();

In the request queue, if there are any requests whose app key is different than the current app key, these requests will be removed from the request queue.

Setting an event queue threshold

Events get grouped together and are sent either every minute or after the unsent event count reaches a threshold. By default it is 10. If you would like to change this, call:

Countly.setEventSendThreshold(6);

Looking for help?