Summary

The Unity SDK for Tenjin. To learn more about Tenjin and our product offering, please visit https://www.tenjin.com.

  • Please see our Release Notes to see detailed version history of changes.
  • Tenjin Unity SDK supports both iOS and Android.
  • Review the iOS and Android documentation and apply the proper platform settings to your builds.
  • For any issues or support, please contact: support@tenjin.com
  • iOS Notes:

    • Xcode 12 is required if using Unity iOS SDK v1.12.0 and higher.
    • When building iOS, confirm that these frameworks were automatically added to the Xcode build. If any are missing, you will need to add them manually.
      • AdSupport.framework
      • AppTrackingTransparency.framework
      • iAd.framework
      • StoreKit.framework
    • For AppTrackingTransparency, be sure update your project .plist file and add Privacy - Tracking Usage Description (NSUserTrackingUsageDescription) along with the text message you want to display to users.
  • Android Notes:

    1. If you have another SDK installed which already has Google Play Services installed or uses PlayServicesResolver, you may need to delete duplicate libraries:
     /Assets/Plugins/Android/play-services-ads-identifier--*.aar
     /Assets/Plugins/Android/play-services-basement---*.aar
    
    1. If you are using Tenjin Unity SDK alongside another SDK in Unity version > 2019, and are using Gradle to build the Android App, you might face build errors such as DuplicateMethodException etc., or find that referrer install is not working. If that is the case, please do the following:

      • Remove all the *.aar files from the Assets/Plugins/Android folder except tenjin.aar.
      • Add the following to your mainTemplate.gradle file:

             // Android Resolver Repos Start
             ([rootProject] + (rootProject.subprojects as List)).each { project ->
                 project.repositories {
                     def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/")
                     maven {
                         url "https://maven.google.com"
                     }
                     maven {
                         url "https://s3.amazonaws.com/moat-sdk-builds"
                     }
                     maven {
                         url 'https://developer.huawei.com/repo/'
                     }
                     mavenLocal()
                     jcenter()
                     mavenCentral()
                     google()
                 }
             }
        
         // Android Resolver Repos End
             apply plugin: 'com.android.library'
             **APPLY_PLUGINS**
             dependencies {
                 implementation fileTree(dir: 'libs', include: ['*.jar'])
             // Android Resolver Dependencies Start
                 implementation 'com.android.support:multidex:1.0.1'
                 implementation 'com.google.android.gms:play-services-analytics:{version}'
                 implementation 'com.android.installreferrer:installreferrer:{version}'
                 implementation 'com.huawei.hms:ads-identifier:{version}'
                 implementation 'com.huawei.hms:ads-installreferrer:{version}'
                 androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
                     exclude group: 'com.android.support', module: 'support-annotations'
                 })
             // Android Resolver Dependencies End
             **DEPS**}
        
        • Add the following entry to the gradleTemplate.properties file:
          android.useAndroidX=true
          
    2. If you see the following errors on the app initialization, move tenjin.aar file from /Assets/Plugins/Android/Tenjin/libs to /Assets/Plugins/Android/. Also check the Proguard Settings here.

      AndroidJavaException: java.lang.NoSuchMethodError: no static method with name='setWrapperVersion'
      

      or

      AndroidJavaException: java.lang.ClassNotFoundException: com.tenjin.android.TenjinSDK
      

Table of contents

SDK Integration

  1. Download the latest Unity SDK from here.

  2. Import the TenjinUnityPackage.unitypackage into your project: Assets -> Import Package.

  3. By default, we have included Google Play Services AAR files as part of our SDK. If you do not plan on using Google Play Services, you can delete these AAR files:

 /Assets/Plugins/Android/play-services-*.aar
 /Assets/Plugins/Android/installreferrer-*.aar

OAID and other Android App Stores

Tenjin supports promoting on other Android App Stores using the Android OAID. We have the following options for integrating OAID libraries. If you plan to develop app outside of Google Play, make sure to implement these OAID libraries.

MSA OAID (In China)

For integration with the MSA libary, download the following oaid_sdk_1.0.25.aar.

Place the oaid_sdk_1.0.25.aar file in your project's Android libs directory: /Assets/Plugins/Android

Set your App Store Type value to other:

BaseTenjin instance = Tenjin.getInstance("<API_KEY>");

