iOS – Using the SDK

The following document describes the common use cases for the Kochava SDK after integration is complete. For information on integrating the SDK or configuring and starting the measurement client, refer to our iOS SDK Integration support documentation.


Estimated Time to Complete
5 Minutes

After integrating the SDK or creating a new App GUID, we suggest performing these tests to ensure the SDK has been integrated successfully and is functioning as expected within your app.

 

Validate the Install:

The SDK will send an install for the app once, after a fresh install. This test ensures the SDK was configured properly and successfully sent the install to Kochava.

  1. Double check the SDK configuration in code, ensuring the correct App GUID.
  2. Run the app for approximately 30 seconds, which will allow more than enough time for the SDK to start and send an install to Kochava under typical conditions.
  3. Wait a minute or two and visit the Install Feed Validation page for your app within the Kochava dashboard, under Apps & Assets > Install Feed Validation. Within that page, look for the Integration Success! message which indicates integration was successful and that Kochava did receive an install from the SDK. At this point you have confirmed a successful SDK integration and can move ahead to Validate Post Install Events below.
  4. If instead you see a Integration Not Complete! message, wait a few more minutes and refresh the page. After refreshing, if the Integration Not Complete! message persists, double check the following, then repeat this test:
    • Correct App GUID is used within SDK code configuration.
    • Ensure the SDK configuration and startup code is being reached.
    • Ensure the network connection from the test device is not limited behind a firewall or otherwise.

Validate Event Measurement:

If you are measuring user events, you can use this test to ensure the SDK was configured properly and is successfully sending these events to Kochava.

  1. Double check the SDK configuration in code, ensuring the correct App GUID.
  2. Double check your event measurement code and ensure it is reachable.
  3. Launch the app and perform necessary actions within the app to trigger the event(s) you wish to test. After performing these actions, wait 60 seconds to allow more than enough time for the SDK to send these events.
  4. Wait a minute or two and visit the Event Manager page for your app within the Kochava dashboard, under Apps & Assets > Event Manager. Within that page, ensure the tested event names are displayed here, in which case you have confirmed the SDK is successfully measuring these events.
  5. If your event names are not displayed here after waiting a few minutes, double check the following, then repeat this test:
    • Correct App GUID is used within SDK code configuration.
    • Ensure the SDK configuration and startup code is being reached prior to any event code.
    • Ensure the SDK event code is being reached.
    • Ensure the network connection from the test device is not limited behind a firewall or otherwise.

10 Minutes
Estimated Time to Complete
10 Minutes

 

SDK PLATFORM NOTE: This feature is applicable to iOS platforms only.

 

No special code is needed to support SKAdNetwork, beyond measuring your existing events which are eligible for conversion.

 

After setting up SKAdNetwork in your Kochava dashboard, the SDK will automatically:

  1. Call Apple’s SKAdNetwork registration at the first opportunity following launch.
  2. When an eligible conversion event is triggered on iOS 14, the SDK will calculate the appropriate conversion value based on the event’s properties and automatically call Apple’s SKAdNetwork conversion update.

Generating SKAdNetwork Postbacks:

While the SDK automatically makes the necessary Apple API calls for you, a SKAdNetwork postback will only be generated if requirements are met for both the source app and advertised app. The advertised app must have been reviewed and available for download in the App Store, while the source app (where the ad is displayed) can be one that you are currently developing and run from Xcode or through TestFlight. Be sure to use the correct SKStoreProductParameterAdNetworkSourceAppStoreIdentifier per your case.

For testing purposes, you can cut down on the 24 hour postback wait by using the “SKAdNetwork Profile” from the Apple developer console here: https://developer.apple.com/download/more/ (search for “skad”).


Subscribe to SDK Callbacks:

Optionally, you may set callbacks which will be called by the SDK when it calls Apple’s registerAppForAdNetworkAttribution() and updatePostbackConversionValue() APIs. Subscribing to these notifications is NOT required, and is necessary only if you wish to do something with the latest conversion value or understand the timing of these calls.

 

30 Minutes
Estimated Time to Complete
30 Minutes

 

SDK PLATFORM NOTE: This feature is applicable to iOS platforms only.

 

As of iOS 14, IDFA collection is gated behind Apple’s new AppTrackingTransparency (ATT) permission-based authorization. This means that when an app is running on iOS 14, the IDFA is not available for collection until after the user grants permission, similar to any other iOS permission-based collection. However, Apple is delaying enforcement of ATT, which is discussed below.


Enforcing ATT for IDFA Collection

The SDK makes this very simple for you. All you need to do is tell the SDK you want to enable ATT enforcement during configuration.

As a tracking requirement by Apple, you must include in your info.plist the key NSUserTrackingUsageDescription and a string value explaining why you are requesting authorization to track. This text will be included in the prompt displayed by the operating system when tracking authorization is requested.


Configure the SDK

During SDK configuration, tell the SDK you wish to enable ATT enforcement. By default, the user will be prompted for tracking authorization one time, upon launch, and the SDK will allow up to 30 seconds for the user to answer the tracking authorization prompt. You may adjust this behavior if you wish.

 

Example Enabling ATT with default settings (recommended):

Swift:

let measurement = Measurement.shared
measurement.appTrackingTransparency.enabledBool = true
measurement.start(appGUIDString: "_YOUR_APP_GUID_")

 

Example Allow more than the default 30 seconds for the user to respond:

Swift:

let measurement = Measurement.shared
measurement.appTrackingTransparency.enabledBool = true
measurement.appTrackingTransparency.authorizationStatusWaitTimeInterval = 90.0
measurement.start(appGUIDString: "_YOUR_APP_GUID_")

 

At this point you are done. The user will be prompted for tracking authorization one time, during the first launch of the app, and the IDFA will be gathered if authorization is granted.

For purposes of testing, you will need to uninstall and reinstall the app each time you wish for the tracking prompt to appear, as Apple will only allow this to be displayed once.

Optionally, if you wish to prompt the user for tracking authorization at a specific moment or you do not want the SDK to trigger the prompt, continue reading below.


Custom Prompt Timing (Optional)

Follow these steps only if you wish for the tracking authorization prompt to be displayed at a time other than when the app is first launched or you do not want the SDK to trigger the prompt.

In order to accomplish this, first configure the SDK so that it does not automatically request authorization and allows enough time for the user to reach the point where tracking authorization will be requested at the moment of your choosing. In this example, we are allowing up to 120 seconds for the user to provide an answer to the tracking authorization request.

 

Example Configure the SDK:

Swift:

// a. This will instruct the SDK not to request authorization results yet, unless we've already prompted the user on a previous launch:
let measurement = Measurement.shared
measurement.appTrackingTransparency.enabledBool = true
measurement.appTrackingTransparency.autoRequestTrackingAuthorizationBool = UserDefaults.standard.value(forKey: “com.myOrg.didATTPrompt”) as? Bool
    ?? false  // [a]
measurement.appTrackingTransparency.authorizationStatusWaitTimeInterval = 120.0
measurement.start(appGUIDString: "_YOUR_APP_GUID_")

 

Secondly, add code which requests the tracking authorization at the time of your choosing and then notifies the SDK when the authorization request completes. It is your responsibility to ensure your tracking authorization request code is reached. If it is not, the timeout will be reached and the SDK will proceed without collecting the IDFA.

