iOS SDK - Event Tracking

  1. Analytics Activation
    1. i. Automatic Events
    2. ii. Standard Events
    3. iii. Custom Events
    4. iv. In-App Webview Events
    5. v. Best Practices

a. Analytics Activation Setup your App Delegate

(Only for Objective C code)

#import "R1SDK.h"
#import "R1Emitter.h"
#import "R1Push.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  R1SDK *sdk = [R1SDK sharedInstance];

  // Initialize Analytics      
  sdk.applicationId = @"YOUR APPLICATION ID";  //Ask your RadiumOne contact for an app id
 
  // Start SDK
  [sdk start];

  return YES;
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

    let sdk = R1SDK.sharedInstance();

    // Initialize Anlaytics
    sdk.applicationId = "YOUR APPLICATION ID";

    // Start SDK
    sdk.start();

    return true
}

 

i. Automatic Events

The R1 Connect SDK will automatically capture some generic events in order to get the most meaningful data on how users interact with your app. These events are triggered when the state of the application is changed, and therefore do not require any additional code to work out of the box:

Launch - emitted when the app starts

First Launch - emitted when the app starts for the first time

First Launch After Update - emitted when the app starts for the first time after a version upgrade

Suspend - emitted when the app is put into the background state

Resume - emitted when the app returns from the background state

Application Opened - emitted when the app is opened and can measure when your app is opened after a message is sent

Session Start - emitted when a new session begins

Session End - emitted when a session ends; includes a Session Length attribute with the session length in seconds.

ii. Standard Events

Standard events cover all the main user flows (login, register, share, purchase...) in a standardized format for optimized reporting on the portal, providing a great foundation to your analytics strategy. Once you set them up in your code, they unlock great insights, particularly on user lifetime value.

Note: The last argument in all of the following emitter callbacks, otherInfo, is a dictionary of “key”,”value” pairs or nil, which enables you to customize these events as much as you want.

Login

Tracks a user login within the app

[[R1Emitter sharedInstance] emitLoginWithUserID:@"userId"
userName:@"user_name"
otherInfo:@{@"custom_key":@"value"}];
R1Emitter.sharedInstance()
    .emitLoginWithUserID("userId", userName: "user_name", otherInfo: ["custom_key": "value"]);

Registration

Records a user registration within the app

[[R1Emitter sharedInstance] emitRegistrationWithUserID:@"userId"
userName:@"userName"
country:@"country"
state:@"state"
city:@"city"
otherInfo:@{@"custom_key":@"value"}];
R1Emitter.sharedInstance()
    .emitRegistrationWithUserID("userId", userName: "userName", country: "country", state: "state", city: "city", otherInfo: ["custom_key": "value"]);

Facebook connect

Allows access to Facebook services

NSArray *permissions = @[[R1EmitterSocialPermission socialPermissionWithName:@"photos" granted:YES]];

[[R1Emitter sharedInstance] emitFBConnectWithPermissions:permissions
otherInfo:@{@"custom_key":@"value"}];
let permissions = [R1EmitterSocialPermission.init(name: "photos", granted: true)];

R1Emitter.sharedInstance()
    .emitFBConnectWithPermissions(permissions, otherInfo: ["custom_key": "value"]);

Twitter connect

Allows access to Twitter services

NSArray *permissions = @[[R1EmitterSocialPermission socialPermissionWithName:@"photos" granted:YES]];

[[R1Emitter sharedInstance] emitTConnectWithUserID:@"12345"
userName:@"user_name"
permissions:permissions
otherInfo:@{@"custom_key":@"value"}];
let permissions = [R1EmitterSocialPermission.init(name: "photos", granted: true)];

R1Emitter.sharedInstance()
    .emitTConnectWithUserID("12345", userName: "user_name", permissions: permissions, otherInfo: ["custom_key": "value"]);

User Info

Enables you to send user profiles to the backend.

