Handling New Installs using the Mobile Growth API on iOS

Overview

With the Yozio Mobile Growth API you can attribute new installs and pass new install data to your app. This enables you to provide your users with highly personalized onboarding (e.g. deferred deep linking, auto-login, referrals, etc). Since Yozio offers 100% accurate options for both iOS and Android and gives you our exact confidence calculation with each transaction you can personalize with confidence.

Install Attribution on iOS

The first part of handling users who do not have the app installed is attributing installs to a previous click on a Yozio SuperLink. This is the most important step, as the tracking and data passing cannot occur without a correct attribution. We tie installs to clicks by using a unique identifier for the SuperLink: the short URL (e.g. "a.b.c" is the short URL for “r.rivendell.com/a.b.c”), so that you can track on our Yozio Dashboard the activity on your SuperLinks and the campaigns that they represent.

The iOS App Store does not allow for direct data passing from a click event to the app install. A subtle consequence of this is that we have to be creative in how we attribute a new app install to the past click event that triggered it. Yozio provides many methods for attributing installs to clicks. They fall into two categories:

  • 100% accurate match
  • Fingerprint match

Yozio recommends you deploy all methods of attribution. Then, by default, when we are not able to make a 100% match, we will automatically fallback to fingerprint matching. Make sure you’ve implemented the attribution methods correctly, however, as you’ll run into errors if you’ve missed any steps.

Yozio Fingerprint Matching API

With fingerprint matching, Yozio uses a variety of parameters that we collect when the install happens to calculate the likelihood that a particular click event was responsible for the new install later down the road.

To attribute an install made as a result of a click on a Yozio SuperLink using Fingerprint matching, make an HTTP POST to the endpoint https://api.yozio.com/v2.0/?method=install.attribution.metadata.get in the didFinishLaunchingWithOptions method of your AppDelegate. Additionally you’ll set the parameter "attribution_method" to “fingerprinting”. Here is a list of the required parameters needed to make a call to the Install Attribution Fingerprint Matching API:

Name Required Description Example Code
app_key Yes Log in Yozio Console, click "SDK" on the left sidebar and App Key and Secret Key is available at that page. 99e90a1a-f07a-42db-926d-0be333b726c6
secret_key Yes 3fc0caee-8bef-4ea0-a89e-919b5168a9e6
attribution_method Yes The method used to attribute the install back to the click. At this moment, only fingerprint matching is supported. All available values will be listed in the “Example” column. fingerprinting
os Yes Operating system. All available value will be listed in the “Example” column. ios
os_version Yes The current iOS Version on your device 9.2 [[UIDevice currentDevice] systemVersion]
hardware Yes Type of iOS Device. The accepted values are listed in the "Example" column iPhone, iPad [[UIDevice currentDevice] model]
screen_width Yes Screen Width of Device. 320
CGRect screenRect = [[UIScreen mainScreen] bounds];
NSNumber *screenWidth = [NSNumber numberWithDouble: screenRect.size.width];
NSNumber *screenHeight = [NSNumber numberWithDouble: screenRect.size.height];
NSNumber *screen_scale = [NSNumber numberWithDouble: [[UIScreen mainScreen] scale]];
screen_height Yes Screen Height of Device. 568
screen_scale Yes Screen Scale set on Device. 2

Additionally, there are optional parameters that may not be required, but will greatly improve the accuracy of the fingerprint match. Check out our API reference doc for a list of all the possible optional parameters.

On a successful call to this API, the response will contain whether or not a match was found, and if a match was found, the metadata associated with the Yozio SuperLink click event.

This call will also track the install if a match is found. There is no additional action required to track your install once a match is found; you can expect the Yozio Dashboard to reflect the new install event.