NOTE: Regardless of how many times you request tracking authorization, the user is only prompted once. This means you can repeatedly request tracking authorization at a specific moment per app launch and the user will only be prompted once, the first time the code is reached.

 

Example Request authorization and notify the SDK upon completion:

Swift:

// ATTrackingManager
// ⓘ Request tracking authorization at a curated moment, and then:
// a. Notify the Kochava SDK that it may proceed with tracking authorization status.
// b. Persist a flag indicating the user has answered the ATT prompt at least once.
ATTrackingManager.requestTrackingAuthorization
{
    authorizationStatus in

    Measurement.shared.appTrackingTransparency.autoRequestTrackingAuthorizationBool = true  // [a]
    UserDefaults.standard.setValue(true, forKey: “com.myOrg.didATTPrompt”)  // [b]
}

App Store Submission Guidance and Best Practices

Developing Section: The information contained within this section is subject to change as Apple shares more information and their requirements evolve.

 

If you have added the NSUserTrackingUsageDescription entry to your info.plist and/or are referencing the ATT framework, Apple expects to visibly see the ATT prompt during the review process of your submission. At the time of this writing, the App Store submission guidelines do not state this requirement, but Apple has cited this as cause for rejection.

If the ATT prompt is not automatically triggered upon launch, we suggest that you include instructions for the reviewer detailing the steps they must take to trigger the ATT prompt. If you’ve forcibly disabled the ATT prompt whether through our UI or otherwise, you must add a review note indicating that you are not invoking the ATT consent prompt until the release of iOS 14.5.

NOTE: Apple may reject apps which attempt to preempt the ATT prompt with a soft prompt of any kind. We suggest that you avoid this approach and allow the ATT prompt to be triggered immediately upon launch.

15 Minutes
Estimated Time to Complete
15 Minutes

 

The standard SDK is used within your app clip app (no special variant of the SDK is needed for an app clip).

In order to properly support user and attribution flow between the app clip and full app, the following steps must be taken.

 

Create Two App GUIDs:

The app clip and full app should not use the same App GUID. For the app clip, create or use a Kochava app of platform type iOS – App Clip. For the full app, create or use a Kochava app of platform type iOS.

 

Configure the SDK:

You will need to provide an app group identifier string which facilitates shared storage between the app clip and full app. To accomplish this, in Xcode under the project Signing & Capabilities, add a new capability for App Groups if you do not have one already using the plus button. For the new identifier, start with your app’s bundle identifier and then prefix it with group. and suffix it with .kochava. Provide this identifier to the SDK prior to starting the SDK. This identifier must be the same between the app clip and full app.

 

Example Provide a Shared Storage Container Before Starting the SDK:

Swift:

// AppGroups
// ⓘ Set the shared generalAppGroupIdentifier. This facilitates shared storage between the app clip and full app.
AppGroups.shared.generalAppGroupIdentifier = "group.com.kochava.host.kochava"
 
// Measurement
// ⓘ Start. Pass the Kochava app guid for either the app clip or full app.
let measurement = Measurement.shared
measurement.start(appGUIDString: "_YOUR_APP_GUID_")

 

NOTE: The SDK detects your app clip by looking for the bundle identifier’s default .clip suffix. If your app clip does not use the default .clip suffix, you must also set KVAAppGroups.shared.appClipBool to true immediately following setting KVAAppGroups.shared.deviceAppGroupIdentifierString within the app clip code (not the full app).

 

Process the Deeplink:

When the instant app is launched, pass the invocation URL to the SDK’s Process Deeplink API as soon as possible, just as you would any other deeplink. See the topic Enhanced Deeplinking for complete instructions on how to use the Process Deeplink API.

By taking these steps, all functionality related to attribution, analytics, and measurement will be properly implemented between the app clip and full app.

Estimated Time to Complete
15 Minutes

Examples include in-app purchases, level completions, or other noteworthy user activity you wish to measure. Events can be instrumented either by using the standard format provided by the SDK or using your own custom event name and data.

 

BEST PRACTICES: Use a standard event type whenever possible.

 

Standard Events:

Standard events are built by first selecting a standard event type and then setting any applicable standard parameters you wish to include with the event. For example, you might choose a Purchase standard event type and set values for the Price and Name parameters. There are a variety of standard event types to choose from and dozens of standard parameters available. When creating a standard event, you only need to set values for the parameters you wish to measure. A maximum of 16 parameters can be set.

  1. Create an event object using the desired standard event type.
  2. Set the desired parameter value(s) within the event object.
  3. Send the event.

 

Example (Standard Event with Standard Parameters)

  • let event = Event(type: .purchase)
    event.nameString = "Gold Token"
    event.priceDoubleNumber = 0.99
    event.send()
  • KVAEvent *event = [[KVAEvent alloc] initWithType:KVAEventType.purchase];
    event.nameString = @"Gold Token";
    event.priceDoubleNumber = @(0.99);
    [event send];

 

Custom event parameters (which can also be serialized JSON) may also be set within a standard event.

 

Example (Standard Event with Standard and Custom Parameters)

  • let event = Event(type: .levelComplete)
    event.nameString = "The Deep Dark Forest"
    event.infoDictionary =
    [
      "attempts": 3,
      "score": 12000
    ]
    event.send()
  • KVAEvent *event = [[KVAEvent alloc] initWithType:KVAEventType.levelComplete];
    event.nameString = @"The Deep Dark Forest";
    event.infoDictionary = @{
      @"attempts": @(3),
      @"score": @(12000)
    };
    [event send];

 

If you wish to use a custom event type with standard parameters, use a custom event name string within your event constructor in place of a standard event type.

 

For a detailed list of standard event types and parameters, see: Post Install Event Examples


Custom Events:

For scenarios where the standard event types and standard parameters do not meet your needs, custom events can be used. To instrument a custom event, pass the event’s name and data (which can also be serialized JSON) to the method to send the custom event.

 

Example (Custom Event with Custom Parameters)

  • let event = Event(customWithEventName: "Enemy Defeated")
    event.infoDictionary =
    [
       "enemy": "The Angry Ogre",
       "reward": "Gold Token"
    ]
    event.send()
  • KVAEvent *event = [[KVAEvent alloc] initCustomWithEventName:@"Enemy Defeated"];
    event.infoDictionary = @{
      @"enemy": @"The Angry Ogre",
      @"reward": @"Gold Token"
    };
    [event send];
  • let event = KVAEvent(customEventWithNameString: "Enemy Defeated")
    event.infoDictionary =
    [
       "enemy": "The Angry Ogre",
       "reward": "Gold Token"
    ]
    event.send()

 

Example (Send a Custom Event with Only a Name, no Event Data)

  • Event.sendCustom(eventName: "Player Defeated")
  • [KVAEvent sendCustomWithEventName:@"Player Defeated"];
  • KVAEvent.sendCustom(withNameString: “Player Defeated”)

 

Example (Send a Custom Event with Event Data)

  • Event.sendCustom(
        eventName: "Player Defeated", 
        infoString: "Angry Ogre"
    )
  • [KVAEvent sendCustomWithEventName:@"Player Defeated" infoString:@"Angry Ogre"];
  • KVAEvent.sendCustom(withNameString: "Player Defeated", infoString: "Angry Ogre")

 

