Advanced guide
1. Set user IDs
By default, the SDK instance uses a random UUID as each user's default distinct ID, which serves as the user's identifier when they are not logged in. Note that the distinct ID changes when the user reinstalls the app or switches devices.
1.1 Set the distinct ID
In general, you don't need to customize the distinct ID. Make sure you understand the user identification rules before you set a distinct ID.
If you need to replace the distinct ID, call the method immediately after the SDK is initialized. Do not call it multiple times, to avoid creating useless accounts.
If your app has its own distinct ID management system for each user, you can call SetDistinctId to set the distinct ID:
// Set the distinct ID to Thinker
TDAnalytics.SetDistinctId("Thinker");
To get the current distinct ID, you can call GetDistinctId:
//Return the distinct ID
String distinctId = TDAnalytics.GetDistinctId();
1.2 Set the account ID
When a user logs in, you can call login to set the user's account ID. The AE platform uses the account ID as the identifier, and the account ID you set is kept until logout is called. Calling login multiple times overwrites the previous account ID.
// The unique login identifier of the user, which corresponds to #account_id in the reported data. In this case, the value of #account_id is TA
TDAnalytics.Login("TA");
This method does not upload a login event
1.3 Clear the account ID
After a user logs out, you can call Logout to clear the account ID. Until Login is called again, the distinct ID is used as the identifier.
TDAnalytics.Logout();
We recommend calling Logout only on explicit logout events, for example when the user deletes their account, rather than when the app is closed.
This method does not upload a logout event
2. Send events
After the SDK is initialized, you can start tracking data to collect user behavior. In most cases, regular events meet business needs. You can also use first events, updatable events, and other event types based on your actual business scenarios.
2.1 Regular events
You can call Track to upload events. We recommend setting event properties and the conditions for sending events according to the document you prepared earlier. The following example tracks a user purchasing a product:
Dictionary<string, object> properties = new Dictionary<string, object>(){
{"product_name", "Product Name"}
};
TDAnalytics.Track("product_buy", properties);
2.2 First events
A first event is an event that is recorded only once for a given device or an ID of another dimension. For example, in some scenarios you may want to record the first event that occurs on a device; you can report this data as a first event.
Dictionary<string, object> properties = new Dictionary<string, object>() {
{ "status", 1}
};
TDFirstEventModel firstEvent = new TDFirstEventModel("first_event");
firstEvent.Properties = properties;
TDAnalytics.Track(firstEvent);
If you want to determine whether an event is the first one based on a dimension other than the device, you can customize first_check_id for the first event:
// Set the user ID as the first_check_id of the first event to track first-time user activation
Dictionary<string, object> properties = new Dictionary<string, object>() {
{ "status", 1}
};
TDFirstEventModel firstEvent = new TDFirstEventModel("first_event", "any-user-id");
firstEvent.Properties = properties;
TDAnalytics.Track(firstEvent);
Note: Because the first-event check is performed on the server, first events are stored with a 1-hour delay by default.
2.3 Updatable events
You can use updatable events to modify event data in specific scenarios. An updatable event requires an ID that identifies the event, which you pass in when you create the updatable event object. The AE backend determines which data to update based on the event name and event ID.
// Example: report an updatable event, assuming the event name is UPDATABLE_EVENT
// After reporting, the event property status is 3 and price is 100
TDUpdatableEventModel updatableEvent = new TDUpdatableEventModel("UPDATABLE_EVENT", "test_event_id");
updatableEvent.Properties = new Dictionary<string, object>{
{"status", 3},
{"price", 100}
};
TDAnalytics.Track(updatableEvent);
// After reporting, the event property status is updated to 5 and price is unchanged
TDUpdatableEventModel updatableEvent_new = new TDUpdatableEventModel("UPDATABLE_EVENT", "test_event_id");
updatableEvent_new.Properties = new Dictionary<string, object>{
{"status", 5}
};
TDAnalytics.Track(updatableEvent_new);
2.4 Overwritable events
Overwritable events are similar to updatable events, except that an overwritable event completely overwrites historical data with the latest data. In effect, the previous record is deleted and the latest data is ingested. The AE backend determines which data to update based on the event name and event ID.
// Example: report an overwritable event, assuming the event name is OVERWRITABLE_EVENT
// After reporting, the event property status is 3 and price is 100
TDOverwritableEventModel overWritableEvent = new TDOverwritableEventModel("OVERWRITABLE_EVENT", "test_event_id");
overWritableEvent.Properties = new Dictionary<string, object>{
{"status", 3},
{"price", 100}
};
TDAnalytics.Track(overWritableEvent);
// After reporting, the event property status is updated to 5 and the price property is deleted
TDOverwritableEventModel overWritableEvent_new = new TDOverwritableEventModel("OVERWRITABLE_EVENT", "test_event_id");
overWritableEvent_new.Properties = new Dictionary<string, object>{
{"status", 5}
};
TDAnalytics.Track(overWritableEvent_new);
2.5 Super properties
Super properties are properties that are uploaded with every event. Based on how often they are updated, super properties are divided into static super properties and dynamic super properties. You can choose different ways to set super properties based on your business scenarios; we recommend setting super properties before sending events. For the same event, when a super property, a custom event property, and a preset property have the same key, values are assigned in the following order of priority: custom properties > dynamic super properties > static super properties > preset properties.
2.5.1 Static super properties
Static super properties are properties that change infrequently and are carried by every event, such as a user's membership level. After you set static super properties through setSuperProperties, the SDK adds them to each event as event properties when the event is collected.
Dictionary<string, object> superProperties = new Dictionary<string, object>(){
{"vip_level", 2}
};
TDAnalytics.SetSuperProperties(superProperties);
Static super properties are saved in the cache, so you don't need to call this every time the app starts. If a property already exists, the new value overwrites the existing value; if the property did not exist before, it is created. Besides setting properties, we also provide other APIs for working with static super properties to meet day-to-day business needs.
// Clear the super property named CHANNEL
TDAnalytics.UnsetSuperProperty("CHANNEL");
// Clear all super properties
TDAnalytics.ClearSuperProperties();
// Get all super properties
TDAnalytics.GetSuperProperties();
2.5.2 Dynamic super properties
Dynamic super properties are properties that change frequently and are carried by every event, such as the user's coin count. To set dynamic super properties, first create a dynamic super properties class that implements the TDDynamicSuperPropertiesHandler interface and override the public Dictionary<string, object> GetDynamicSuperProperties() method. The return value of this method is the dynamic super properties to set. Then call SetDynamicSuperProperties and pass in the dynamic super properties object. Example:
// 1. Define the dynamic super properties implementation. This example sets a dynamically changing coin count
public class DynamicProp : TDDynamicSuperPropertiesHandler
{
int coin = 0;
public Dictionary<string, object> GetDynamicSuperProperties()
{
coin++;
return new Dictionary<string, object>() {
{"coin",coin}
};
}
}
// 2. Set dynamic super properties
TDAnalytics.SetDynamicSuperProperties(new DynamicProp());
2.6 Track event duration
To record the duration of an event, call TimeEvent to start timing and specify the name of the event you want to time. When you upload that event, the #duration property is automatically added to the event properties to indicate the recorded duration, in seconds. Note that only one timing task can run for the same event name.
//The following example measures how long a user stays on a product page
//The user enters the product page and timing starts
TDAnalytics.TimeEvent("stay_shop");
/**do someting
.......
**/
//The user leaves the product page and timing ends. The "stay_shop" event will carry the #duration property that indicates the event duration
TDAnalytics.Track("stay_shop");
3. User properties
The user property APIs supported by the AE platform are UserSet, UserSetOnce, UserAdd, UserUnset, UserDelete, UserAppend, and UserUniqAppend.
3.1 UserSet
For general user properties, you can call UserSet to set them. Properties uploaded through this API overwrite the existing property values. If the user property did not exist before, it is created.
//user_name is TA at this point
TDAnalytics.UserSet(new Dictionary<string, object>(){
{"user_name", "TA"}
});
//user_name is AE at this point
TDAnalytics.UserSet(new Dictionary<string, object>(){
{"user_name", "AE"}
});
3.2 UserSetOnce
If a user property only needs to be set once, you can call UserSetOnce to set it. If the property already has a value, this call is ignored:
//first_payment_time is 2018-01-01 01:23:45.678
TDAnalytics.UserSetOnce(new Dictionary<string, object>(){
{"first_payment_time","2018-01-01 01:23:45.678"}
});
//first_payment_time is still 2018-01-01 01:23:45.678
TDAnalytics.UserSetOnce(new Dictionary<string, object>(){
{"first_payment_time","2018-12-31 01:23:45.678"}
});
3.3 UserAdd
When you upload a numeric property, you can call UserAdd to accumulate its value. If the property has not been set yet, it is assigned 0 before the calculation. You can pass a negative value, which is equivalent to subtraction.
//total_revenue is now 30
TDAnalytics.UserAdd(new Dictionary<string, object>(){
{"total_revenue",30}
});
//total_revenue is now 678
TDAnalytics.UserAdd(new Dictionary<string, object>(){
{"total_revenue",648}
});
The property key is a string, and the Value can only be a number.
3.4 UserUnset
If you need to reset a property of a user, you can call UserUnset to clear the value of the specified user property. This API accepts a string or a list as the parameter:
// Delete a single user property
TDAnalytics.UserUnset("userPropertyName");
// Delete multiple user properties
List<string> listProps = new List<string>();
listProps.Add("aaa");
listProps.Add("bbb");
listProps.Add("ccc");
TDAnalytics.UserUnset(listProps);
UserUnset: the value passed in is the key of the property to be cleared.
3.5 UserDelete
To delete a user, you can call UserDelete. After that, you can no longer query this user's user properties, but the events generated by the user can still be queried.
TDAnalytics.UserDelete();
3.6 UserAppend
Starting from v1.4.0, you can call UserAppend to append elements to user properties of the List type:
List<string> stringList = new List<string>();
stringList.Add("apple");
stringList.Add("ball");
// Append 2 elements to the user property named user_list
TDAnalytics.UserAppend(new Dictionary<string, object>{
{"user_list", stringList }
});
3.7 UserUniqAppend
Starting from v2.4.0, you can call UserUniqAppend to append elements with deduplication to user properties of the List type. Calling UserUniqAppend deduplicates the appended user property values, whereas the UserAppend API doesn't deduplicate, so the user property may contain duplicates.
//The value of user_list is now ["apple","ball"]
List<string> stringList = new List<string>();
stringList.Add("apple");
stringList.Add("ball");
TDAnalytics.UserAppend(new Dictionary<string, object>{
{"user_list", stringList}
});
List<string> stringList1 = new List<string>();
stringList1.Add("apple");
stringList1.Add("cube");
//The value of user_list is now ["apple","apple","ball","cube"]
TDAnalytics.UserAppend(new Dictionary<string, object>{
{"user_list", stringList1}
});
//The value of user_list is now ["apple","ball","cube"]
TDAnalytics.UserUniqAppend(new Dictionary<string, object>{
{"user_list", stringList1}
});
4. Encryption
Starting from v2.4.0, the SDK supports encrypting data with AES+RSA. Data encryption requires cooperation between the client and the server. For details, contact your customer success representative.
Call the EnableEncrypt method of TDConfig and pass in the public key and the default version number.
TDConfig tdConfig = new TDConfig(appId, serverUrl);
// Enable encrypted transmission (iOS/Android only), and set the default version number and public key
tdConfig.EnableEncrypt("YOUR_ENCRYPT_PUBLIC_KEY", 1);
TDAnalytics.Init(tdConfig);
5. Other features
5.1 Get the device ID
You can call GetDeviceId to get the device ID:
TDAnalytics.GetDeviceId();
// Use the device ID as the distinct ID
// TDAnalytics.SetDistinctId(TDAnalytics.GetDeviceId());
5.2 Set the default time zone
By default, the SDK reports the local device time at the moment of the API call as the event time. You can also specify a default time zone through the API for setting the default time zone, so that the event time of all events is aligned to the time zone you set:
TDConfig tdConfig = new TDConfig(appId, serverUrl);
tdConfig.timeZone = TDTimeZone.UTC;
TDAnalytics.Init(tdConfig);
Aligning the event time to a specified time zone discards the device's local time zone information. To keep the device's local time zone information, you currently need to add the relevant property to events yourself.
5.3 Calibrate time
By default, the SDK uses the local device time as the event time. If users manually change the device time, your business analysis may be affected. In this case, you can calibrate the time to keep the event time accurate. We provide two time calibration methods: timestamp, NTP.
- You can calibrate the SDK time with the current timestamp obtained from the server. After that, all calls that don't specify a time, including event data and user property operations, use the calibrated time as the time of occurrence.
// 1585633785954 is the current Unix timestamp in milliseconds, corresponding to Beijing time 2020-03-31 13:49:45
TDAnalytics.CalibrateTime(1585633785954);
- You can also set an NTP server address. The SDK then tries to get the current time from that NTP server and calibrates the SDK time. If no correct response is received within the default timeout (3 seconds), data is reported with the local time afterward.
// Use Apple's NTP service to calibrate the time
TDAnalytics.CalibrateTimeWithNtp("time.apple.com");
1. Time calibration with an NTP service involves some uncertainty. We recommend calibrating with a timestamp whenever possible.
2. Choose your NTP server address carefully so that user devices can quickly get the server time when the network is in good condition.
5.4 Report data immediately
In some business scenarios, if you want data to be reported to the AE server immediately, you can call the Flush API
TDAnalytics.Flush();
5.5 Get the country/region code
In some business scenarios, if you need to know the country/region code of the user's device, you can get it through GetLocalRegion
TDAnalytics.GetLocalRegion();
5.6 Call from Lua
To call the SDK directly from Lua files, you can use the wrapped Lua API. Download
After downloading, import TDAnalytics.lua and TDAnalyticsProxy.cs into your project.
Usage example:
local config = {
appId = "AppId",
serverUrl = "ServerUrl",
enableLog = true, -- Whether to enable logging. Defaults to false--
mode = 'debug' -- Defaults to normal--
}
--Initialize the SDK--
TDAnalytics.init(config);
--If the user has logged in, you can set the user's account ID as the unique identifier
TDAnalytics.login("TA")
--After super properties are set, every event carries them
local superProperties = {}
superProperties["channel"] = "ta" -- String
superProperties["age"] = 1 -- Number
superProperties["isSuccess"] = true -- Boolean
superProperties["birthday"] = os.date("%Y-%m-%d %H:%M:%S") -- Time
superProperties["object"] = { key="value" } -- Object
superProperties["object_arr"] = { { key="value" } } -- Object group
superProperties["arr"] = { "value" } -- Array
TDAnalytics.setSuperProperties(superProperties) -- Set super properties
--Send an event
TDAnalytics.track("product_buy", {
product_name="Product Name"
});
--Set user properties
TDAnalytics.userSet({
user_name = "TE"
})
5.7 Auto-tracked events for WeChat mini games
On the WeChat mini game platform, auto-tracking of the show, hide, and launch events is currently supported. To integrate:
- Download the WeChat mini game plugin
Menu bar: Window->Package Manager-> + -> Add package from git url
PackageManager (git installation URL): https://github.com/wechat-miniprogram/minigame-tuanjie-transform-sdk.git
- Define a custom macro
Menu bar: Edit -> Project Settings -> Scripting Define Symbols
Add the global macro TD_WEIXIN_GAME_MODE
Click Apply to complete the setup
- Add a dependency to the assembly
In the Project window: ThinkingAnalytics folder -> TDAnalytics(Assembly Definition) -> Assembly Definition References -> + -> WxWasmSDKRuntime
Usage example:
// Enable auto-tracked events: AppStart tracks ta_mg_show, AppEnd tracks ta_mg_hide, and AppInstall tracks ta_mg_launch
TDAnalytics.EnableAutoTrack(TDAutoTrackEventType.AppStart | TDAutoTrackEventType.AppEnd | TDAutoTrackEventType.AppInstall);
5.8 Report data via IP
To prevent or resolve issues where client data can't be reported to the server because of DNS hijacking, the SDK resolves the ServerUrl to get the IP and then reports data directly to the server via the IP. The following example shows how to enable it:
using ThinkingData.Analytics;
TDConfig config = new TDConfig(appId, serverUrl);
config.EnableDNSService(
TDDNSService.CloudAli,
TDDNSService.CloudFlare,
TDDNSService.CloudGoogle
);
TDAnalytics.Init(config);
Enum TDDNSService
| Enum values | No. | Description |
|---|---|---|
CloudFlare | 0 | Cloudflare DoH |
CloudAli | 1 | Alibaba Cloud DoH |
CloudGoogle | 2 | Google DoH |
You can pass in multiple providers, and the SDK tries them in the order they are passed in.
5.9 SDK error callback
Use this to listen for failures in SDK data sending or related operations. It must be called after Init.
The sample code is as follows:
using UnityEngine;
using ThinkingData.Analytics;
public class GameAnalytics : MonoBehaviour, TDErrorCallbackHandler
{
void Start()
{
TDConfig config = new TDConfig(appId, serverUrl);
TDAnalytics.Init(config);
TDAnalytics.RegisterErrorCallback(this);
// Multiple instances: TDAnalytics.RegisterErrorCallback(this, appId);
}
public void OnSDKErrorCallback(int code, string errorMsg, string ext)
{
Debug.Log("TDAnalytics error, code=" + code
+ ", errorMsg=" + errorMsg
+ ", ext=" + ext);
}
}
- Parameter description
| Parameter | Description |
|---|---|
code | Native SDK error code |
errorMsg | Error description or message returned by the server |
ext | Additional context, usually the request data at the time |
- Error code
| Error code | Platform | Description |
|---|---|---|
| 1001 | Android | Network error |
| 1002 | Android | Database insertion failed |
| 1003 | Android | Database exception |
| 1004 | Android | Data reporting (Flush) failed |
| 1006 | Android | Network exception |
| 10001 | iOS | Network error |
5.10 Auto-tracked events for Douyin mini games
On the Douyin mini game platform, auto-tracking of the show, hide, and launch events is currently supported. To integrate:
- Install the Douyin mini game plugin
- Define a custom macro
Menu bar: Edit -> Project Settings -> Scripting Define Symbols
Add the global macro TD_DOUYIN_GAME_MODE
Click Apply to complete the setup
Usage example:
// Enable auto-tracked events: AppStart tracks ta_mg_show, AppEnd tracks ta_mg_hide, and AppInstall tracks ta_mg_launch
TDAnalytics.EnableAutoTrack(TDAutoTrackEventType.AppStart | TDAutoTrackEventType.AppEnd | TDAutoTrackEventType.AppInstall);
6. Channel SDK compatibility
6.1 Tencent Ads
6.1.1 Solution overview
After you integrate the TDAnalytics SDK, you don't need to integrate the Tencent Ads SDK separately. Once you complete the TDAnalytics initialization method, the Tencent Ads SDK is initialized automatically. When you report key events such as registration and payment, the system automatically sends these events to Tencent Ads based on your configuration.
6.1.2 Integration steps
- Download the Tencent Ads SDK. Version 1.5.4 is currently used, but you can also switch to another version.
- Initialize
The TDAnalytics SDK version must be >= 3.1.1
TDConfig config = new TDConfig("APPID","SERVER");
config.reportingToTencentSdk = 2; //1: report only to Tencent; 2: report to both Tencent and AE; 3: report only to AE
TDAnalytics.Init(config);
To report data to Tencent, do the following:
After you export the WeChat mini game project, import the dn-sdk-minigame.js file into the project, and modify game.js to import dn-sdk and complete initialization
import { SDK } from "./dn-sdk-minigame.js";
try {
// Initialize
GameGlobal.dnSDK = new SDK({
user_action_set_id: 123xxxxxx,
secret_key: 'xxxxxxxxxxxxxxxxxxx',
appid: 'xxxxxxxxxxxxx',
});
// Report the launch
GameGlobal.dnSDK.onAppStart();
} catch {
}
- Set the user ID
- setOpenId
The openid is usually obtained asynchronously by calling a backend API (How to get the openid). After you get the openid, call the sdk.setOpenId() method to set it. You can set only one of openid and unionid, and openid takes precedence.
Call this after you get the openid
TDAnalytics.login(openid);
- setUnionId
The unionid is usually obtained asynchronously by calling a backend API (How to get the unionid). After you get the unionid, call the sdk.setUnionId() method to set it. Use this method to set the unionid only when there is no openid.
Call this after you get the unionid
TDAnalytics.setDistinctId(unionid);
- Report behaviors
Dictionary<string, object> properties = new Dictionary<string, object>(){{"product_name", "Product Name"}};
TDAnalytics.Track("product_buy", properties);
For the following specific events, report the specified event names
| Event | Event name | Event properties (must include these keys) |
Mini game launch | START_APP | None |
Payment | PURCHASE | { value: 600 } |
Registration | REGISTER | |
Reactivation | RE_ACTIVE | { backFlowDay: 30 } |
Add the mini game to favorites | ADD_TO_WISHLIST | { type: 'default', } |
Share the mini game | SHARE | { target: 'APP_MESSAGE' } |
Create a character | CREATE_ROL | { name: 'SuperMan' } |
Complete the tutorial | TUTORIAL_FINISH | None |
Level up | UPDATE_LEVEL | { level: 2, power: 85, } |
View the mall page | VIEW_CONTENT | { // Key scene visit: Mall item: 'Mall', } |
View game activities | VIEW_CONTENT | { // Key scene visit: Activity item: 'Activity', } |
For example, report the level-up event
Dictionary<string, object> properties = new Dictionary<string, object>();
properties["level"] = 2;
properties["power"] = 85;
2TDAnalytics.Track("product_buy", properties);