R1EmitterUserInfo *userInfo = [R1EmitterUserInfo userInfoWithUserID:@"userId"
userName:@"userName"
email:@"user@email.com"
firstName:@"first name"
lastName:@"last name"
streetAddress:@"streetAddress"
phone:@"phone"
city:@"city"
state:@"state"
zip:@"zip"];

[[R1Emitter sharedInstance] emitUserInfo:userInfo
otherInfo:@{@"custom_key":@"value"}];
let userInfo = R1EmitterUserInfo.init(userID: "userID", userName: "userName", email: "email", firstName: "firstName", lastName: "lastName", streetAddress: "streetAddress", phone: "phone", city: "city", state: "state", zip: "zip");

R1Emitter.sharedInstance().emitUserInfo(userInfo, otherInfo: ["custom_key": "value"]);

Upgrade

Tracks an application version upgrade

[[R1Emitter sharedInstance] emitUpgradeWithOtherInfo:@{@"custom_key":@"value"}];
R1Emitter.sharedInstance()
    .emitUpgradeWithOtherInfo(["custom_key": "value"]);

Trial Upgrade

Tracks an application upgrade from a trial version to a full version

[[R1Emitter sharedInstance] emitTrialUpgradeWithOtherInfo:@{@"custom_key":@"value"}];
R1Emitter.sharedInstance()
    .emitTrialUpgradeWithOtherInfo(["custom_key": "value"]);

Screen View

A page view that provides info about that screen

[[R1Emitter sharedInstance] emitScreenViewWithDocumentTitle:@"title"
contentDescription:@"description"
documentLocationUrl:@"http://www.example.com/path"
documentHostName:@"example.com"
documentPath:@"path"
otherInfo:@{@"custom_key":@"value"}];
R1Emitter.sharedInstance()
    .emitScreenViewWithDocumentTitle("title", contentDescription: "description", documentLocationUrl: "http://www.example.com/path", documentHostName: "example.com", documentPath: "path", otherInfo:["custom_key": "value"]);

Transaction

[[R1Emitter sharedInstance] emitTransactionWithID:@"transaction_id"
storeID:@"store_id"
storeName:@"store_name"
cartID:@"cart_id"
orderID:@"order_id"
totalSale:1.5
currency:@"USD"
shippingCosts:10.5
transactionTax:12.0
otherInfo:@{@"custom_key":@"value"}];
R1Emitter.sharedInstance()
    .emitTransactionWithID("transaction_id", storeID: "store_id", storeName: "store_name", cartID: "cart_id", orderID: "order_id", totalSale: 1.5, currency: "USD", shippingCosts: 10.5, transactionTax: 12.0, otherInfo:["custom_key": "value"]);

TransactionItem

R1EmitterLineItem *lineItem = [R1EmitterLineItem itemWithID:@"product_id"
name:@"product_name"
quantity:5
unitOfMeasure:@"unit"
msrPrice:10
pricePaid:10
currency:@"USD"
itemCategory:@"category"];

[[R1Emitter sharedInstance] emitTransactionItemWithTransactionID:@"transaction_id"
lineItem:lineItem
otherInfo:@{@"custom_key":@"value"}];
let lineItem = R1EmitterLineItem.init(
    ID: "product_id",
    name: "product_name",
    quantity: 5,
    unitOfMeasure: "unit",
    msrPrice: 10,
    pricePaid: 10,
    currency: "USD",
    itemCategory: "category");

R1Emitter.sharedInstance()
    .emitTransactionItemWithTransactionID("transaction_id", lineItem: lineItem, otherInfo: ["custom_key": "value"]);

Create Cart

[[R1Emitter sharedInstance] emitCartCreateWithCartID:@"cart_id"
otherInfo:@{@"custom_key":@"value"}];
R1Emitter.sharedInstance()
    .emitCartCreateWithCartID("cart_id", otherInfo: ["custom_key": "value"]);

Delete Cart

[[R1Emitter sharedInstance] emitCartDeleteWithCartID:@"cart_id"
otherInfo:@{@"custom_key":@"value"}];
R1Emitter.sharedInstance()
    .emitCartDeleteWithCartID("cart_id", otherInfo: ["custom_key": "value"]);