Example (Send a Custom Event with Additional Data)

  • Event.sendCustom(
        eventName: "Player Defeated", 
        infoDictionary: 
        [
            "enemy": "Angry Ogre"
        ]
    )
  • [KVAEvent sendCustomWithEventName:@"Player Defeated" infoDictionary:@{@"enemy": @"Angry Ogre"}];
  • KVAEvent.sendCustom(withNameString: "Player Defeated", infoDictionary: ["enemy": "Angry Ogre"])

 

NOTE: No custom event name pre-registration is required. However, a maximum of 100 unique event names can be measured within the Kochava dashboard (including any standard event types also used), so keep this in mind as you create new custom event names.


Estimated Time to Complete
10 Minutes

In-app purchases and subscription can be easily measured and attributed by creating a purchase event. To accomplish this, simply create an event of type Purchase and include the total amount of revenue as the price value within the event data parameters.

 

BEST PRACTICES: Include the name parameter, so that you can easily identify the SKU from the analytics side. It is also highly recommended to set a currency.

App Store receipts can also be optionally included with your purchase event to be validated server-side by Kochava through the App Store. If desired, include the receipt in your event using the standard parameter appStoreReceiptBase64EncodedString.

 

Example (Standard Purchase Event):

  • let event = Event(type: .purchase)
    event.priceDecimalNumber = 4.99
    event.currencyString = "usd"
    event.nameString = "Loot Box"
    event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString
    event.send()
  • KVAEvent *event = [[KVAEvent alloc] initWithType:KVAEventType.purchase];
    event.priceDoubleNumber = @(4.99);
    event.currencyString = @"usd";
    event.nameString = @"Loot Box";
    event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString;
    [event send];

 

Example (Custom Purchase Event with Additional Data):

  • Event.sendCustom(eventName: "Purchase", infoDictionary: [
        "price": 4.99,
        "name": "Loot Box"
    ])
  • [KVAEvent sendCustomWithEventName:@"Purchase" infoDictionary:@{
      @"price": @(4.99),
      @"name": @"Loot Box"
    }];
  • KVAEvent.sendCustom(withNameString: "Purchase", infoDictionary: [
        "price": 4.99,
        "name": "Loot Box"
    ])


10 Minutes
Estimated Time to Complete
10 Minutes

In order to effectively measure user subscriptions and free trials, an event should be instrumented at the time of the subscription purchase or start of the free trial along with an accompanying identity link.

When a subscription or free trial begins, first set an identity link for this subscriber and then instrument a standard Subscription or Trial event populated with the following values:

  • Price
  • Currency
  • Product Name
  • User or Subscriber ID (hash suggested)
  • Receipt (if available)

 

BEST PRACTICES: When registering an identity link, register the identity link prior to sending the event, otherwise the event will not be properly associated with the user’s identity.

 

Example (Identity Link with Subscription):

  • // IdentityLink
    // ⓘ Register. First this user is linked with this install.
    IdentityLink.register(
        name: "Subscriber ID",
        identifier: "ABCDEF123456789"
    )
     
    // event     
    // ⓘ Send. Next, the subscription is sent.
    let event = Event(type: .subscribe)
    event.priceDecimalNumber = 9.99
    event.currencyString = "usd"
    event.nameString = "Monthly Subscription"
    event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString
    event.send()
  • // KVAIdentityLink
    // ⓘ Register. First this user is linked with this install.
    [KVAIdentityLink registerWithName:@"Subscriber ID" identifier:@"ABCDEF123456789"];
       
    // event     
    // ⓘ Send. Next, the subscription is sent.
    KVAEvent *event = [[KVAEvent alloc] initWithType:KVAEventType.subscribe];
    event.priceDecimalNumber = (NSDecimalNumber *)[NSDecimalNumber numberWithDouble:9.99];
    event.currencyString = @"usd";
    event.nameString = @"Monthly Subscription";
    event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString;
    [event send];
  • // identityLink
    // ⓘ Register. First this user is linked with this install.
    KVATracker.shared.identityLink.register(
        withNameString: "Subscriber ID",
        identifierString: "ABCDEF123456789"
    )
     
    // event     
    // ⓘ Send. Next, the subscription is sent.
    let event = KVAEvent(type: .subscribe)
    event.priceDecimalNumber = 9.99
    event.currencyString = "usd"
    event.nameString = "Monthly Subscription"
    event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString
    event.send()

 

A free trial is handled in a similar way, although the price should be set to 0 and the event type should indicate Trial rather than Subscription. The product name should remain the same, as the event type indicates whether this was free trial or subscription.

 

Example (Identity Link with Free Trial):

  • // IdentityLink
    // ⓘ Register. First this user is linked with this install.
    IdentityLink.register(
        name: "Subscriber ID", 
        identifier: "ABCDEF123456789"
    )
           
    // event     
    // ⓘ Send. Next, the subscription is sent.
    let event = Event(type: .startTrial)
    event.priceDecimalNumber = 0.00
    event.currencyString = "usd"
    event.nameString = "Monthly Subscription"
    event.userIdString = "ABCDEF123456789"
    event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString
    event.send()
  • // KVAIdentityLink
    // ⓘ Register. First this user is linked with this install.
    [KVAIdentityLink registerWithName:@"Subscriber ID" identifier:@"ABCDEF123456789"];
       
    // event     
    // ⓘ Send. Next, the subscription is sent.
    KVAEvent *event = [[KVAEvent alloc] initWithType:KVAEventType.startTrial];
    event.priceDecimalNumber = (NSDecimalNumber *)[NSDecimalNumber numberWithDouble:0.00];
    event.currencyString = @"usd";
    event.nameString = @"Monthly Subscription";
    event.userIdString = @"ABCDEF123456789";
    event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString;
    [event send];
  • // identityLink
    // ⓘ Register. First this user is linked with this install.
    KVATracker.shared.identityLink.register(
        withNameString: "Subscriber ID", 
        identifierString: "ABCDEF123456789"
    )
           
    // event     
    // ⓘ Send. Next, the subscription is sent.
    let event = KVAEvent(type: .startTrial)
    event.priceDecimalNumber = 0.00
    event.currencyString = "usd"
    event.nameString = "Monthly Subscription"
    event.userIdString = "ABCDEF123456789"
    event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString
    event.send()

 


Estimated Time to Complete
5 Minutes

Measuring deeplinks is accomplished similar to any other type of event. In order to measure a deeplink event, create a standard event of type Deeplink and set the URI parameter along with any other relevant parameters to the values provided when the deeplink occurred.

 

Example (Standard Deeplink Event):

  • let event = Event(type: .deeplink)
    event.uriString = "some_deeplink_uri"
    event.send()
  • KVAEvent *event = [[KVAEvent alloc] initWithType:KVAEventType.deeplink];
    event.uriString = @"some_deeplink_uri";
    [event send];


10 Minutes
Estimated Time to Complete
10 Minutes

 

Enhanced Deeplinking facilitates the routing of users who are deeplinked into the app, whether through a standard deeplink or deferred deeplink. Re-engagement attribution is also supported for those users.

 

Use of this feature consists of these steps:

  1. Complete the steps within Adding Universal Link or App Link Support documentation.
  2. Acquire a deeplink and Pass it to the SDK.
  3. Wait for the callback and Route the user.

 

Terminology:

Standard Deeplink: A deeplink into an app which is already installed. The app opens (or is already open) and the deeplink is immediately available.

Deferred Deeplink: A deeplink into an app which is not yet installed. In this case, a deeplink-enabled Kochava SmartLink is clicked, but the user must first install and then launch the app. The deeplink would have been lost during the installation, but Kochava is able to provide you with the original deeplink.

 