instance.SetAppStoreType(AppStoreType.other);

Huawei OAID (Outside China)

For integration with the Huawei OAID libary, download the following Huawei AAR file: huawei-ads-identifier.aar. If your app is in the Huawei App Gallery, download and add the Huawei Install Referrer file: huawei-ads-installreferrer.aar.

Place the Huawei files in your project's Android libs directory: /Assets/Plugins/Android

Set your App Store Type value to other:

BaseTenjin instance = Tenjin.getInstance("<API_KEY>");

instance.SetAppStoreType(AppStoreType.other);

Proguard Settings

-keep class com.tenjin.** { *; }
-keep public class com.google.android.gms.ads.identifier.** { *; }
-keep public class com.google.android.gms.common.** { *; }
-keep public class com.android.installreferrer.** { *; }
-keep class * extends java.util.ListResourceBundle {
    protected Object[][] getContents();
}
-keepattributes *Annotation*

If you are using Huawei libraries, you can to use these setttings:

-keep class com.huawei.hms.ads.** { *; }
-keep interface com.huawei.hms.ads.** { *; }

App Initialization

  1. Get your <API_KEY> from your Tenjin dashboard.
  2. In your project's first Start() method add the following line of code. Also add to OnApplicationPause() if you want to send sessions data when a user resumes using the app from the background.
using UnityEngine;
using System.Collections;

public class TenjinExampleScript : MonoBehaviour {

  void Start() {
    TenjinConnect();
  }

  void OnApplicationPause(bool pauseStatus) {
    if (!pauseStatus) {
      TenjinConnect();
    }
  }

  public void TenjinConnect() {
    BaseTenjin instance = Tenjin.getInstance("<API_KEY>");

    // Sends install/open event to Tenjin
    instance.Connect();
  }
}

App Store

By default, Google Play is the default App Store. If you are publishing in a different App Store, update to the appropriate TenjinSDK.AppStoreType.* value:

BaseTenjin instance = Tenjin.getInstance("<API_KEY>");

instance.SetAppStoreType(AppStoreType.{{SET_APP_STORE_TYPE_VALUE}});

Current AppStoreType options:

  • googleplay
  • amazon
  • other

ATTrackingManager (iOS)

  • Starting with iOS 14, you have the option to show the initial ATTrackingManager permissions prompt and selection to opt in/opt out users.

  • If the device doesn't accept tracking permission, IDFA will become zero. If the device accepts tracking permission, the Connect() method will send the IDFA to our servers.

  • You can also still call Tenjin connect(), without using ATTrackingManager. ATTrackingManager permissions prompt is not obligatory until the early spring of 2021.

using UnityEngine;
using System.Collections;
using UnityEngine.iOS;

public class TenjinExampleScript : MonoBehaviour {

    void Start() {
      TenjinConnect();
    }

    void OnApplicationPause(bool pauseStatus) {
      if (!pauseStatus) {
        TenjinConnect();
      }
    }

    public void TenjinConnect() {
      BaseTenjin instance = Tenjin.getInstance("API_KEY");

#if UNITY_IOS
      if (new Version(Device.systemVersion).CompareTo(new Version("14.0")) >= 0) {
        // Tenjin wrapper for requestTrackingAuthorization
        instance.RequestTrackingAuthorizationWithCompletionHandler((status) => {
          Debug.Log("===> App Tracking Transparency Authorization Status: " + status);

          // Sends install/open event to Tenjin
          instance.Connect();

        });
      }
      else {
          instance.Connect();
      }
#elif UNITY_ANDROID

      // Sends install/open event to Tenjin
      instance.Connect();

#endif
    }
}

SKAdNetwork and Conversion Values

As part of SKAdNetwork, we created wrapper methods for registerAppForAdNetworkAttribution() and updateConversionValue(_:). Our methods will register the equivalent SKAdNetwork methods and also send the conversion values on our servers.

updateConversionValue(_:) 6 bit value should correspond to the in-app event and shouldn't be entered as binary representation but 0-63 integer. Our server will reject any invalid values.

using UnityEngine;
using System.Collections;

public class TenjinExampleScript : MonoBehaviour {

    void Start() {
      TenjinConnect();
    }

