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 Tracker, refer to our iOS/tvOS SDK Integration support documentation.
Confirming the SDK Integration:
Ensure the SDK has been properly integrated.

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 Free App Analytics.
- Double check the SDK configuration in code, ensuring the correct App GUID.
- Run the app for approximately 30 seconds, which will allow more than enough time for the SDK to start and send an install to Free App Analytics under typical conditions.
- Wait a minute or two and visit the Install Feed Validation page for your app within the Free App Analytics dashboard, under Apps & Assets > Install Feed Validation. Within that page, look for the Integration Success! message which indicates integration was successful and that Free App Analytics 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.
- 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 Tracking:
If you are tracking user events, you can use this test to ensure the SDK was configured properly and is successfully sending these events to Free App Analytics.
- Double check the SDK configuration in code, ensuring the correct App GUID.
- Double check your event tracking code and ensure it is reachable.
- 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.
- Wait a minute or two and visit the Event Manager page for your app within the Free App Analytics 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 tracking these events.
- 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.
Supporting SKAdNetwork:
Kochava’s SKAdNetwork support is seamlessly integrated with standard event tracking.
No special code is needed to support SKAdNetwork, beyond tracking your existing events which are eligible for conversion. However, events which are to be considered for SKAdNetwork conversion updates must be built using standard parameters, rather than using an existing serialized/stringified JSON object for event data. See the topic Tracking Events for more detail on using standard parameters.
The KochavaAdNetwork SDK module must be included to support SKAdNetwork functionality (see SDK Integration if you haven’t done this yet). Once the module is included, it must be registered in code prior to starting the tracker.
Register the KochavaAdNetwork Module —
-
12// register the module prior to starting the trackerKVAAdNetworkProduct.shared.register();
-
12// register the module prior to starting the tracker[KVAAdNetworkProduct.shared register];
After setting up SKAdNetwork in your Free App Analytics dashboard, the SDK will automatically:
- Call Apple’s SKAdNetwork registration at the first opportunity following launch.
- 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.
Subscribe to SDK Notifications:
Optionally, you may subscribe to notifications triggered when the SDK calls Apple’s registerAppForAdNetworkAttribution() and updateConversionValue() API. 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.
NOTE: Requires iOS SDK version 4.4.0 or higher.
-
1234567891011121314151617181920// conversionDidUpdateValueBlocklet conversionDidUpdateValueBlock: KVAAdNetworkConversionDidUpdateValueBlock ={(conversion, result) inprint("updateConversionValue() called with a value of \(result.valueInt())")}// didRegisterAppForAttributionBlocklet didRegisterAppForAttributionBlock: KVAAdNetworkDidRegisterAppForAttributionBlock ={(adNetwork) inprint("registerAppForAdNetworkAttribution() called")}// KVATrackerKVATracker.shared.adNetwork?.conversion.didUpdateValueBlock = conversionDidUpdateValueBlockKVATracker.shared.adNetwork?.didRegisterAppForAttributionBlock = didRegisterAppForAttributionBlockKVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_")
-
12345678910111213141516// conversionDidUpdateValueBlockKVAAdNetworkConversionDidUpdateValueBlock conversionDidUpdateValueBlock = ^(KVAAdNetworkConversion * _Nonnull conversion, KVAAdNetworkConversionResult * _Nonnull result){NSLog(@"updateConversionValue() called with a value of %@", @(result.valueInt));};// didRegisterAppForAttributionBlockKVAAdNetworkDidRegisterAppForAttributionBlock didRegisterAppForAttributionBlock = ^(KVAAdNetwork * _Nonnull adNetwork){NSLog(@"registerAppForAdNetworkAttribution() called");};// KVATrackerKVATracker.shared.adNetwork.conversion.didUpdateValueBlock = conversionDidUpdateValueBlock;KVATracker.shared.adNetwork.didRegisterAppForAttributionBlock = didRegisterAppForAttributionBlock;[KVATracker.shared startWithAppGUIDString:@"_YOUR_APP_GUID_"];
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”).
AppTrackingTransparency and IDFA Collection:
Method for handling Apple’s new IDFA collection model.
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 drop-dead 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):
KVATracker.shared.appTrackingTransparency.enabledBool = true KVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_")
Example Allow more than the default 30 seconds for the user to respond:
KVATracker.shared.appTrackingTransparency.enabledBool = true KVATracker.shared.appTrackingTransparency.authorizationStatusWaitTimeInterval = 90.0 KVATracker.shared.start(withAppGUIDString: "_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:
KVATracker.shared.appTrackingTransparency.enabledBool = true // Instruct the SDK not to request authorization results yet, unless we've already prompted the user on a previous launch: KVATracker.shared.appTrackingTransparency.autoRequestTrackingAuthorizationBool = UserDefaults.standard.value(forKey: “com.myOrg.didATTPrompt”) as? Bool ?? false KVATracker.shared.appTrackingTransparency.authorizationStatusWaitTimeInterval = 120.0 KVATracker.shared.start(withAppGUIDString: "_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:
// request tracking authorization at a curated moment ATTrackingManager.requestTrackingAuthorization { (authorizationStatus) in // notify the Kochava SDK that it may proceed with tracking authorization KVATracker.shared.appTrackingTransparency.autoRequestTrackingAuthorizationBool = true // Persist a flag indicating the user has answered the ATT prompt at least once UserDefaults.standard.setValue(true, forKey: “com.myOrg.didATTPrompt”) }
App Store Submission Guidance and Best Practices
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.
Supporting App Clips:
Additional steps to properly support app clips.
The standard SDK is used within your app clip (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.
- Configure the SDK.
- Process the Deeplink
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 and pass that App GUID to the SDK during configuration of your app clip. For the full app, create or use a Kochava app of platform type iOS and pass that App GUID to the SDK during configuration of your full app.
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 by setting KVAAppGroups.shared.deviceAppGroupIdentifierString to the new App Group identifier you created. It is important that if you set KVAAppGroups.shared.deviceAppGroupIdentifierString that you do so prior to ever working with parent class KVATracker. This identifier must be the same between the app clip and full app.
Example — Provide a Shared Storage Container Before Starting the Tracker —
1 2 3 4 5 |
// Provide an app group identifier string which facilitates shared storage between the app clip and full app KVAAppGroups.shared.deviceAppGroupIdentifierString = "group.com.kochava.host.kochava" // Start the tracker using either the app clip or full app guid KVATracker.shared.start(withAppGUIDString: "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).
When the app clip 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 Enhanced Deeplinking below 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.
Tracking Events:
Track user behavior and actions beyond the install.

Examples include in-app purchases, level completions, or other noteworthy user activity you wish to track. Events can be instrumented either by using the standard format provided by the SDK or using your own custom event name and data.
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 track. A maximum of 16 parameters can be set.
- Create an event object using the desired standard event type.
- Set the desired parameter value(s) within the event object.
- Pass the event object to the tracker’s Send Event method.
Example (Standard Event with Standard Parameters) —
-
1234let event = KVAEvent(type: .purchase)event.nameString = "Gold Token"event.priceDoubleNumber = 0.99event.send()
-
1234KVAEvent *event = [KVAEvent eventWithType:KVAEventType.purchase];event.nameString = @"Gold Token";event.priceDoubleNumber = @(0.99);[event send];
-
123456if let event = KochavaEvent(eventTypeEnum: .purchase) {event.nameString = "Gold Token"event.priceDoubleNumber = 0.99KVATracker.shared.send(event)}
-
1234KochavaEvent *event = [KochavaEvent eventWithEventTypeEnum:KochavaEventTypeEnumPurchase];event.nameString = @"Gold Token";event.priceDoubleNumber = @(0.99);[KVATracker.shared sendEvent:event];
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) —
-
12345678let event = KVAEvent(type: .levelComplete)event.nameString = "The Deep Dark Forest"event.infoDictionary =["attempts": 3,"score": 12000]event.send()
-
1234567KVAEvent *event = [KVAEvent eventWithType:KVAEventType.levelComplete];event.nameString = @"The Deep Dark Forest";event.infoDictionary = @{@"attempts": @(3),@"score": @(12000)};[event send];
-
123456789if let event = KochavaEvent(eventTypeEnum: .levelComplete) {event.nameString = "The Deep Dark Forest"event.infoDictionary = ["attempts": 3,"score": 12000]KVATracker.shared.send(event)}
-
1234567KochavaEvent *event = [KochavaEvent eventWithEventTypeEnum:KochavaEventTypeEnumLevelComplete];event.nameString = @"The Deep Dark Forest";event.infoDictionary = @{@"attempts": @(3),@"score": @(12000)};[KVATracker.shared sendEvent:event];
If you wish to use a custom event type with standard parameters, choose the Custom standard event type and then set the customEventNameString property with your desired event name. Be aware that if you choose the Custom type but do not also provide a custom event name, the event name will simply appear as “Custom”.
For a detailed list of standard event types and parameters, see: Post Install Event Examples
Developer API Reference:
func sendEvent(withNameString: String, infoString: String?)
func sendEvent(withNameString: String, infoDictionary: [AnyHashable: Any]?)
func send(KochavaEvent)
class KochavaEvent
Purchases:
Track in-app purchases and revenue.
App Store receipts can also be optionally included with your purchase event to be validated server-side by Free App Analytics through the App Store. If desired, include the receipt in your event using the standard parameter appStoreReceiptBase64EncodedString.
Example (Standard Purchase Event):
-
123456let event = KVAEvent(type: .purchase)event.priceDecimalNumber = 4.99event.currencyString = "usd"event.nameString = "Loot Box"event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedStringevent.send()
-
123456KVAEvent *event = [KVAEvent eventWithType:KVAEventType.purchase];event.priceDoubleNumber = @(4.99);event.currencyString = @"usd";event.nameString = @"Loot Box";event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString;[event send];
-
12345678if let event = KochavaEvent(eventTypeEnum: .purchase) {event.priceDecimalNumber = 4.99event.nameString = "Loot Box"event.currencyString = "usd"event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedStringKVATracker.shared.send(event)}
-
123456KochavaEvent *event = [KochavaEvent eventWithEventTypeEnum:KochavaEventTypeEnumPurchase];event.priceDoubleNumber = @(4.99);event.nameString = @"Loot Box";event.currencyString = @"usd";event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString;[KVATracker.shared sendEvent:event];
Example (Custom Purchase Event with Additional Data):
-
123KVAEvent.sendCustom(withNameString: "Purchase", infoDictionary: ["price": 4.99,"name": "Loot Box"
-
1234[KVAEvent sendCustomWithNameString:@"Purchase" infoDictionary:@{@"price": @(4.99),@"name": @"Loot Box"}];
-
1234KVATracker.shared.sendEvent(withNameString: "Purchase", infoDictionary: ["name": "Monthly Subscription","price": 9.99])
-
1234[KVATracker.shared sendEventWithNameString:@"Purchase" infoDictionary:@{@"name": @"Monthly Subscription",@"price": @(9.99)}];
Developer API Reference:
func sendEvent(withNameString: String, infoString: String?)
func sendEvent(withNameString: String, infoDictionary: [AnyHashable: Any]?)
func send(KochavaEvent)
class KochavaEvent
Tracking Subscriptions and Trials:
Track user subscriptions and free trials.
In order to effectively track 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)
Example (Identity Link with Subscription):
-
12345678910// first register an identity link for this userKVATracker.shared.identityLink.register(withNameString: "Subscriber ID", identifierString: "ABCDEF123456789")// next, instrument the subscription eventlet event = KVAEvent(type: .subscribe)event.priceDecimalNumber = 9.99event.currencyString = "usd"event.nameString = "Monthly Subscription"event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedStringevent.send()
-
12345678910// first register an identity link for this user[KVATracker.shared.identityLink registerWithNameString:@"Subscriber ID" identifierString:@"ABCDEF123456789"];// next, instrument the subscription eventKVAEvent *event = [KVAEvent eventWithType:KVAEventType.subscribe];event.priceDecimalNumber = (NSDecimalNumber *)[NSDecimalNumber numberWithDouble:9.99];event.currencyString = @"usd";event.nameString = @"Monthly Subscription";event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString;[event send];
-
1234567891011121314// first set an identity link for this userKVATracker.shared.sendIdentityLink(with: ["Subscriber ID": "ABCDEF123456789"])// next, instrument the subscription eventif let event = KochavaEvent(eventTypeEnum: .subscribe) {event.priceDecimalNumber = 9.99event.currencyString = "usd"event.nameString = "Monthly Subscription"event.appStoreReceiptBase64EncodedString = appStoreReceiptData.base64EncodedString()KVATracker.shared.send(event)}
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):
-
1234567891011// first register an identity link for this userKVATracker.shared.identityLink.register(withNameString: "Subscriber ID", identifierString: "ABCDEF123456789")// next, instrument the subscription eventlet event = KVAEvent(type: .startTrial)event.priceDecimalNumber = 0.00event.currencyString = "usd"event.nameString = "Monthly Subscription"event.userIdString = "ABCDEF123456789"event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedStringevent.send()
-
1234567891011// first register an identity link for this user[KVATracker.shared.identityLink registerWithNameString:@"Subscriber ID" identifierString:@"ABCDEF123456789"];// next, instrument the subscription eventKVAEvent *event = [KVAEvent eventWithType:KVAEventType.startTrial];event.priceDecimalNumber = (NSDecimalNumber *)[NSDecimalNumber numberWithDouble:0.00];event.currencyString = @"usd";event.nameString = @"Monthly Subscription";event.userIdString = @"ABCDEF123456789";event.appStoreReceiptBase64EncodedString = appStoreReceiptBase64EncodedString;[event send];
-
123456789101112131415// first set an identity link for this userKVATracker.shared.sendIdentityLink(with: ["Subscriber ID": "ABCDEF123456789"])// next, instrument the subscription eventif let event = KochavaEvent(eventTypeEnum: .startTrial) {event.priceDecimalNumber = 0.0event.currencyString = "usd"event.nameString = "Monthly Subscription"event.userIdString = "ABCDEF123456789"event.appStoreReceiptBase64EncodedString = appStoreReceiptData.base64EncodedString()KVATracker.shared.send(event)}
Deeplinking:
Track deeplink related actions and user activity.

Tracking deeplinks is accomplished similar to any other type of event. In order to track 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):
-
1234let event = KVAEvent(type: .deeplink)event.uriString = "some_deeplink_uri"event.sourceString = "some_source_application"event.send()
-
123KVAEvent *event = [KVAEvent eventWithType:KVAEventType.deeplink];event.uriString = @"some_deeplink_uri";[event send];
-
123456if let event = KochavaEvent(eventTypeEnum: .deepLink) {event.uriString = "some_deeplink_uri"event.sourceString = "some_source_application"KVATracker.shared.send(event)}
-
1234KochavaEvent *event = [KochavaEvent eventWithEventTypeEnum:KochavaEventTypeEnumDeepLink];event.uriString = @"some_deeplink_uri";event.sourceString = @"some_source_application";[KVATracker.shared sendEvent:event];
Example (Custom Deeplink Event with Additional Data):
-
1234KVAEvent.sendCustom(withNameString: "_Deeplink", infoDictionary: ["uri": "some_deeplink_uri","source": "some_source_application"])
-
1234[KVAEvent sendCustomWithNameString:@"_Deeplink" infoDictionary:@{@"uri": @"some_deeplink_uri"}];;
-
1234KVATracker.shared.sendEvent(withNameString: "_Deeplink", infoDictionary: ["uri": "some_deeplink_uri","source": "some_source_application"])
-
1234[KVATracker.shared sendEventWithNameString:@"_Deeplink" infoDictionary:@{@"uri": @"some_deeplink_uri",@"source": @"some_source_application"}];
Developer API Reference:
func send(KochavaEvent)
func sendEvent(withNameString: String, infoDictionary: [AnyHashable: Any]?)
class KochavaEvent
Enhanced Deeplinking:
Universal click deeplinking and re-engagement attribution.
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:
- Acquire a deeplink and Pass it to the SDK.
- 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 tracker 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 Kochava Tracker SDK before using this feature.
Example (Acquire the Deeplink) —
-
12345678910111213141516// example of standard (existing) deeplink processingfunc 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}
-
123456789101112{NSURL *url = userActivity.webpageURL;if (url != nil){[KVADeeplink processWithURL:url completionHandler:^(KVADeeplink * _Nonnull deeplink){// handle deeplink}];}return YES;}
-
12345678910111213141516// example of standard (existing) deeplink processingfunc 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}
-
12345678910111213-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler{NSURL *url = userActivity.webpageURL;if (url != nil){[KVADeeplink processWithURL:url completionHandler:^(KVADeeplink * _Nonnull deeplink){// handle deeplink}];}return YES;}
Example (Acquire a Deferred Deeplink) —
-
12345678910111213141516171819202122// example of deferred deeplink processingfunc 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) inif let destinationString = deeplink.destinationString, destinationString.count > 0{// handle deferred deeplink}}}return true}
-
123456789101112131415161718192021// example of deferred deeplink processing- (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;}
-
123456789101112131415161718192021222324252627// example of deferred deeplink processingfunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool{// ...let deferredDeeplinkProcessedBoolKey = "com.kochava.host.deferredDeeplinkProcessedBool"if UserDefaults.standard.object(forKey: deferredDeeplinkProcessedBoolKey) == nil{UserDefaults.standard.set(true, forKey: deferredDeeplinkProcessedBoolKey)let userActivityDictionary = launchOptions?[UIApplicationLaunchOptionsKey.userActivityDictionary] as? [AnyHashable: Any]if userActivityDictionary == nil{KVADeeplink.process(withURL: nil){(deeplink) inif let destinationString = deeplink.destinationString, destinationString.count > 0{// handle deferred deeplink}}}}return true}
-
1234567891011121314151617181920212223242526// example of deferred deeplink processing- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{// ...NSString * const deferredDeeplinkProcessedBoolKey = @"com.kochava.host.deferredDeeplinkProcessedBool";if ([NSUserDefaults.standardUserDefaults objectForKey:deferredDeeplinkProcessedBoolKey] == nil){[NSUserDefaults.standardUserDefaults setBool:YES forKey:deferredDeeplinkProcessedBoolKey];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;}
NOTE: For iOS, only proceed with deeplink processing if the deeplink is either not empty or this is the first app launch. This will prevent deferred deeplinks from being returned on app launches other than the first. This is reflected in the code sample above.
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 Free App Analytics 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 Free App Analytics 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) —
-
1234567891011121314151617KVADeeplink.process(withURL: url){(deeplink) inif let destinationString = deeplink.destinationString, destinationString.count > 0{// deeplink exists, parse the destination as you see fitlet urlComponents = URLComponents(string: destinationString)// route the user to the destination accordinglyprint("urlComponents=\(String(describing: urlComponents))")}else{// no deeplink to act upon, route to a default destination or take no action}}
-
12345678910111213141516[KVADeeplink processWithURL:url completionHandler:^(KVADeeplink * _Nonnull deeplink){NSString *destinationString = deeplink.destinationString;if (destinationString.length > 0){// deeplink exists, parse the destination as you see fitNSURLComponents *urlComponents = [NSURLComponents componentsWithString:destinationString];// route the user to the destination accordinglyNSLog(@"urlComponents=%@", urlComponents);}else{// no deeplink to act upon, route to a default destination or take no action}}];
-
12345678910111213141516KVADeeplink.process(withURL: url){(deeplink) inif let destinationString = deeplink.destinationString, destinationString.count > 0{// deeplink exists, parse the destination as you see fitlet urlComponents = URLComponents(string: destinationString)// route the user to the destination accordingly}else{// no deeplink to act upon, route to a default destination or take no action}}
-
12345678910111213141516[KVADeeplink processWithURL:url completionHandler:^(KVADeeplink * _Nonnull deeplink){NSString *destinationString = deeplink.destinationString;if (destinationString.length > 0){// deeplink exists, parse the destination as you see fitNSURLComponents *urlComponents = [NSURLComponents componentsWithString:destinationString];// route the user to the destination accordinglyNSLog(@"urlComponents=%@", urlComponents);}else{// no deeplink to act upon, route to a default destination or take no action}}];
About Deferred Deeplinking:
By passing an empty string (“”) to the Tracker’s ProcessDeeplink() 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 [Android, iOS] 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.
Identity Linking:
Link existing user identities with Free App Analytics devices.

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 Free App Analytics database.
In order to link identities, you will need to pass this identity link information in the form of unique key and value pairs to the tracker as early as possible. This can be done during tracker configuration if the identity link information is already known, or it can be done after starting the tracker using the Set Identity Link method.
Example (Set an Identity Link During Tracker Configuration):
-
1234KVATracker.sharedKVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_")KVATracker.shared.identityLink.register(withNameString: "User ID", identifierString: "123456789")KVATracker.shared.identityLink.register(withNameString: "Login", identifierString: "ljenkins")
-
1234// KVATracker.shared[KVATracker.shared startWithAppGUIDString:@"_YOUR_APP_GUID_"];[KVATracker.shared.identityLink registerWithNameString:@"User ID" identifierString:@"123456789"];[KVATracker.shared.identityLink registerWithNameString:@"Login" identifierString:@"ljenkins"];
-
1234567891011121314// identityLinkDictionarylet identityLinkDictionary: [AnyHashable: Any] = ["User ID": "123456789","Login": "username"]// parametersDictionarylet parametersDictionary: [AnyHashable: Any] = [kKVAParamAppGUIDStringKey: "_YOUR_APP_GUID_",kKVAParamIdentityLinkDictionaryKey: identityLinkDictionary]// KVATracker.sharedKVATracker.shared.configure(withParametersDictionary: parametersDictionary, delegate: nil)
-
1234567891011121314// identityLinkDictionaryNSDictionary *identityLinkDictionary = @{@"User ID": @"123456789",@"Login": @"username"};// parametersDictionaryNSDictionary *parametersDictionary = @{kKVAParamAppGUIDStringKey: @"_YOUR_APP_GUID_",kKVAParamIdentityLinkDictionaryKey: identityLinkDictionary};// KVATracker.shared[KVATracker.shared configureWithParametersDictionary:parametersDictionary delegate:nil];
Example (Set an Identity Link After Starting the Tracker):
-
12KVATracker.shared.identityLink.register(withNameString: "User ID", identifierString: "123456789")KVATracker.shared.identityLink.register(withNameString: "Login", identifierString: "ljenkins")
-
12[KVATracker.shared.identityLink registerWithNameString:@"User ID" identifierString:@"123456789"];[KVATracker.shared.identityLink registerWithNameString:@"Login" identifierString:@"ljenkins"];
-
1234KVATracker.shared.sendIdentityLink(with: ["User ID": "123456789","Login": "username"])
-
1234[KVATracker.shared sendIdentityLinkWithDictionary:@{@"User ID": @"123456789",@"Login": @"username"}];
Developer API Reference:
func sendIdentityLink(with: [AnyHashable: Any])
constant kKVAParamIdentityLinkDictionaryKey
trackerParametersDictionary
Retrieving Attribution:
Access the attribution results within the app.

Install attribution results can be retrieved from Free App Analytics 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 tracker as soon as 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.
Example (Configuring the Tracker to Retrieve Attribution):
-
12345678910// This completion handler will fire on every launch. Optionally check the retrievedBool if you wish to only parse these results once.if !KVATracker.shared.attribution.result.retrievedBool{KVATracker.shared.attribution.retrieveResult{(attributionResult) in// do something with attributionResult}}
-
12345678// This completion handler will fire on every launch. Optionally check the retrievedBool if you wish to only parse these results once.if (!KVATracker.shared.attribution.result.retrievedBool){[KVATracker.shared.attribution retrieveResultWithCompletionHandler:^(KVAAttributionResult * _Nonnull attributionResult){// do something with attributionResult}];}
-
123456789101112131415func tracker_configure() {// parametersDictionarylet parametersDictionary: [AnyHashable: Any] = [kKVAParamAppGUIDStringKey: "_YOUR_APP_GUID_",kKVAParamRetrieveAttributionBoolKey: true]// KVATracker.sharedKVATracker.shared.configure(withParametersDictionary: parametersDictionary, delegate: self)}func tracker(_ tracker: KochavaTracker, didRetrieveAttributionDictionary attributionDictionary: [AnyHashable : Any]) {print("do something with attributionDictionary... \(attributionDictionary)")}
-
123456789101112131415- (void)tracker_configure {// parametersDictionaryNSDictionary *parametersDictionary = @{kKVAParamAppGUIDStringKey: @"_YOUR_APP_GUID_",kKVAParamRetrieveAttributionBoolKey: @(YES)};// KVATracker.shared[KVATracker.shared configureWithParametersDictionary:parametersDictionary delegate:self];}- (void)tracker:(KochavaTracker *)tracker didRetrieveAttributionDictionary:(NSDictionary *)attributionDictionary {NSLog(@"do something with attributionDictionary...%@", attributionDictionary);}
Example (Using the Getter After Attribution Results Have Been Retrieved):
-
12let attributionResult = KVATracker.shared.attribution.resultprint("do something with attributionResult... \(attributionResult.kva_asForContextObject(withContext: .log) as! [AnyHashable: Any])")
-
1234KVAAttributionResult *attributionResult = KVATracker.shared.attribution.result;if (attributionResult != nil) {NSLog(@"do something with attributionResult...%@", [attributionResult kva_asForContextObjectWithContext:KVAContext.log]);}
-
123if let attributionDictionary = KVATracker.shared.attributionDictionary() {print("do something with attributionDictionary... \(attributionDictionary)")}
-
1234NSDictionary *attributionDictionary = KVATracker.shared.attributionDictionary;if (attributionDictionary != nil) {NSLog(@"do something with attributionDictionary...%@", attributionDictionary);}
The attribution results are provided as an NSDictionary which contains a decoded JSON object. If the attribution failed to attribute (e.g., false) an NSDictionary is created and returned which contains a key which indicates this. If the attribution results have not yet been retrieved, calling the attribution data getter will return nil rather than an NSDictionary object.
Developer API Reference:
constant kKVAParamRetrieveAttributionBoolKey
trackerParametersDictionary
KochavaTrackerDelegate
func attributionDictionary() -> String?
Getting The Free App Analytics Device ID:
Obtain the unique device identifier assigned by Free App Analytics.

If at any time after starting the tracker you would like to get the unique identifier assigned to this device by Kochava, this string (represented as a GUID) can be obtained by calling the device id getter.
Example (Getting the Kochava Device ID):
-
123if let kochavaDeviceIdString = KVATracker.shared.deviceIdString {print("do something with the kochavaDeviceIdString...\(kochavaDeviceIdString)")}
-
1234NSString *kochavaDeviceIdString = KVATracker.shared.deviceIdString;if (kochavaDeviceIdString != nil) {NSLog(@"do something with the kochavaDeviceIdString...%@", kochavaDeviceIdString);}
-
123if let kochavaDeviceIdString = KVATracker.shared.deviceIdString() {print("do something with the kochavaDeviceIdString...\(kochavaDeviceIdString)")}
-
1234NSString *kochavaDeviceIdString = KVATracker.shared.deviceIdString;if (kochavaDeviceIdString != nil) {NSLog(@"do something with the kochavaDeviceIdString...%@", kochavaDeviceIdString);}
NOTE: If you have enabled the Intelligent Consent Management feature, this unique device ID may change between consent status changes when consent is required and has not been granted.
Developer API Reference:
Enabling App Limit Ad Tracking:
Limit the ad tracking at the application level.

If you wish to limit ad tracking at the application level, with respect to Free App Analytics 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 (Enabling App Limit Ad Tracking During Tracker Configuration):
-
123// KVATracker.sharedKVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_")KVATracker.shared.appLimitAdTrackingBool = true
-
123// KVATracker.shared[KVATracker.shared startWithAppGUIDString:@"_YOUR_APP_GUID_"];KVATracker.shared.appLimitAdTrackingBool = YES;
-
12345678// parametersDictionarylet parametersDictionary: [AnyHashable: Any] = [kKVAParamAppGUIDStringKey: "_YOUR_APP_GUID_",kKVAParamAppLimitAdTrackingBoolKey: true]// KVATracker.sharedKVATracker.shared.configure(withParametersDictionary: parametersDictionary, delegate: nil)
-
12345678// parametersDictionaryNSDictionary *parametersDictionary = @{kKVAParamAppGUIDStringKey: @"_YOUR_APP_GUID_",kKVAParamAppLimitAdTrackingBoolKey: @(YES)};// KVATracker.shared[KVATracker.shared configureWithParametersDictionary:parametersDictionary delegate:nil];
Example (Enable App Limit Ad Tracking After Starting the Tracker):
-
1KVATracker.shared.appLimitAdTrackingBool = true
-
1KVATracker.shared.appLimitAdTrackingBool = YES;
-
1KVATracker.shared.setAppLimitAdTrackingBool(true)
-
1[KVATracker.shared setAppLimitAdTrackingBool:YES];
Developer API Reference:
constant kKVAParamAppLimitAdTrackingBoolKey
trackerParametersDictionary
func setAppLimitAdTrackingBool(_ appLimitAdTrackingBool: Bool)
Enabling Logging:
Enable logging output from the SDK.

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 Free App Analytics’ 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: none
No logging messages are generated. |
Log Level: error
Errors which are usually fatal to the tracker. |
Log Level: warn
Warnings which are not fatal to the tracker. |
Log Level: info
Minimal detail, such as tracker initialization. |
Log Level: debug
Granular detail, including network transaction payloads. |
Log Level: trace
Very granular detail, including low level behavior. |
To enable logging, set the desired log level during tracker configuration. When the tracker runs, each log message will be printed to your console log. In this example we’re setting a higher log level only when a debug build is detected, which is suggested but is not required.
Example (Configuring the Tracker with Testing Values):
-
1234567// KVALog#if DEBUGKVALog.shared.level = .trace#endif// KVATracker.sharedKVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_")
-
1234567// KVALog#if DEBUGKVALog.shared.level = KVALogLevel.trace;#endif// KVATracker.shared[KVATracker.shared startWithAppGUIDString:@"_YOUR_APP_GUID_"];
-
123456789// parametersDictionaryvar parametersDictionary: [AnyHashable: Any] = [:]#if DEBUGparametersDictionary[kKVAParamLogLevelEnumKey] = kKVALogLevelEnumTrace#endifparametersDictionary[kKVAParamAppGUIDStringKey] = "_YOUR_APP_GUID_"// KVATracker.sharedKVATracker.shared.configure(withParametersDictionary: parametersDictionary, delegate: nil)
-
123456789// parametersDictionaryNSMutableDictionary *parametersDictionary = NSMutableDictionary.dictionary;#if DEBUGparametersDictionary[kKVAParamLogLevelEnumKey] = kKVALogLevelEnumTrace;#endifparametersDictionary[kKVAParamAppGUIDStringKey] = @"_YOUR_APP_GUID_";// KVATracker.shared[KVATracker.shared configureWithParametersDictionary:parametersDictionary delegate:nil];
The log can be copied and viewed in the Xcode debug area which slides up from the bottom of Xcode. When the debug area is closed it can be opened using View > Debug Area > Show Debug Area. From there you can Hide the Variables View using the left-side indicating button and the log will fill the lower debug area.
NOTE: If you are copying a trace log for purposes of troubleshooting, please copy the entire log, as the SDK log entries often span multiple lines and copying only lines with “kochava” in them will result in missing log entries.
Developer API Reference:
Log Levels
constant kKVAParamLogLevelEnumKey
constant kKVAParamLogMultiLineBoolKey
trackerParametersDictionary
Sleeping the Tracker:
Delay the start of the tracker.

Placing the tracker into sleep mode, the tracker will enter a state where all non-essential network transactions will be held and persisted until the tracker is woken up. This can be useful if you wish to start the tracker early and begin tracking events but need the tracker to wait before sending events or the install data to Kochava servers.
For example, if you wanted the tracker startup process to have little or no impact on your own loading process, you might start the tracker in sleep mode during the app launch but wait while your app’s resource-intensive loading process completes. When the app’s loading process completes, the tracker 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 Tracker Configuration):
-
123456// KVATracker.shared// 1. If you want the tracker to sleep from the moment it is started, you should set var sleepBool immediately following (and within the same scope as) configuring the tracker. Provisions have been made within the iOS/tvOS SDK to avoid a race condition as long as this requirement is satisfied.KVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_")KVATracker.shared.sleepBool = true; // [1]//
-
12345// KVATracker.shared// 1. If you want the tracker to sleep from the moment it is started, you should set var sleepBool immediately following (and within the same scope as) configuring the tracker. Provisions have been made within the iOS/tvOS SDK to avoid a race condition as long as this requirement is satisfied.[KVATracker.shared startWithAppGUIDString:@"_YOUR_APP_GUID_"];KVATracker.shared.sleepBool = YES; // [1]
-
12345// KVATracker.shared// 1. If you want the tracker to sleep from the moment it is started, you should set var sleepBool immediately following (and within the same scope as) configuring the tracker. Provisions have been made within the iOS/tvOS SDK to avoid a race condition as long as this requirement is satisfied.KVATracker.shared.configure(withParametersDictionary: [kKVAParamAppGUIDStringKey: "_YOUR_APP_GUID_"], delegate: nil)KVATracker.shared.sleepBool = true; // [1]
-
12345// KVATracker.shared// 1. If you want the tracker to sleep from the moment it is started, you should set var sleepBool immediately following (and within the same scope as) configuring the tracker. Provisions have been made within the iOS/tvOS SDK to avoid a race condition as long as this requirement is satisfied.[KVATracker.shared configureWithParametersDictionary:@{ kKVAParamAppGUIDStringKey: @"_YOUR_APP_GUID_" } delegate:nil];KVATracker.shared.sleepBool = YES; // [1]
Example (Enabling Sleep Mode After Starting the Tracker):
-
1KVATracker.shared.sleepBool = YES;
-
1KVATracker.shared.sleepBool = true
-
1KVATracker.shared.sleepBool = true
-
1KVATracker.shared.sleepBool = YES;
Example (Waking the Tracker from Sleep Mode) —
-
1KVATracker.shared.sleepBool = false
-
1KVATracker.shared.sleepBool = NO;
-
1KVATracker.shared.sleepBool = false
-
1KVATracker.shared.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 tracker asleep permanently. This allows the tracker 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 tracker is never woken from sleep mode, events and other activity will continue to build up in the queue, causing undesirable results and resource usage.
Developer API Reference:
Shutting Down the Tracker:
Shutting down the SDK after starting.
The SDK can be shut down after starting, which will completely disable the SDK and stop all tracking from continuing.
The SDK should not be shutdown in typical scenarios, but this may be useful during consent-applicable instances when consent has been declined or revoked after starting the SDK and you wish to completely stop tracking the user.
After shutting down, all network communication with Kochava will cease, events will no longer queue, and any API calls will fail just as if the SDK had never been started. The SDK must be configured and started again if you wish for tracking to resume.
Example (Shut Down the SDK) —
-
12345// Option 1: shut down without deleting its local data.KVATrackerProduct.shared.shutdown(deleteLocalDataBool: false)..// Option 2: shut down and delete local data. WARNING: If local data is deleted the tracker will behave as a new install the next time it is started.KVATrackerProduct.shared.shutdown(deleteLocalDataBool: true)
-
12345// Option 1: shut down without deleting its local data.[KVATrackerProduct.shared shutdownWithDeleteLocalDataBool:false];..// Option 2: shut down and delete local data. WARNING: If local data is deleted the tracker will behave as a new install the next time it is started.[KVATrackerProduct.shared shutdownWithDeleteLocalDataBool:true];
-
12345// objective-c (shared instance)[KVATracker.shared invalidate];// swift (shared instance)KVATracker.shared.invalidate()
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 queue events and track the user, but will simply not send that data to Kochava until awoken. For this reason, Sleep() should not be used as a solution for consent-applicable situations because tracking is still queuing locally and may be sent on the next app launch.
Shutting down the SDK, on the other hand, completely stops the SDK from functioning and no tracking will occur until it is configured and started again.
Consent — GDPR:
Handle GDPR and consent requirements.
The Kochava SDK does deal with GDPR-sensitive data, such as device identifiers. Care should be taken to ensure that your use of the Kochava SDK remains GDPR compliant when applicable.
GDPR applies to users within the EU and requires users to opt-in to data collection. For common questions and answers regarding both GDPR and CCPA consent models, refer to our Handling Consent support documentation.
Intelligent Consent Manager:
As GDPR can present many challenges during development, Free App Analytics offers a fully managed solution for all your GDPR consent requirements through the use of our Intelligent Consent Manager feature. By using this feature the Kochava SDK will handle determining when consent is required for any user while shouldering much the GDPR burden for you. For more information on this feature, see: Intelligent Consent Manager
Self Managed Consent:

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 tracker should not occur until after you have determined consent is not required or consent has been granted. Any calls to the tracker should be wrapped in this check, so that if a user revokes consent later calls to the tracker will stop. Ensure that the tracker has been started before any calls to the tracker are made when going this route.
Example (Starting the Tracker Only When Consent Allows) —
-
12345if !consentRequiredBool || consentGrantedBool {// KVATracker.sharedKVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_")}
-
12345if (!consentRequiredBool || consentGrantedBool) {// KVATracker.shared[KVATracker.shared startWithAppGUIDString:@"_YOUR_APP_GUID_"];}
-
12345678910if !consentRequiredBool || consentGrantedBool {// parametersDictionarylet parametersDictionary: [AnyHashable: Any] = [kKVAParamAppGUIDStringKey: "_YOUR_APP_GUID_"]// KVATracker.sharedKVATracker.shared.configure(withParametersDictionary: parametersDictionary, delegate: nil)}
-
12345678910if (!consentRequiredBool || consentGrantedBool) {// parametersDictionaryNSDictionary *parametersDictionary = @{kKVAParamAppGUIDStringKey: @"_YOUR_APP_GUID_"};// KVATracker.shared[KVATracker.shared configureWithParametersDictionary:parametersDictionary delegate:nil];}
Example (Calling Tracker Methods Only When Consent Allows) —
-
123if !consentRequiredBool || consentGrantedBool {KVAEvent.sendCustom(withNameString: "My Event", infoString: nil)}
-
123if (!consentRequiredBool || consentGrantedBool) {[KVAEvent sendCustomWithNameString:@"My Event"];}
-
123if !consentRequiredBool || consentGrantedBool {KVATracker.shared.sendEvent(withNameString: "My Event", infoString: nil)}
-
123if (!consentRequiredBool || consentGrantedBool) {[KVATracker.shared sendEventWithNameString:@"My Event" infoString:nil];}
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 code and has also been setup in your Free App Analytics dashboard. Do not use these methods if you are handling consent on your own or through a 3rd party tool.
Consent — CCPA:
Handle CCPA and consent requirements.
CCPA applies to users within California and allows users to opt out of the sale of their data.
For purposes of CCPA, the Kochava Tracker SDK follows IAB’s CCPA Compliance Framework by reading the U.S. Privacy String from local storage, when present. The app or any entity within the app can set this string any time.
This means that if IAB’s U.S. Privacy String has been set within the app, the Kochava Tracker SDK will automatically adorn installs and post-install events with it’s value, when present. By doing so, CCPA consent status via the U.S. Privacy String can be associated with all user tracking and syndicated downstream for interested 3rd parties.
You do not need to take any action in the Kochava Tracker 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.
For more information regarding SDK-based solutions for CCPA, refer to our Handling Consent support documentation.
Intelligent Consent Manager:
Free App Analytics’ fully managed consent solution.

As GDPR can present many challenges during development, Free App Analytics offers a fully managed solution for all your GDPR consent requirements through the use of our Intelligent Consent Manager feature. By using this feature the Kochava SDK will handle determining when consent is required for any user while shouldering much the GDPR burden for you. For complete details on how this feature works, see: Intelligent Consent Manager.
Setting Up a Test Environment:
Create a test environment to ensure the integration is working properly.

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.
- Use an alternate testing App GUID so that your testing activities do not have an impact on your live app analytics.
- Enable Logging, if helpful, to gain insight into the SDK’s behavior during runtime.
- 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.
- Test your Free App Analytics 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.
-
12345678910111213141516171819// KVALoglet logLevel: KVALogLevel#if DEBUGlogLevel = .trace#elselogLevel = .info#endifKVALog.shared.level = logLevel// appGUIDStringlet appGUIDString: String#if DEBUGappGUIDString = "_YOUR_TEST_APP_GUID_"#elseappGUIDString = "_YOUR_PRODUCTION_APP_GUID_"#endif// KVATracker.sharedKVATracker.shared.start(withAppGUIDString: appGUIDString)
-
12345678910111213141516171819// KVALogKVALogLevel *logLevel;#if DEBUGlogLevel = KVALogLevel.trace;#elselogLevel = KVALogLevel.info;#endifKVALog.shared.level = logLevel;// appGUIDStringNSString *appGUIDString;#if DEBUGappGUIDString = @"_YOUR_TEST_APP_GUID_";#elseappGUIDString = @"_YOUR_PRODUCTION_APP_GUID_";#endif// KVATracker.shared[KVATracker.shared startWithAppGUIDString:appGUIDString];
-
123456789101112131415161718192021222324// logLevellet logLevel: Any#if DEBUGlogLevel = kKVALogLevelEnumTrace#elselogLevel = kKVALogLevelEnumInfo#endif// appGUIDStringlet appGUIDString: String#if DEBUGappGUIDString = "_YOUR_TEST_APP_GUID_"#elseappGUIDString = "_YOUR_PRODUCTION_APP_GUID_"#endif// parametersDictionarylet parametersDictionary: [AnyHashable: Any] = [kKVAParamLogLevelEnumKey: logLevel,kKVAParamAppGUIDStringKey: appGUIDString]// KVATracker.sharedKVATracker.shared.configure(withParametersDictionary: parametersDictionary, delegate: nil)
-
123456789101112131415161718192021222324// logLevelid logLevel;#if DEBUGlogLevel = kKVALogLevelEnumTrace;#elselogLevel = kKVALogLevelEnumInfo;#endif// appGUIDStringNSString *appGUIDString;#if DEBUGappGUIDString = @"_YOUR_TEST_APP_GUID_";#elseappGUIDString = @"_YOUR_PRODUCTION_APP_GUID_";#endif// parametersDictionaryNSDictionary *parametersDictionary = @{kKVAParamLogLevelEnumKey: logLevel,kKVAParamAppGUIDStringKey: appGUIDString};// KVATracker.shared[KVATracker.shared configureWithParametersDictionary:parametersDictionary delegate:nil];
Analyzing SDK Behavior:
While testing your integration, it is important to understand the tracker’s basic flow of operations. When the tracker is started the following sequence of events occur:
- A handshake with Free App Analytics may be made to determine dynamic settings for this app.
- If this is the first launch, the install data is sent to Free App Analytics (this only happens once).
- At this point the SDK is idle and awaits requests from the app.
- 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.
- When the app is terminated or suspended, a session-end payload may be sent to Free App Analytics.
- When the app is resumed or relaunched, a session-begin payload may be sent to Free App Analytics.
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 Free App Analytics analytics dashboard.
Developer API Reference:
constant kKVAParamLogLevelEnumKey
constant kKVAParamAppGUIDStringKey
trackerParametersDictionary
func configure(withParametersDictionary: [AnyHashable: Any], delegate: KochavaTrackerDelegate?)
Utilization from a Web View:
Interact with the Kochava SDK from within a native WebView.
In order to call methods and interact with the Kochava SDK from within a native WebView, see: SDK Utilization from a Web View.
Privacy and Restricting Data Collection:
How to prevent transmission of certain data from the device.
Example (Always restrict IDFA and IDFV egress) —
In this example, we want to prevent the IDFA and IDFV 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 datapoint keys “idfa” and “idfv”.
-
12345678910// start the trackerKVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_")// create the privacy profileKVAPrivacyProfile.register(withNameString: "restrict ids", payloadKeyStringArray: ["idfa", "idfv"])// enable the privacy profile for this userKVATracker.shared.privacy.setEnabledBool(forProfileNameString: "restrict ids", enabledBool: true)// ...for the remainder of this app launch, measurement signal from the SDK will not include the IDFA or IDFV
-
12345678910// start the tracker[KVATracker.shared startWithAppGUIDString:@"_YOUR_APP_GUID_"];// create the privacy profile[KVAPrivacyProfile registerWithNameString:@"restrict ids" payloadKeyStringArray:@[@"idfa", @"idfv"]];// enable the privacy profile for this user[KVATracker.shared.privacy setEnabledBoolForProfileNameString:@"restrict ids" enabledBool:YES];// ...for the remainder of this app launch, measurement signal from the SDK will not include the IDFA or IDFV
Example (Restrict IDFA and IDFV egress for children) —
In this example, we want to prevent the IDFA and IDFV 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.
-
1234567891011// determine if this user is a child using your own logiclet childBool = self.childBool()// start the trackerKVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_")// create a privacy profile for childrenKVAPrivacyProfile.register(withNameString: "child", payloadKeyStringArray: ["idfa", "idfv"])// enable the privacy profile only if this user is a childKVATracker.shared.privacy.setEnabledBool(forProfileNameString: "child", enabledBool: childBool)
-
1234567891011// determine if this user is a child using your own logiclet childBool = self.childBool()// start the tracker[KVATracker.shared startWithAppGUIDString:@"_YOUR_APP_GUID_"];// create the privacy profile[KVAPrivacyProfile registerWithNameString:@"child" payloadKeyStringArray:@[@"idfa", @"idfv"]];// enable the privacy profile only if this user is a child[KVATracker.shared.privacy setEnabledBoolForProfileNameString:@"child" enabledBool:childBool];
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.
1 2 3 4 5 6 7 8 9 10 |
// Start the tracker KVATracker.shared.start(withAppGUIDString: "_YOUR_APP_GUID_") // As soon as the tracker 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 KVAPrivacyProfile.register(withNameString: "restrictIDFA", payloadKeyStringArray: ["idfa"]) KVATracker.shared.privacy.setEnabledBool(forProfileNameString: "restrictIDFA", enabledBool: true) } |