Acquire the Deeplink and Pass it to the SDK:

As a first step, acquire any incoming deeplink on launch or at runtime. If no deeplink exists on launch, pass in an empty string to indicate a deferred deeplink may be present. Remember, you do not need to check whether the deeplink is from Kochava or not; the SDK will do this for you.

A timeout, in seconds, may also be specified with the deeplink, which indicates the maximum amount of time to allow the SDK to attempt to process the deeplink. Typical standard deeplink processing should complete in less than 1 second, but if network connectivity is poor the timeout could be reached. Deferred deeplinking typically completes in 3-7 seconds, but could take longer depending on network connection and attribution processing time. We suggest setting a timeout of at least 10 seconds for standard deeplinks and 15 seconds for deferred deeplinks.

NOTE: Ensure you have started the measurement client before using this feature.

 

Example (Acquire the Deeplink)

  • func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool
    {
        if let url = userActivity.webpageURL
        {
            Deeplink.process(url: url)
            {
                deeplink in
    
                // handle deeplink
            }
        }
                
        return true
    }
  • - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler
    {
        NSURL *url = userActivity.webpageURL;
        if (url != nil)
        {
            [KVADeeplink processWithURL:url completionHandler:^(KVADeeplink * _Nonnull deeplink)
            {
                // handle deeplink
            }];
        }
        
        return YES;
    }
  • func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool
    {
        if let url = userActivity.webpageURL
        {
            KVADeeplink.process(withURL: url)
            {
                deeplink in
    
                // handle deeplink
            }
        }
                
        return true
    }

 

Example (Acquire the Deferred Deeplink)

  • func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
    {
        // ...
    
        let userActivityDictionary = launchOptions?[UIApplication.LaunchOptionsKey.userActivityDictionary] as? [AnyHashable: Any]
        if userActivityDictionary == nil
        {
            Deeplink.process(url: nil)
            {
                deeplink in
                     
                if let destinationString = deeplink.destinationString, destinationString.count > 0
                {
                    // handle deferred deeplink
                }
            }
        }
                    
        return true
    }
  • - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // ...
    
        NSDictionary *userActivityDictionary = launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
        if (userActivityDictionary == nil)
        {
            [KVADeeplink processWithURL:nil completionHandler:^(KVADeeplink * _Nonnull deeplink)
            {
                NSString *destinationString = deeplink.destinationString;
                if (destinationString.length > 0)
                {
                    // handle deferred deeplink
                }
            }];
        }
            
        return YES;
    }
  • func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
    {
        // ...
    
        let userActivityDictionary = launchOptions?[UIApplication.LaunchOptionsKey.userActivityDictionary] as? [AnyHashable: Any]
        if userActivityDictionary == nil
        {
            KVADeeplink.process(withURL: nil)
            {
                deeplink in
                     
                if let destinationString = deeplink.destinationString, destinationString.count > 0
                {
                    // handle deferred deeplink
                }
            }
        }
                    
        return true
    }

 

Example (Set a Timeout)

Optionally, you may set a timeout other than the default of 10 seconds.

KVADeeplink.process(url: nil, timeoutTimeInterval: 15.0)
{
    deeplink in

    // ...
}

Wait for the Callback and Route the User:

Once the SDK finishes processing the deeplink, a callback will fire which will include the final routing destination for this user. This destination may or may not differ from the original deeplink passed in, but it’s been validated and is now ready to be used. Please note that if the timeout is reached the callback will fire with the unchanged deeplink originally passed in.

If no deeplink was present, an empty string will be returned as the destination.

It is important to understand that if the SDK does not recognize the deeplink as a Kochava deeplink, the deeplink passed in will be returned via the callback unchanged. Because of this, you don’t need to add code to determine whether or not the deeplink is from Kochava before passing it to the SDK; simply pass all deeplinks to the SDK and wait for the callback each time.

 

Example (Wait for the Callback)

  • Deeplink.process(url: url)
    {
        deeplink in
             
        if let destinationString = deeplink.destinationString, 
               destinationString.count > 0
        {
            // deeplink exists, parse the destination as you see fit
            let urlComponents = URLComponents(string: destinationString)
                            
            // route the user to the destination accordingly
            print("urlComponents=\(String(describing: urlComponents))")
        }
        else
        {
            // no deeplink to act upon, route to a default destination or take no action
        }
    }
  • [KVADeeplink processWithURL:url completionHandler:^(KVADeeplink * _Nonnull deeplink)
        {
            NSString *destinationString = deeplink.destinationString;
            if (destinationString.length > 0)
            {
                // deeplink exists, parse the destination as you see fit
                NSURLComponents *urlComponents = [NSURLComponents componentsWithString:destinationString];
    
                // route the user to the destination accordingly
                NSLog(@"urlComponents=%@", urlComponents);
            }
            else
            {
                // no deeplink to act upon, route to a default destination or take no action
            }
        }];
  • KVADeeplink.process(withURL: url)
    {
        deeplink in
             
        if let destinationString = deeplink.destinationString, 
               destinationString.count > 0
        {
            // deeplink exists, parse the destination as you see fit
            let urlComponents = URLComponents(string: destinationString)
                            
            // route the user to the destination accordingly
            print("urlComponents=\(String(describing: urlComponents))")
        }
        else
        {
            // no deeplink to act upon, route to a default destination or take no action
        }
    }

 

About Deferred Deeplinking:

By providing no link to the deeplink process method, as described in the first example above, the SDK will automatically look for any deferred deeplink from the attribution results and surface it within the callback no differently than a standard deeplink.

This means you do not need to care about where a deeplink is coming from or whether it is deferred or standard. Just pass in the deeplink if it exists or pass in an empty string if it does not, then wait for the callback and route the user.

 

Enhanced Deeplinking vs. Attribution Retrieval:

If you choose to use this Enhanced Deeplinking functionality for deferred deeplinking, you should not also need to use the SDK’s attribution retrieval feature for deferred deeplinking, unless you want to parse the attribution results for another reason. If you do not want Enhanced Deeplinking checking for a deferred deeplink, do not pass an empty string to the SDK when no deeplink exists. Choose one method or the other, but don’t rely on both for deferred deeplinking as you could end up routing the user twice.

One important difference between these two approaches is that Attribution Retrieval has no timeout and will eventually complete, no matter how much time it takes. If, for example, the network connection is bad and attribution results are not able to be retrieved, they would eventually be retrieved once the network connection was restored or even on a future launch. By contrast, Enhanced Deeplinking is designed with user experience in mind, and will only attempt to retrieve a deferred deeplink on the first launch of the first install, and only up to the timeout you specify. If the SDK is able to detect a reinstall, any deferred deeplink from the original install will be ignored. This is because a user would not expect to be routed to a deeplink destination long after the relevant time window had expired or on a future app launch. Imagine clicking a deeplink on a Monday, and being routed on Friday; it would be a confusing experience.

In summary, if you want to parse the raw attribution results yourself, or you do not care about receiving a deferred deeplink after the relevant time window expires, you may use the Attribution Retrieval (within this page) functionality for deferred deeplinking. If you do not care to parse the raw attribution results and you want to receive a deferred deeplink only when relevant, use the Enhanced Deeplinking functionality described here.

Estimated Time to Complete
10 Minutes