Add To Cart

R1EmitterLineItem *lineItem = [R1EmitterLineItem itemWithID:@"product_id"
name:@"product_name"
quantity:5
unitOfMeasure:@"unit"
msrPrice:10
pricePaid:10
currency:@"USD"
itemCategory:@"category"];

[[R1Emitter sharedInstance] emitAddToCartWithCartID:@"cart_id"
lineItem:lineItem
otherInfo:@{@"custom_key":@"value"}];
let lineItem = R1EmitterLineItem.init(
    ID: "product_id",
    name: "product_name",
    quantity: 5,
    unitOfMeasure: "unit",
    msrPrice: 10,
    pricePaid: 10,
    currency: "USD",
    itemCategory: "category");

R1Emitter.sharedInstance()
    .emitAddToCartWithCartID("cart_id", lineItem: lineItem, otherInfo: ["custom_key": "value"]);

Delete From Cart

R1EmitterLineItem *lineItem = [R1EmitterLineItem itemWithID:@"product_id"
name:@"product_name"
quantity:5
unitOfMeasure:@"unit"
msrPrice:10
pricePaid:10
currency:@"USD"
itemCategory:@"category"];

[[R1Emitter sharedInstance] emitDeleteFromCartWithCartID:@"cart_id"
lineItem:lineItem
otherInfo:@{@"custom_key":@"value"}];
let lineItem = R1EmitterLineItem.init(
    ID: "product_id",
    name: "product_name",
    quantity: 5,
    unitOfMeasure: "unit",
    msrPrice: 10,
    pricePaid: 10,
    currency: "USD",
    itemCategory: "category");

R1Emitter.sharedInstance()
    .emitDeleteFromCartWithCartID("cart_id", lineItem: lineItem, otherInfo: ["custom_key": "value"]);

iii. Custom Events

Custom events allow you to create and track specific events that are more closely aligned with your app. If planned and structured correctly, custom events can be strong indicators of user intent and behavior. Some examples include pressing the “like” button, playing a song, changing the screen mode from portrait to landscape, and zooming in or out of an image. These are all actions by the user that could be tracked with events.

To include tracking of custom events for the mobile app, the following callbacks need to be included in the application code:

// Emits a custom event without parameters
[[R1Emitter sharedInstance] emitEvent:@"Your custom event name"];

// Emits a custom event with parameters
[[R1Emitter sharedInstance] emitEvent:@"Your custom event name"
withParameters:@{@"key":@"value"}];
// Emits a custom event without parameters
R1Emitter.sharedInstance().emitEvent("Your custom event name");
// Emits a custom event with parameters
R1Emitter.sharedInstance().emitEvent("Your custom event name", withParameters: ["key": "value"]);

iv. In-App Webview Events

By setting up in-app webview events, you can track events that begin inside your native application but are completed in the embedded browser. To do so, you will have to embed some JavaScript in the page that you will want to fire the event, and you need to properly trigger the webview in your application.

JavaScript Setup

To properly fire the event from your webpage, please make the following call:

R1Connect.emitEvent(eventName,eventParameters);

For example, both of the following work. The use of parameters is optional.

R1Connect.emitEvent("Test Event");
R1Connect.emitEvent("Test Event",{"key":"value"});

Additionally, please attach the following r1connect.js file to your webpage:

var R1ConnectBridgePlatforms = {
    UNKNOWN: 0,
    IOS: 1,
    ANDROID: 2
};