Here is a sample HTTP request containing the required parameters on iOS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [YozioApi getInstallMetadataByFingerprintingApiWithMetadataCallback:^(NSDictionary *metaData) {

    //Persist MetaData locally so that it can be accessed by other views
    [[NSUserDefaults standardUserDefaults] setObject:metaData forKey:@"newInstallMetaData"];
    [[NSUserDefaults standardUserDefaults] setObject:metaData[@"__ysurl"] forKey:@"shortURL"];
    NSLog(@"Saving meta data with data: %@", [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"metaData"]);

+ (void *) getInstallMetadataByFingerprintingApiWithMetadataCallback: (void (^)(NSDictionary *))callback
{    
    NSMutableDictionary *params = [NSMutableDictionary
                                   dictionaryWithObjectsAndKeys:
                                   YOZIO_APP_KEY, @"app_key", YOZIO_SECRET_KEY, @"secret_key",
                                   nil];

    //Util function to add system params needed for the fingerprint matching API
    [Utils addSytemParamstoDictionary: params];

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    NSLog(@"%@", [NSString stringWithFormat: @"%@?method=fingerprint.matching", YOZIO_API_BASE_URL]);

    manager.responseSerializer.acceptableContentTypes = [manager.responseSerializer.acceptableContentTypes setByAddingObject:@"text/html"];
    [manager.requestSerializer setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [manager POST:[NSString stringWithFormat: @"%@?method=fingerprint.matching", YOZIO_API_BASE_URL] parameters:params progress:nil success:^(NSURLSessionTask *task, id responseObject) {

        NSLog(@"response:%@", responseObject);
        NSDictionary *response = (NSDictionary * ) responseObject;
        NSLog(@"Response body: %@", response[@"body"]);
        NSDictionary *metaData = response[@"body"][@"metadata"];
        NSLog(@"MetaData :%@", metaData);
        callback(metaData);

    } failure:^(NSURLSessionTask *operation, NSError *error) {

        NSLog(@"operation.responseString: %@", operation);
        callback(nil);

    }];
    return nil;

}

Here is a sample of the JSON response made on a successful call to the Install Attribution API where a match has been found:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
    "status": "ok",
    "body": {
        "short_url": "tb.c.c",
        "probability": 0.7400948543818, // range [0,1]
        "timestamp": 1453404473338
        "metadata": { 
            "promo-code": "XXYYZZ",
            "referrer": "ZZYYXX",
        }
    }
}

For example JSON responses for when a match has not been found, or when the call is unsuccessful (due to missing parameters, or any other type of error), check out the API reference doc.

Yozio iOS 100% Matching API (for iOS 9 users only)

One of the weaknesses of fingerprint matching is that ultimately, we cannot always be 100% sure that a particular install event is indeed a result of a particular click event. In order to address this, we’ve provided an option that will leverage the power of SFSafariViewController, introduced in iOS 9, to guarantee when an install event is tied to a click event earlier.

In order to implement 100% Matching using SFSafariViewController API,

  • Add the SafariServices.framework to your project (Build Phase -> Link Binary With Libraries -> +).
  • Enable deep linking and enter your custom scheme URL. You can either do this by editing individual SuperLinks or editing the default redirect settings.

To attribute an install made as a result of a click on a Yozio SuperLink using 100% matching, you’ll make an HTTP POST to the endpoint https://api.yozio.com/v2.0/?method=safari.services.matching in the didFinishLaunchingWithOptions method of your AppDelegate. Here is a list of the required parameters needed to make a call to the Install Attribution 100% Matching API:

Name Required Description Example Code
app_key Yes Log in Yozio Console, click "SDK" on the left sidebar and App Key and Secret Key is available at that page. 99e90a1a-f07a-42db-926d-0be333b726c6
app_key
secret_key Yes 3fc0caee-8bef-4ea0-a89e-919b5168a9e6
secret_key
hardware Yes Type of iOS Device. The accepted values are listed in the “Example” column iPhone, iPad, iPod
[[UIDevice currentDevice] model]

On a successful call to this API, the response will contain whether or not a match was found, and if a match was found, the metadata associated with the Yozio SuperLink click event.

This call will also track the install if a match is found. There is no additional action required to track your install once a match is found; you can expect the Yozio Dashboard to reflect the new install event.

in order to achieve 100% matching using SFSafariViewController, you will need to create a SafariViewController object within your app. We drop a cookie into the user’s Safari browser, and by using a SafariViewController object, the API call made will have access to this cookie. Here are some functions used in our implementation of a SafariViewController wrapper/helper class that handles this interaction:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
+ (void) getInstallMetadataBySafariServices: (void (^)(NSDictionary *))callback
{
    [self initYozioDirectory];
    NSString *yozioDirectory = [Utils getYozioDirectory];
    NSString *installTrackedPath = [yozioDirectory stringByAppendingPathComponent:YOZIO_INSTALL_TRACKED_PATH];

    // Create File to track if its a new Install install

    if (![[NSFileManager defaultManager] fileExistsAtPath:installTrackedPath]) {

        [[NSFileManager defaultManager] createFileAtPath:installTrackedPath contents:nil attributes:nil];   
        NSLog(@"created InstallTracked file %@", installTrackedPath);
        [YozioSFSafariViewController sendMatchingRequest:callback];

    } else {
        NSLog(@"Install already been tracked");
    }
}

+ (void)sendMatchingRequest: (void(^)(NSDictionary *))callback
{
    if (isSent) {
        NSLog(@"YozioSFSafariViewController - Request already been sent.");
        return;
    }
    isSent = TRUE;

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReceiveNotification:) name:YOZIO_SF_SAFARI_VIEW_CONTROLLER_NOTIFICATION object:nil];

    _newInstallMetadataCallback = callback;
    double delayInSeconds = 0.1;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

        topWindow = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] applicationFrame]];

        NSString *url = [self buildInstallApiUrl];

        safariViewController = [[NSClassFromString(@"SFSafariViewController") alloc] initWithURL:[NSURL URLWithString:url]];
        [topWindow setUserInteractionEnabled:NO];
        [topWindow setRootViewController:safariViewController];
        [topWindow setWindowLevel: UIWindowLevelNormal-1];
        [topWindow setHidden: NO];
    });

    NSLog(@"request timeout: %d", YOZIO_REQUEST_TIMEOUT);

    dispatch_time_t dispatchTime = dispatch_time(DISPATCH_TIME_NOW, YOZIO_REQUEST_TIMEOUT * NSEC_PER_SEC);

    dispatch_after(dispatchTime, dispatch_get_main_queue(), ^(void){
        [safariViewController dismissViewControllerAnimated:false completion:nil];
        topWindow = nil;
        isTimeout = TRUE;
        if (!isNotifictionReceived) {
            NSLog(@"notification not received");
            NSDictionary *response = [[NSDictionary alloc] init];
            [YozioSFSafariViewController newInstallCallback: response];
        }
    });
}

The response will contain the following:

  • short_url
  • metadata
    • Key value pairs associated with your Yozio SuperLink. This will include metadata attached via any of the supported methods of attaching metadata (ie static, dynamic, Sublink API).

Verifying Install Attribution on iOS

The easiest way to test if the install attribution is working correctly is to make an HTTP request to the API directly using an HTTP request tool, such as curl.

First, click on the Yozio SuperLink that you’d like to test using any iOS device. Make sure that you collect the system information needed in the API call.

These are the typical responses you should expect to see, and when you can expect to see them:

API call succeeds and finds a matched click on a Yozio SuperLink
{
    "status": "ok",
    "body": {
        "short_url": "tb.c.c",
        "probability": 0.7400948543818, // range [0,1]
        "timestamp": 1453404473338
        "metadata": { 
            "promo-code": "XXYYZZ",
            "referrer": "ZZYYXX",
        }
    }
}
API call succeeds and does not find a matched SuperLink click
{
    "status": "ok",
    "body": {
        "probability": -1,
        "timestamp": 1453404473338
    }
}
API call fails
{
    "status": "error",
    "message": "[error message]" // format of error msg may change
}