    void OnApplicationPause(bool pauseStatus) {
      if (!pauseStatus) {
        TenjinConnect();
      }
    }

    public void TenjinConnect() {
      BaseTenjin instance = Tenjin.getInstance("API_KEY");

#if UNITY_IOS

      // Registers SKAdNetwork app for attribution
      instance.RegisterAppForAdNetworkAttribution();

      // Sends install/open event to Tenjin
      instance.Connect();

      // Sets SKAdNetwork Conversion Value
      // You will need to use a value between 0-63 for <YOUR 6 bit value>
      instance.UpdateConversionValue(<your 6 bit value>);

#elif UNITY_ANDROID

      // Sends install/open event to Tenjin
      instance.Connect();

#endif
    }
}

GDPR

As part of GDPR compliance, with Tenjin's SDK you can opt-in, opt-out devices/users, or select which specific device-related params to opt-in or opt-out. OptOut() will not send any API requests to Tenjin and we will not process any events.

To opt-in/opt-out:

void Start () {

  BaseTenjin instance = Tenjin.getInstance("API_KEY");

  boolean userOptIn = CheckOptInValue();

  if (userOptIn) {
    instance.OptIn();
  }
  else {
    instance.OptOut();
  }

  instance.Connect();
}

boolean CheckOptInValue()
{
  // check opt-in value
  // return true; // if user opted-in
  return false;
}
  • To opt-in/opt-out specific device-related parameters, you can use the OptInParams() or OptOutParams().

  • OptInParams() will only send device-related parameters that are specified. OptOutParams() will send all device-related parameters except ones that are specified.

  • Please note that we require the following parameters to properly track devices in Tenjin's system:

    • ip_address
    • advertising_id
    • limit_ad_tracking
    • referrer (Android)
    • iad (iOS)
  • If you are targeting IMEI and/or OAID Ad Networks, add:

    • imei
    • oaid
  • If you plan on using Google Ad Words, you will also need to add:

    • platform
    • os_version
    • locale
    • device_model
    • build_id

If you want to only get specific device-related parameters, use OptInParams(). In example below, we will only these device-related parameters: ip_address, advertising_id, developer_device_id, limit_ad_tracking, referrer, and iad:

BaseTenjin instance = Tenjin.getInstance("API_KEY");

List<string> optInParams = new List<string> {"ip_address", "advertising_id", "developer_device_id", "limit_ad_tracking", "referrer", "iad"};
instance.OptInParams(optInParams);

instance.Connect();

If you want to send ALL parameters except specfic device-related parameters, use OptOutParams(). In example below, we will send ALL device-related parameters except: locale, timezone, and build_id parameters.

BaseTenjin instance = Tenjin.getInstance("API_KEY");

List<string> optOutParams = new List<string> {"locale", "timezone", "build_id"};
instance.OptOutParams(optOutParams);

instance.Connect();
Param Description Platform Reference
ip_address IP Address All
advertising_id Device Advertising ID All Android>), iOS
developer_device_id ID for Vendor iOS iOS
oaid Open Advertising ID Android Android
imei Device IMEI Android Android>)
limit_ad_tracking limit ad tracking enabled All Android>), iOS
platform platform All iOS or Android
referrer Google Play Install Referrer Android Android
iad Apple Search Ad parameters iOS iOS
os_version operating system version All Android, iOS
device device name All Android, iOS (hw.machine)
device_manufacturer device manufactuer Android Android
device_model device model All Android, iOS (hw.model)
device_brand device brand Android Android
device_product device product Android Android
device_model_name device machine iOS iOS (hw.model)
device_cpu device cpu name iOS iOS (hw.cputype)
carrier phone carrier Android Android>)
connection_type cellular or wifi Android Android>)
screen_width device screen width Android Android
screen_height device screen height Android Android
os_version_release operating system version All Android, iOS
build_id build ID All Android, iOS (kern.osversion)
locale device locale All Android>), iOS
country locale country All Android>), iOS
timezone timezone All Android, iOS


Purchase Events

iOS IAP Validation

iOS receipt validation requires transactionId and receipt (signature will be set to null). For receipt, be sure to send the receipt Payload(the base64 encoded ASN.1 receipt) from Unity.