Setting an Identity Link provides the opportunity to link different identities together in the form of key and value pairs. For example, you may have assigned each user of your app an internal ID which you want to connect to a user’s service identifier. Using this feature, you can send both your internal ID and their service identifier to connect them in the Kochava database.

In order to link identities, you will need to register this identity link information in the form of unique key and value pair(s) as early as possible. This can be done during the initial configuration of the measurement client if the identity link information is already known, or it can be done after starting the client.

 

BEST PRACTICES: Keep from sending Personal Identifiable Information (PII) such as email addresses or deprecated platform identifiers such as IMEI to Kochava..

 

Example (Register an Identity Link During Initial Configuration):

  • IdentityLink.register(
        name: "User ID", 
        identifier: "123456789"
    )
    IdentityLink.register(
        name: "Login", 
        identifier: "ljenkins"
    )
  • [KVAIdentityLink registerWithName:@"User ID" identifier:@"123456789"];
    [KVAIdentityLink registerWithName:@"Login" identifier:@"ljenkins"];
    
    KVAMeasurement *measurement = KVATracker.shared
    [measurement startWithAppGUIDString:@"_YOUR_APP_GUID_"]
  • let tracker = KVATracker.shared
    tracker.identityLink.register(
        withNameString: "User ID", 
        identifierString: "123456789"
    )
    tracker.identityLink.register(
        withNameString: "Login", 
        identifierString: "ljenkins"
    )
    tracker.start(withAppGUIDString: "_YOUR_APP_GUID_")

 

Example (Register an Identity Link After Starting the Measurement Client):

  • IdentityLink.register(
        name: "User ID", 
        identifier: "123456789"
    )
    IdentityLink.register(
        name: "Login", 
        identifier: "ljenkins"
    )
  • [KVAIdentityLink registerWithName:@"User ID" identifier:@"123456789"];
    [KVAIdentityLink registerWithName:@"Login" identifier:@"ljenkins"];
  • KVATracker.shared.identityLink.register(
        withNameString: "User ID", 
        identifierString: "123456789"
    )
    KVATracker.shared.identityLink.register(
        withNameString: "Login", 
        identifierString: "ljenkins"
    )


Estimated Time to Complete
15 Minutes

Install attribution results can be retrieved from Kochava servers if you wish to use these results within your app. Be aware that attribution results are always determined by Kochava servers; this feature simply provides the app with a copy of whatever the results were.

For example, you may wish to present a user with a different path if you have determined they installed the app from a certain advertising network or source.

Attribution results are fetched by the measurement client when requested and returned to the app asynchronously via a callback. This process usually takes about 3-4 seconds but can take longer depending on network latency and other factors. Once attribution results have been retrieved for the first time, they are not retrieved again and the results are persisted. From that point on they can be queried synchronously by calling the attribution data getter which always provides the persisted attribution results from the original retrieval.

NOTE: For purposes of deferred deeplinking, care should be taken to act upon the attribution results only once, as the original results will continue to be reported after the first retrieval, and are not refreshed on a per-launch basis.

 

BEST PRACTICES: Attribution retrieval does not affect attribution and should only be used if there is a clearly defined use within your app for knowing the attribution results; otherwise this causes needless network activity.

 

Example (Requesting Attribution Results):

  • let measurement = Measurement.shared
    measurement.attribution.retrieveResult
    {
        result in
            
        // ... do something with the result
    }
  • KVAMeasurement *measurement = KVAMeasurement.shared;
    [measurement.attribution retrieveResultWithClosureDidComplete:^(KVAMeasurement_Attribution_Result * _Nonnull result)
    {
        // ... do something with the result
    }];

 

Example (Using the Getter After Attribution Results Have Been Retrieved):

  • let measurement = Measurement.shared
    let result = measurement.attribution.result
    print("... do something with the result=\(result.to(context: .log) as? [AnyHashable: Any] ?? [:])")
  • KVAMeasurement *measurement = KVAMeasurement.shared;
    KVAMeasurement_Attribution_Result *result = measurement.attribution.result;
    if (result != nil) {
       NSLog(@"... do something with the result=%@", result);
    }
  • let tracker = KVATracker.shared
    let result = tracker.attribution.result
    print("... do something with the result=\(result.kva_asForContextObject(withContext: .log) as! [AnyHashable: Any])")

Once you have the attribution results, you will need to parse and handle them in some meaningful way. A variety of data exists within this json object and you will need to determine which data is meaningful for your purposes. For an overview of the attribution dictionary contents, see: Attribution Response Examples.

NOTE: If you wish to send attribution results to your own server, this should be done directly through Kochava’s postback system, rather than retrieving attribution in the app and then sending the results to your own server.


Estimated Time to Complete
1 Minute

If at any time after starting the measurement client you would like to get the universally unique identifier assigned to this install by Kochava, this identifier can be obtained by calling the retrieve function.

 

Example (Getting the Kochava Install ID):

  • let measurement = Measurement.shared
    measurement.installIdentifier.retrieve()
    {
        installIdentifier in
    
        print("installIdentifier=\(installIdentifier)")
    }
  • KVAMeasurement *measurement = KVAMeasurement.shared;
    [measurement.installIdentifier retrieveWithClosure_didComplete:^(NSString * _Nullable installIdentifier)
    {
        NSLog(@"... do something with the installIdentifier=%@", installIdentifier);
    }];


Estimated Time to Complete
1 Minute

If you wish to limit ad tracking at the application level, with respect to Kochava conversions, you can set this value during or after configuration. By default the limit ad tracking state is not enabled (false).

For example, you might provide an option for a user to indicate whether or not they wish to allow this app to use their advertising identifier for tracking purposes. If they do not wish to be tracked, this value would be set to true.

 

Example (Enable App Limit Ad Tracking During Configuration of the Measurement Client):

  • let measurement = Measurement.shared
    measurement.appLimitAdTracking.bool = true
    measurement.start(appGUIDString: "_YOUR_APP_GUID_")
  • KVAMeasurement *measurement = KVAMeasurement.shared;
    measurement.appLimitAdTracking.boolean = YES;
    [measurement startWithAppGUIDString:@"_YOUR_APP_GUID_"];

 

Example (Enable App Limit Ad Tracking After Starting the Measurement Client):

  • let measurement = Measurement.shared
    measurement.appLimitAdTracking.bool = true
  • KVAMeasurement *measurement = KVAMeasurement.shared;
    measurement.appLimitAdTracking.boolean = @(YES)


Estimated Time to Complete
5 Minutes

Logging provides a text-based log of the SDK’s behavior at runtime, for purposes of debugging.

For example, while testing you may wish to see the contents of certain payloads being sent to Kochava servers, in which case you would enable logging at a debug (or higher) level.

Six different log levels are available, each of which include all log levels beneath them. Info log level is set by default, although trace log level should be used when debugging so that all possible log messages are generated.

 

Log Level: never

No logging messages are generated.

Log Level: error

Errors which are to some extent fatal.

Log Level: warn

Warnings which are not fatal.

Log Level: info

General information, such as basic initialization and API calls.

Log Level: debug

Low level messages intended for verifying (or debugging) an integration. Includes transaction payloads.

Log Level: trace

Very low level messages intended for tracing the origin of an issue.

 

To enable logging, set the desired log level early during initialization. As the SDK executes log messages will be printed to your console log. In this example we’re setting a more detailed log level when a debug build is detected, which is suggested but not required.

 