window.R1Connect = {
    iosBridgeScheme: "r1connectbridge://",

    platform: R1ConnectBridgePlatforms.UNKNOWN,

    iosURLQueue: [],
    iosInProgress: false,

    buildIOSUrl: function(eventName, parameters) {
        var url = this.iosBridgeScheme + 'emit/?event=' + eventName;

        if (parameters)
            url += '&params='+JSON.stringify(parameters);

        return url;
    },

    sendURLInFrame: function(url) {
        var iframe = document.createElement("IFRAME");
        iframe.setAttribute("src", encodeURI(url));
        document.documentElement.appendChild(iframe);
        iframe.parentNode.removeChild(iframe);

        iframe = null;
    },

    emitEvent: function(eventName, parameters) {
        if (this.platform == R1ConnectBridgePlatforms.UNKNOWN) {
            if (window.R1ConnectJSInterface && window.R1ConnectJSInterface.emit)
                this.platform = R1ConnectBridgePlatforms.ANDROID;
            else
                this.platform = R1ConnectBridgePlatforms.IOS;
        }

        if (this.platform == R1ConnectBridgePlatforms.ANDROID) {
            window.R1ConnectJSInterface.emit(eventName, parameters ? JSON.stringify(parameters) : '');
            return;
        }

        var iosURL = this.buildIOSUrl(eventName, parameters);

        if (this.iosInProgress) {
            this.iosURLQueue.push(iosURL);
            return;
        }

        this.iosInProgress = true;
        this.sendURLInFrame(iosURL);
    },

    iosURLReceived: function() {
        setTimeout(function() {
            window.R1Connect._iosURLReceived();
        }, 0);
    },

    _iosURLReceived: function() {
        if (this.iosURLQueue.length == 0) {
            this.iosInProgress = false;
            return;
        }

        var iosURL = this.iosURLQueue.shift();
        this.sendURLInFrame(iosURL);    
    },
};

 

To properly handle the events in your application, you first need to import the R1WebViewHelper header file in your ViewController .m implementation file:

(Only for Objective C code)

#import "R1WebViewHelper.h"

You then should setup the UIWebView delegate from the Interface Builder or from code:

- (void) viewDidLoad {
    [super viewDidLoad];

    self.webView.delegate = self;

    // Your code here
}
override func viewDidLoad() {
    super.viewDidLoad();

    webView?.delegate = self;

    // Your code here
}

Finally, add the delegate method implementation:

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { if (![R1WebViewHelper webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]) return NO; // Your code here return YES; }
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
   if R1WebViewHelper.webView(webView, shouldStartLoadWithRequest: request, navigationType: navigationType) {
      return false;
   }

   // Your code here
   return true;
}

v. Best Practices

Event Naming Convention

One common mistake is to parametrize event names (with user data for example). Event names should be hard-coded values that you use to segement data on a specific category of event.

Example: "ProfileViewing"

Avoid: "Profile Viewing - Lady Gaga's profile"

As you may have thousands of user profiles in your database, it is preferable to keep the event name high level ("ProfileViewing") so you can run interesting anaytics on it. High level events help answer questions like "how many profiles does a user visit every day on average?"

Parameter Variance

Another common mistake is to add parameters to the event that have too many possible values. To follow up on the previous example, one may decide to add the number of profile followers as an event parameter:

[[R1Emitter sharedInstance] emitEvent:@"ProfileViewing"
withParameters:@{@"profileFollowers":profileFollowers}];
R1Emitter.sharedInstance()
    .emitEvent("ProfileViewing", withParameters: ["profileFollowers": profileFollowers]);

Again, the problem here is that each profile may have any number of followers. This will fragment your data too much to extract any valuable information.

A good strategy would be to define relevant buckets for high variance parameters. In this case, it might be more relevant to separate traffic on the profiles with a lot of followers from traffic on profiles with very few followers. You could define 3 categories:

  • "VERY_INFLUENTIAL" for profiles > 100,000
  • "INFLUENTIAL" for profile > 10,000 and <= 100,000
  • "NON_INFLUENTIAL" for profile <= 10,000

A proper event could be

[[R1Emitter sharedInstance] emitEvent:@"ProfileViewing"
withParameters:@{@"profileFollowersBucket":@"VERY_INFLUENTIAL"}];
R1Emitter.sharedInstance()
    .emitEvent("ProfileViewing", withParameters: ["profileFollowersBucket": "VERY_INFLUENTIAL"]);

This will enable you to create more insightful reports.

 

Have more questions? Submit a request

0 Comments

Article is closed for comments.
Powered by Zendesk