IMPORTANT: If you have subscription IAP, you will need to add your app's shared secret in the Tenjin dashboard. You can retreive your iOS App-Specific Shared Secret from the iTunes Connect Console > Select your app > Features > In-App Purchases > App-Specific Shared Secret.

Android IAP Validation

Android receipt validation requires receipt and signature are required (transactionId is set to null).

IMPORTANT: You will need to add your app's public key in the Tenjin dashboard. You can retreive your Base64-encoded RSA public key from the Google Play Developer Console > Select your app > Development Tools > Services & APIs. Please note that for Android, we currently only support IAP transactions from Google Play.

iOS and Android IAP Example:

In the example below, we are using the widely used MiniJSON library for JSON deserializing.

  public static void OnProcessPurchase(PurchaseEventArgs purchaseEventArgs) {
    var price = purchaseEventArgs.purchasedProduct.metadata.localizedPrice;
    double lPrice = decimal.ToDouble(price);
    var currencyCode = purchaseEventArgs.purchasedProduct.metadata.isoCurrencyCode;

    var wrapper = Json.Deserialize(purchaseEventArgs.purchasedProduct.receipt) as Dictionary<string, object>;  // https://gist.github.com/darktable/1411710
    if (null == wrapper) {
        return;
    }

    var payload   = (string)wrapper["Payload"]; // For Apple this will be the base64 encoded ASN.1 receipt
    var productId = purchaseEventArgs.purchasedProduct.definition.id;

#if UNITY_ANDROID

  var gpDetails = Json.Deserialize(payload) as Dictionary<string, object>;
  var gpJson    = (string)gpDetails["json"];
  var gpSig     = (string)gpDetails["signature"];

  CompletedAndroidPurchase(productId, currencyCode, 1, lPrice, gpJson, gpSig);

#elif UNITY_IOS

  var transactionId = purchaseEventArgs.purchasedProduct.transactionID;

  CompletedIosPurchase(productId, currencyCode, 1, lPrice , transactionId, payload);

#endif

  }

  private static void CompletedAndroidPurchase(string ProductId, string CurrencyCode, int Quantity, double UnitPrice, string Receipt, string Signature)
  {
      BaseTenjin instance = Tenjin.getInstance("API_KEY");
      instance.Transaction(ProductId, CurrencyCode, Quantity, UnitPrice, null, Receipt, Signature);
  }

  private static void CompletedIosPurchase(string ProductId, string CurrencyCode, int Quantity, double UnitPrice, string TransactionId, string Receipt)
  {
      BaseTenjin instance = Tenjin.getInstance("API_KEY");
      instance.Transaction(ProductId, CurrencyCode, Quantity, UnitPrice, TransactionId, Receipt, null);
  }

Disclaimer: If you are implementing purchase events on Tenjin for the first time, make sure to verify the data with other tools you’re using before you start scaling up you user acquisition campaigns using purchase data.

Subscription IAP

  • You are responsible to send subscription transaction one time during each subscription interval (i.e. For example, for a monthly subscription, you will need to send us 1 transaction per month). In the example timeline below, a transaction event should only be sent at the "First Charge" and "Renewal" events. During the trial period, do not send Tenjin the transaction event.

  • Tenjin does not de-dupe duplicate transactions.

  • If you have iOS subscription IAP, you will need to add your app's public key in the Tenjin dashboard. You can retreive your iOS App-Specific Shared Secret from the iTunes Connect Console > Select your app > Features > In-App Purchases > App-Specific Shared Secret.

  • For more information on iOS subscriptions, please see: Apple documentation on Working with Subscriptions

  • For more information on Android subscriptions, please see: Google Play Billing subscriptions documentation

Custom Events

IMPORTANT: Limit custom event names to less than 80 characters. Do not exceed 500 unique custom event names.

  • Include the Assets folder in your Unity project
  • In your projects method for the custom event write the following for a named event: Tenjin.getInstance("<API_KEY>").SendEvent("name") and the following for a named event with an integer value: Tenjin.getInstance("<API_KEY>").SendEvent("nameWithValue","value")
  • Make sure value passed is an integer. If value is not an integer, your event will not be passed.

Here's an example of the code:

void MethodWithCustomEvent(){
    //event with name
    BaseTenjin instance = Tenjin.getInstance ("API_KEY");
    instance.SendEvent("name");

    //event with name and integer value
    instance.SendEvent("nameWithValue", "value");
}