Example (Enabling trace logging in a non-production build):

  • // log
    let log = Log.shared
    #if DEBUG
    log.level = .trace
    #else
    log.level = .warn
    #endif
     
    // measurement
    let measurement = Measurement.shared
    measurement.start(appGUIDString: "_YOUR_APP_GUID_")
  • // log
    KVALog *log = KVALog.shared;
    #if DEBUG
    log.level = KVALog_Level.trace;
    #else
    log.level = KVALog_Level.warn;
    #endif
     
    // measurement
    KVAMeasurement *measurement = KVAMeasurement.shared;
    [measurement startWithAppGUIDString:@"_YOUR_APP_GUID_"];

 

NOTE: If you are copying a trace log for the purposes of troubleshooting, please copy the entire log, as the SDK log messages often span multiple lines and copying only lines with “kva” or “kochava” in them will result in missing lines.

 

BEST PRACTICES: Logging should be set to info log level or lower for production builds. This will limit the log messages generated by the SDK to errors, warnings, and basic information messages which contain no sensitive information.


Estimated Time to Complete
10 Minutes

Placing the measurement client into sleep mode, the client will enter a state where all non-essential network transactions will be held and persisted until the client is woken up. This can be useful if you wish to start the client early but need the measurement client to wait before sending install data or events to Kochava servers.

For example, if you wanted the measurement startup process to wait for permission prompts, you might start the measurement client in sleep mode during the app launch but wait while the user provides input. Once ready, the measurement client can be woken and will continue with its own startup process without losing any events that may have been queued beforehand.

 

Example (Enabling Sleep Mode During Initialization):

  • let measurement = Measurement.shared
    measurement.sleepBool = true
    measurement.start(appGUIDString: "_YOUR_APP_GUID_")
  • KVAMeasurement *measurement = KVAMeasurement.shared;
    measurement.sleepBool = YES;
    [measurement startWithAppGUIDString:@"_YOUR_APP_GUID_"];

 

Example (Enabling Sleep Mode After Starting the Measurement Client):

  • let measurement = Measurement.shared
    measurement.sleepBool = true
  • KVAMeasurement *measurement = KVAMeasurement.shared;
    measurement.sleepBool = YES;

 

Once you are ready to wake the measurement client simply set the sleep state to false. At that point the client will wake up and continue as normal, sending any waiting install data or queued events.

 

Example (Waking the Measurement Client from Sleep Mode)

  • let measurement = Measurement.shared
    measurement.sleepBool = false
  • KVAMeasurement *measurement = KVAMeasurement.shared;
    measurement.sleepBool = NO;

 

NOTE: For every call to set sleep to true, there should be a matching call at some point setting sleep to false. While these calls do not necessarily have to be balanced, care should be taken to not inadvertently leave the measurement client sleeping permanently. This allows the client to wake up and continue with necessary logic and processing of data. Any events or other activity queued while sleeping will be held but not sent until sleep mode is set to false. This means that if the client is never woken from sleep mode, events and other activity will continue to build up in the queue, causing undesirable results.


5 Minutes
Estimated Time to Complete
5 Minutes

The SDK can be shut down at any time, which will completely disable and stop all features from continuing execution.

The SDK should not be expected to be shutdown in typical scenarios, but this may be useful during consent-applicable cases when consent has been declined or revoked after starting the client and you wish to completely stop all forms of measurement or tracking.

After shutting down, all network communication with Kochava will cease, events will no longer be sent to Kochava, and any API calls will respond as if the SDK had never been started. The SDK must be configured and started again if you wish for measurement to resume.

 

Example (Shut Down the SDK)

  • KochavaMeasurement.Product.shared.shutdown()
  • [KochavaMeasurement_Product.shared shutdownWithDeleteLocalDataBool:NO];

 

Clearing SDK Data:

The shutdown() method accepts a boolean indicating whether you wish to also clear all persisted SDK data from disk when shutting down. This should always be set to false and should never be set to true without a complete understanding of the ramifications of clearing this data, as it could create duplicate user metrics or worse.

 

Example (Shut Down the SDK and Delete Data)

  • KochavaMeasurement.Product.shared.shutdown(deleteLocalDataBool: true)
  • [KochavaMeasurement_Product.shared shutdownWithDeleteLocalDataBool:YES];

 

Shutdown vs. Sleep:

The SDK’s sleep functionality can also be used to temporarily prevent the SDK from sending data to Kochava; however, sleep will continue to allow certain select activities behind the scenes. This includes periodic communication with Kochava for new operating parameters, and ultimately the SDK still presents itself to the host as started. Local persistent data for the SDK is also maintained.

Shutting down the SDK, on the other hand, completely stops the SDK from functioning and no operations will occur until it is configured and started again. Persistent local data for the SDK can be optionally removed as part of the shutdown process using a parameter.


30 Minutes
Estimated Time to Complete
30 Minutes

 

DISCLAIMER: Kochava does not offer legal advice for businesses in relation to CCPA and/or GDPR compliance. Information herein is for reference only and businesses are encouraged to seek their own legal counsel regarding CCPA and/or GDPR compliance efforts and obligations.

 

The Kochava SDK deals with potentially sensitive data such as device identifiers, care should be taken to ensure that your use of the Kochava SDK remains compliant with any applicable laws and regulations.


CCPA

For purposes of CCPA, the Kochava SDK follows IAB’s CCPA Compliance Framework by reading the U.S. Privacy String from local storage, when present. You do not need to take any action in the Kochava Measurement SDK for this functionality. However, it is your responsibility to ensure the U.S. Privacy String has been set within local storage when appropriate. The SDK will look for the U.S. Privacy String in local app storage under the key ‘IABUSPrivacy_String’ within default shared preferences on Android and default NSUserDefaults on iOS. As long as the value is present, the SDK will pick it up.


GDPR

GDPR applies to users within the EU and requires users to opt-in to data collection. By using this feature, the Kochava SDK will handle determining when consent is required for any user. It is your responsibility to use that information to determine if and when to prompt for consent and to report the results back to the SDK. The SDK will automatically restrict the sending of data to Kochava’s servers unless consent is not required or has been granted.

 

Configure Dashboard Settings:

Within the Kochava dashboard, enable Intelligent Consent Management GDPR for this app and adjust other related settings. The SDK’s consent related features and API calls will only have any effect if the feature is enabled on the Kochava dashboard.

 

Subscribe to Consent Changes —

Subscribe to updates from the SDK on when the user is in an applicable consent region. This will be called once shortly after starting the SDK and may be called periodically thereafter. This listener is dependent on an active internet connection and may get delayed if it is not available.

Use this information to inform your consent logic on if the user should be prompted. Exact timing and location of prompting is application specific and left up to your implementation. If your consent logic already handles the if and when of prompting this listener can be omitted.

 

Example (Init Completed Handler)

  • let measurement = Measurement.shared
    measurement.config.closure_didComplete =
    {
        config in
                
        print("config.consentGDPRAppliesBool = \(config.consentGDPRAppliesBool)")
    }
  • KVAMeasurement *measurement = KVAMeasurement.shared;
    measurement.config.closure_didComplete = ^(KVANetworking_Config * _Nonnull config)
    {
        NSLog(@"consentGDPRAppliesBool=%@", @(config.consentGDPRAppliesBool));
    };

 

Reporting Consent Results —