.SendEvent("name") is for events that are static markers or milestones. This would include things like tutorial_complete, registration, or level_1.

.SendEvent("name", "value") is for events that you want to do math on a property of that event. For example, ("coins_purchased", "100") will let you analyze a sum or average of the coins that have been purchased for that event.

Tenjin supports the ability to direct users to a specific part of your app after a new attributed install via Tenjin's campaign tracking URLs. You can utilize the GetDeeplink method and callback to access the deferred deeplink through the data object. To test you can follow the instructions found here.


public class TenjinExampleScript : MonoBehaviour {

  // Use this for initialization
  void Start () {
    BaseTenjin instance = Tenjin.getInstance("API_KEY");
    instance.Connect();
    instance.GetDeeplink(DeferredDeeplinkCallback);
  }

  public void DeferredDeeplinkCallback(Dictionary<string, string> data) {
    bool clicked_tenjin_link = false;
    bool is_first_session = false;

    if (data.ContainsKey("clicked_tenjin_link")) {
      //clicked_tenjin_link is a BOOL to handle if a user clicked on a tenjin link
      clicked_tenjin_link = (data["clicked_tenjin_link"].ToLower() == "true");
      Debug.Log("===> DeferredDeeplinkCallback ---> clicked_tenjin_link: " + clicked_tenjin_link);
    }

    if (data.ContainsKey("is_first_session")) {
      //is_first_session is a BOOL to handle if this session for this user is the first session
      is_first_session = (data["is_first_session"].ToLower() == "true");
      Debug.Log("===> DeferredDeeplinkCallback ---> is_first_session: " + is_first_session);
    }

    if (data.ContainsKey("ad_network")) {
      //ad_network is a STRING that returns the name of the ad network
      Debug.Log("===> DeferredDeeplinkCallback ---> adNetwork: " + data["ad_network"]);
    }

    if (data.ContainsKey("campaign_id")) {
      //campaign_id is a STRING that returns the tenjin campaign id
      Debug.Log("===> DeferredDeeplinkCallback ---> campaignId: " + data["campaign_id"]);
    }

    if (data.ContainsKey("advertising_id")) {
      //advertising_id is a STRING that returns the advertising_id of the user
      Debug.Log("===> DeferredDeeplinkCallback ---> advertisingId: " + data["advertising_id"]);
    }

    if (data.ContainsKey("deferred_deeplink_url")) {
      //deferred_deeplink_url is a STRING that returns the deferred_deeplink of the campaign
      Debug.Log("===> DeferredDeeplinkCallback ---> deferredDeeplink: " + data["deferred_deeplink_url"]);
    }

    if (clicked_tenjin_link && is_first_session) {
      //use the deferred_deeplink_url to direct the user to a specific part of your app
      if (String.IsNullOrEmpty(data["deferred_deeplink_url"]) == false) {
      }
    }
  }

}

Below are the parameters, if available, that are returned in the deferred deeplink callback:

Parameter Description
advertising_id Advertising ID of the device
ad_network Ad network of the campaign
campaign_id Tenjin campaign ID
campaign_name Tenjin campaign name
site_id Site ID of source app
referrer The referrer params from the app store
deferred_deeplink_url The deferred deep-link of the campaign
clicked_tenjin_link Boolean representing if the device was tracked by Tenjin
is_first_session Boolean representing if this is the first session for the device

Server-to-server integration

Tenjin offers server-to-server integration. If you want to access to the documentation, please send email to support@tenjin.com.

App Subversion parameter for A/B Testing (requires DataVault)

If you are running A/B tests and want to report the differences, we can append a numeric value to your app version using the AppendAppSubversion() method. For example, if your app version 1.0.1, and set AppendAppSubversion(8888), it will report app version as 1.0.1.8888.

This data will appear within DataVault where you will be able to run reports using the app subversion values.

BaseTenjin instance = Tenjin.getInstance("<API KEY>");
instance.AppendAppSubversion(8888);
instance.Connect();

Testing

You can verify if the integration is working through our Live Test Device Data Tool. Add your advertising_id or IDFA/GAID to the list of test devices. You can find this under Support -> Test Devices. Go to the SDK Live page and send a test events from your app. You should see a live events come in:




results matching ""

    No results matching ""