When the user responds to your consent prompt dialog or if the user otherwise grants or declines consent, the Kochava SDK must be notified of the result. If the user dismisses or ignores the dialog and does not provide a response no action should be taken.

 

Example (User Granted Consent)

  • let measurement = Measurement.shared
    measurement.privacy.intelligentConsent.grantedBool = true
  • KVAMeasurement *measurement = KVAMeasurement.shared;
    measurement.privacy.intelligentConsent.grantedBoolNumber = @(YES);

 

Example (User Declined Consent)

  • let measurement = Measurement.shared
    measurement.privacy.intelligentConsent.grantedBool = false
  • KVAMeasurement *measurement = KVAMeasurement.shared;
    measurement.privacy.intelligentConsent.grantedBoolNumber = @(NO);

For complete details on how this feature works, refer to our Intelligent Consent Manager within this support documentation.


Self-Managed

If you choose not to use our Intelligent Consent Management feature and you are handling consent on your own or using a 3rd party tool, startup and use of the SDK (when integrated with an Tracking features) should not occur until after you have determined consent is not required or consent has been granted. Any calls to the SDK should be wrapped in this check, so that if a user revokes consent later calls to the SDK will stop. Ensure that you have started the SDK when moving forward to make any API calls when going this route.

 

Example (Starting the SDK Only When Consent Allows)

  • // We will not start the measurement module unless consent requirements are met.
    if !consentRequired || consentGranted {
        let measurement = Measurement.shared
        measurement.start(appGUIDString: "_YOUR_APP_GUID_")
    }
  • // We will not start the measurement module unless consent requirements are met.
    if (!consentRequired || consentGranted) {
        KVAMeasurement *measurement = KVAMeasurement.shared;
        [measurement startWithAppGUIDString:@"_YOUR_APP_GUID_"];
    }

 

Example (Calling SDK Methods Only When Consent Allows)

  • // We will not call measurement APIs unless consent requirements are met.
    if !consentRequired || consentGranted {
            Event.sendCustom(eventName: "My Event")
    }
  • // We will not call measurement APIs unless consent requirements are met.
    if (!consentRequired || consentGranted) {
        [KVAEvent sendCustomWithEventName:@"My Event"];
    }

 

Example (Shutdown the SDK if Consent is Revoked)

  • // Shutdown the tracker and optionally delete data if consent requirements are no longer met.
    if (consentRequired && !consentGranted) {
        KochavaMeasurement.Product.shared.shutdown(deleteLocalDataBool: true)
    }
  • // Shutdown the tracker and optionally delete data if consent requirements are no longer met.
    if (consentRequired && !consentGranted) {
        [KochavaMeasurement_Product.shared shutdownWithDeleteLocalDataBool:true];
    }

 

NOTE: All consent-related API calls (such as granting or declining consent) will have no effect unless the Intelligent Consent Manager feature has been enabled in your Kochava dashboard. Do not use these methods if you are handling consent on your own or through a 3rd party tool.


10 Minutes
Estimated Time to Complete
10 Minutes

 

This topic describes how to restrict data egress from the device and is considered an advanced feature which is unnecessary for a typical SDK integration. Improper usage could detrimentally impact measurement and attribution and should be avoided unless you have a specific need.

For purposes of measurement and attribution, the SDK automatically transmits certain device information and device identifiers which are commonly referred to as ‘datapoints’. For privacy reasons or otherwise, there may be times you wish to restrict these datapoints from egressing from the device. For more information around what datapoints the SDK transmits, view the Data Privacy and Safety document.

Restricting the egress of a datapoint can be done one of two ways and is dependent on whether you want the restriction to apply to all users of the app or conditionally on a per-user basis.

 

App Wide Restriction

If you wish to unconditionally prevent the collection and egress of a datapoint for all users of your app, this can be done by disabling the desired datapoint within the Advanced section of the edit app page in your Kochava dashboard. Disabling a datapoint in this manner will automatically instruct the SDK not to collect or allow egress of said datapoint from the device for all users. No code is required for this approach and if this satisfies your requirements there is no need to read any further.

 

Conditional Restriction

If you wish to conditionally restrict datapoint egress on a per-user basis, or you wish to restrict egress of a datapoint not available in your Kochava dashboard, follow the steps below. For example, you may wish to restrict the egress of the device identifiers only for child users or only for users who do not meet other criteria.

Conditionally restricting datapoint egress requires two steps in code:

  1. Create a privacy profile with a list of datapoint key names to restrict.
  2. Enable the privacy profile if restrictions should apply to this user.

 

Create a Privacy Profile:

A privacy profile is a named collection of datapoints which may be restricted for the current user. Privacy profiles must be both created and then enabled before datapoint restriction will take place. Typically, you will create all possible privacy profiles when starting the SDK, and then enable only those which apply to the current user.

You may create multiple privacy profiles, one for each category of restriction you would like to apply. For example, you might create a privacy profile named “child” which includes datapoints you would like to restrict for children. Or, you might create a privacy profile named “always” which includes datapoints you’d like to always restrict for all users. The name of the privacy profile may not begin with an underscore “_” and must be a non-empty string, but is otherwise arbitrary and may be anything you like.

 

Enable a Privacy Profile:

Privacy profiles are not enabled by default, and datapoint egress is not restricted until the privacy profile is enabled. Once you have created a privacy profile, at some point you must also enable it in order to restrict datapoint egress for the current user. A privacy profile is enabled or disabled through a single API call by referencing its name.

Once a privacy profile is enabled, from that moment on its datapoints will no longer egress from the device. Restrictions are enforced from the moment a privacy profile is enabled and are not applied retroactively; it is your responsibility to ensure applicable privacy profiles are enabled prior to the moment when datapoint egress would take place. For example, if you queue an event but do not enable a privacy profile until after queuing the event, the event will egress with all datapoints intact because the privacy profile was not enabled at the time of egress.

If necessary, a privacy profile may be disabled via the same API call used to enable it.

NOTE: The lifecycle of a privacy profile and its enabled status is limited to the current tracker instance and does not persist between app launches or SDK tracker instances. Privacy profiles must be created and enabled each time the SDK is started.

 

Example (Always Restrict Device Identifier Egress)

In this example, we want to prevent the device identifiers from being transmitted from the device for all users. Start the tracker and then immediately create and enable a privacy profile named “restrict ids” with the datapoint keys for your platform’s device identifiers.

  • // PrivacyProfile
    // ⓘ Register. One profile is registered with name "restrict ids".
    PrivacyProfile.register(
        name: "restrict ids", 
        datapointKeyArray: ["idfa", "idfv"]
    )
     
    // measurement
    // ⓘ Configure and start. The "restrict ids" privacy profile is enabled.
    let measurement = Measurement.shared
    measurement.privacy.setEnabledBool(
        forProfileName: "restrict ids", 
        enabledBool: true
    )
    measurement.start(appGUIDString: "_YOUR_APP_GUID_")
     
    // ...for the remainder of this instance any measurement signal from the SDK will not include the IDFA or IDFV.
  • // KVAPrivacyProfile
    // ⓘ Register. One profile is registered with name "restrict ids".
    [KVAPrivacyProfile registerWithName:@"restrict ids" datapointKeyArray:@[@"idfa", @"idfv"]];
     
    // measurement
    // ⓘ Configure and start. The "restrict ids" privacy profile is enabled.
    KVAMeasurement *measurement = KVAMeasurement.shared;
    [measurement.privacy setEnabledBoolForProfileName:@"restrict ids" enabledBool:YES];
    [measurement startWithAppGUIDString:@"_YOUR_APP_GUID_"];
     
    // ...for the remainder of this instance any measurement signal from the SDK will not include the IDFA or IDFV.
    
  • // KVAPrivacyProfile
    // ⓘ Register. One profile is registered with name "restrict ids".
    KVAPrivacyProfile.register(
        withNameString: "restrict ids", 
        payloadKeyStringArray: ["idfa", "idfv"]
    )
     
    // KVATracker
    // ⓘ Configure and start. The "restrict ids" privacy profile is enabled.
    KVATracker.shared.privacy.setEnabledBool(
        forProfileNameString: "restrict ids", 
        enabledBool: true
    )
    KVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_")
     
    // ...for the remainder of this instance any measurement signal from the SDK will not include the IDFA or IDFV.

 

Example (Restrict Device Identifier Egress for Children)

In this example, we want to prevent the device identifiers from being transmitted from the device only for children. This concept works for any scenario where conditional restriction is necessary.

In order to accomplish this, the SDK must not be started until we know whether the user is a child. Once we know that, we start the SDK and then enable our “child” privacy profile only if the user was determined to be a child. For this scenario, it’s important not to start the tracker until child status is known, because the SDK would otherwise immediately begin transmitting measurement signal including datapoints you may wish to restrict.

  • // childBool
    // ⓘ Determine if this user is a child using your own logic.
    let childBool = self.childBool()
    
    // PrivacyProfile
    // ⓘ Register. One profile is registered with name "child".
    PrivacyProfile.register(
        name: "child", 
        datapointKeyArray: ["idfa", "idfv"]
    )
     
    // measurement
    // ⓘ Configure and start. The "child" privacy profile is enabled if this user is a child.
    let measurement = Measurement.shared
    measurement.privacy.setEnabledBool(
        forProfileName: "child", 
        enabledBool: childBool
    )
    measurement.start(appGUIDString: "_YOUR_APP_GUID_")
     
    // ...for the remainder of this instance any measurement signal from the SDK will not include the IDFA or IDFV.
  • // childBool
    // ⓘ Determine if this user is a child using your own logic.
    BOOL childBool = [self childBool];
    
    // KVAPrivacyProfile
    // ⓘ Register. One profile is registered with name "child".
    [KVAPrivacyProfile registerWithName:@"child" datapointKeyArray:@[@"idfa", @"idfv"]];
     
    // measurement
    // ⓘ Configure and start. The "child" privacy profile is enabled if this user is a child.
    KVAMeasurement *measurement = KVAMeasurement.shared;
    [measurement.privacy setEnabledBoolForProfileName:@"child" enabledBool:childBool];
    [measurement startWithAppGUIDString:@"_YOUR_APP_GUID_"];
     
    // ...for the remainder of this instance any measurement signal from the SDK will not include the IDFA or IDFV.
  • // childBool
    // ⓘ Determine if this user is a child using your own logic.
    let childBool = self.childBool()
    
    // KVAPrivacyProfile
    // ⓘ Register. One profile is registered with name "child".
    KVAPrivacyProfile.register(
        withNameString: "child", 
        payloadKeyStringArray: ["idfa", "idfv"]
    )
     
    // KVATracker
    // ⓘ Configure and start. The "child" privacy profile is enabled if this user is a child.
    KVATracker.shared.privacy.setEnabledBool(
        forProfileNameString: "child", 
        enabledBool: childBool
    )
    KVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_")
     
    // ...for the remainder of this instance any measurement signal from the SDK will not include the IDFA or IDFV.

 

Example (Restrict IDFA iOS 14.0+)

In this example, we want to prevent the IDFA from being transmitted from the device for users of iOS 14.0+. This is sometimes desired as Apple allows the collection of the IDFA on 14.0 to 14.4 even without an ATT prompt.

To accomplish this, we create a privacy profile and enable it only if the version of iOS is at least 14.0. This version check can be made a number of ways and how you implement it is up to you.


// Start the measurement client
let measurement = Measurement.shared
measurement.start(appGUIDString: "_YOUR_APP_GUID_")
// As soon as the measurement client is started, create a privacy profile which restricts IDFA and enable it only if the iOS version is at least 14.0:
If #available(iOS 14.0, *)
{
  // create and enable the privacy profile
  PrivacyProfile.register(name: "restrictIDFA", datapointKeyArray: ["idfa"])
  measurement.shared.privacy.setEnabledBool(forProfileName: "restrictIDFA", enabldBool: true)
}


Estimated Time to Complete
5 Minutes

During testing, debugging, and non-production app development, the following steps will help you get the most out of your test environment and help to ensure your integration is working properly.

  1. Use an alternate testing App GUID so that your testing activities do not have an impact on your live app analytics.
  2. Enable Logging, if helpful, to gain insight into the SDK’s behavior during runtime.
  3. If you would like the SDK to behave as it would during a new install, be sure to un-install the app before each test.
  4. Test your Kochava integration. For more information see: Testing the Integration.

Keep in mind that you should always add logic to ensure that you do not accidentally release to production a build with a development configuration used. Below is an example of how this type of configuration might look.

  • // log
    let log = Log.shared
    #if DEBUG
    log.level = .trace
    #else
    log.level = .info
    #endif
            
    // appGUIDString
    let appGUIDString: String
    #if DEBUG
    appGUIDString = "_YOUR_TEST_APP_GUID_"
    #else
    appGUIDString = "_YOUR_PRODUCTION_APP_GUID_"
    #endif
            
    // measurement
    let measurement = Measurement.shared
    measurement.start(appGUIDString: appGUIDString)
  • // KVALog
    KVALog *log = KVALog.shared;
    #if DEBUG
    log.level = KVALog_Level.trace;
    #else
    log.level = KVALog_Level.info;
    #endif
        
    // appGUIDString
    NSString *appGUIDString;
    #if DEBUG
    appGUIDString = @"_YOUR_TEST_APP_GUID_";
    #else
    appGUIDString = @"_YOUR_PRODUCTION_APP_GUID_";
    #endif
        
    // measurement
    KVAMeasurement *measurement = KVAMeasurement.shared;
    [measurement startWithAppGUIDString:appGUIDString];

 

Analyzing SDK Behavior:

While testing your integration, it is important to understand the measurement client’s basic flow of operations. When the client is started the following sequence of events occur:

  1. A handshake with Kochava may be made to determine dynamic settings for this app.
  2. If this is the first launch, the install data is sent to Kochava (this only happens once).
  3. At this point the SDK is idle and awaits requests from the app.
  4. If a request is made to the SDK by the app, the request is moved to a background thread for processing. After processing the request and performing any necessary network calls the SDK returns to an idle state.
  5. When the app is terminated or suspended, a session-end payload may be sent to Kochava.
  6. When the app is resumed or relaunched, a session-begin payload may be sent to Kochava.

NOTE: While testing, keep in mind that data sent from the SDK may sometimes be delayed up to a few minutes before being displayed within the Kochava analytics dashboard.


30 Minutes
Estimated Time to Complete
30 Minutes

In order to call methods and interact with the Kochava SDK from within a native WebView, see: SDK Utilization from a Web View.

 
 

Last Modified: Feb 6, 2024 at 5:34 pm