Skip to main content

iOS push notification integration

Last updated 10/07/2026

Resource downloads​

ThinkingDataPushExtension

Release version: v1.1.2 Framework download

GitHub: ThinkingDataPushExtension

ThinkingDataAnalyticsExtension

Release version: v1.1.2 Framework download

GitHub: ThinkingDataAnalyticsExtension

ThinkingSDK

Release version: v3.1.6 Framework download

All event reporting relies on the reporting capability of ThinkingSDK, so your project must integrate ThinkingSDK first.

For the steps, see the ThinkingSDK guide for iOS

Overview​

ThinkingSDK v3.0.1 and later can report push click events.

ThinkingDataPushExtension SDK (available on iOS 10 and later) reports push delivery events.

Integrate the following SDK in the main Target​

  • We recommend importing it with CocoaPods
  • ThinkingSDK implements data collection and reports push click events

Integrate the following SDK in the notification extension Target​

  • We recommend importing it with CocoaPods
  • ThinkingDataPushExtension automatically reports notification delivery events

All of the SDKs above depend on the ThinkingDataCore library, which is imported automatically when you integrate with CocoaPods. When you use the Framework method, you need to import the ThinkingDataCore Framework manually.

When you use CocoaPods, ThinkingDataPushExtension automatically imports the ThinkingDataAnalyticsExtension library for lightweight data collection. If you use the Framework method, you need to import the ThinkingDataAnalyticsExtension Framework manually.

Integration process​

  1. Integrate the data collection SDK (version >= 3.1.0) in the app's main target
pod 'ThinkingSDK', '3.1.6'
  1. Enable NotificationService

Your project needs a new notification extension Target, for example: TANotification

  1. Integrate ThinkingDataPushExtension in the notification extension
pod 'ThinkingDataPushExtension'

Automatic push click collection solution​

Enable automatic collection​

NSString *appId = @"appId";
NSString *serverUrl = @"serverUrl";
TDConfig *config = [[TDConfig alloc] initWithAppId:appId serverUrl:serverUrl];
// Enable automatic push collection
config.enableAutoPush = YES;
[TDAnalytics startAnalyticsWithConfig:config];

FCM push​

Report the "push ID"​

  1. After you integrate the FCM push SDK, ThinkingSDK reports it automatically (ThinkingSDK version must be >= v3.0.1)
  2. Switch accounts

After switching accounts, call the login API. The SDK automatically reports the push token to the new account through the userSet API.

[TDAnalytics login:@"new_account_id"];

Track push click events​

Push click events are reported automatically. No manual action is needed.

JPush​

Report the "push ID"​

  1. After you integrate the JPush SDK, ThinkingSDK reports it automatically (ThinkingSDK version must be >= v3.0.1)
  2. Switch accounts

After switching accounts, call the login API. The SDK automatically reports the push token to the new account through the userSet API.

[TDAnalytics login:@"new_account_id"];

Track push click events​

Push click events are reported automatically. No manual action is needed.

APNs push​

For how to get an APNs certificate, see Set up APNs push certificates

Report the "push ID"​

The APNs channel currently supports manual reporting only

  1. After calling login or switching accounts, upload the APNs Device Token manually.
// After login, report the APNs Device Token again
[TDAnalytics login:@"test_id"];

// Example: get the RemoteNotificationsWithDeviceToken registered with the system
NSString *token = [self getDviceTokenDemoFunction];

[TDAnalytics userSet:@{ @"#apns_token": token }];

Report the APNs Device Token in the - application:didRegisterForRemoteNotificationsWithDeviceToken: callback

// After the SDK is initialized, report the APNs Device Token in the APNs callback
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// We recommend saving the APNs Device Token here so that you can report it again when switching accounts
NSString *token = [self formatDeviceTokenToHexStr:deviceToken];
[TDAnalytics userSet:@{ @"#apns_token": token }];
}

// Convert (NSData *)deviceToken to (NSString *)deviceToken
- (NSString *)formatDeviceTokenToHexStr:(NSData *)deviceToken {
NSString *tokenStr;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 13.0) {
const unsigned *tokenBytes = [deviceToken bytes];
tokenStr = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
} else {
tokenStr = [[deviceToken description] stringByReplacingOccurrencesOfString:@"<" withString:@""];
tokenStr = [tokenStr stringByReplacingOccurrencesOfString:@">" withString:@""];
tokenStr = [tokenStr stringByReplacingOccurrencesOfString:@" " withString:@""];
}
return tokenStr;
}
  1. Switch accounts

After switching accounts, call the login API. The SDK automatically reports the push token to the new account through the userSet API.

[TDAnalytics login:@"new_account_id"];

Track push click events​

Push click events are reported automatically. No manual action is needed.

Manual push click collection solution​

FCM push​

Report the "push ID"​

  • Upload the FCM token after calling login or switching accounts.
NSString *appId = @"app-id";
NSString *serverUrl = @"server-url";
TDConfig *tdConfig = [[TDConfig alloc] initWithAppId:appId serverUrl:serverUrl];
[TDAnalytics startAnalyticsWithConfig:tdConfig];

[[FIRMessaging messaging] tokenWithCompletion:^(NSString *token, NSError *error) {
if (error != nil) {
NSLog(@"Error getting FCM registration token: %@", error);
} else {
NSLog(@"FCM registration token: %@", token);
[TDAnalytics userSet:@{@"fcm_token": token}];
}
}];

Track push click events​

When the user clicks a push notification, you can send the push click event in the system's push click callback.

// use <UserNotifications/UserNotifications.h> framework
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
NSDictionary *userInfo = response.notification.request.content.userInfo;
// For an example of the trackAppOpenNotification method, see the Appendix below
[self trackAppOpenNotification:userInfo];

completionHandler();
}

JPush​

Report the "push ID"​

  • Upload the JPush Registration ID after calling AE login or switching accounts.
// After login, report the registrationID again
[TDAnalytics login:@"test_id"];
[TDAnalytics userSet:@{@"jiguang_id": registrationID}];
  • Upload the JPush Registration ID in the - registrationIDCompletionHandler: callback.
// After the AE SDK is initialized, report the registrationID in the JPush callback
[JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID) {
[TDAnalytics userSet:@{@"jiguang_id": registrationID}];
}];

Track push click events​

// Callback for notification clicks on versions earlier than iOS 10
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[JPUSHService handleRemoteNotification:userInfo];
// For an example of the trackAppOpenNotification method, see the Appendix below
[self trackAppOpenNotification:userInfo];

completionHandler(UIBackgroundFetchResultNewData);
}

// Callback for notification clicks on iOS 10 and later
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler API_AVAILABLE(ios(10.0)){
// Required
NSDictionary *userInfo = response.notification.request.content.userInfo;
// For an example of the trackAppOpenNotification method, see the Appendix below
[self trackAppOpenNotification:userInfo];
// The system requires calling this method
completionHandler();
}

APNs push​

Report the "push ID"​

  • Upload the APNs Device Token after calling AE login or switching accounts.
// After login, report the APNs Device Token again
[TDAnalytics login:@"test_id"];

// Example: get the RemoteNotificationsWithDeviceToken registered with the system
NSString *token = [self getDviceTokenDemoFunction];

[TDAnalytics userSet:@{ @"#apns_token": token }];
  • Report the APNs Device Token in the - application:didRegisterForRemoteNotificationsWithDeviceToken: callback
// After the SDK is initialized, report the APNs Device Token in the APNs callback
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// We recommend saving the APNs Device Token here so that you can report it again when switching accounts
NSString *token = [self formatDeviceTokenToHexStr:deviceToken];
[TDAnalytics userSet:@{ @"#apns_token": token }];
}

// Convert (NSData *)deviceToken to (NSString *)deviceToken
- (NSString *)formatDeviceTokenToHexStr:(NSData *)deviceToken {
NSString *tokenStr;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 13.0) {
const unsigned *tokenBytes = [deviceToken bytes];
tokenStr = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
} else {
tokenStr = [[deviceToken description] stringByReplacingOccurrencesOfString:@"<" withString:@""];
tokenStr = [tokenStr stringByReplacingOccurrencesOfString:@">" withString:@""];
tokenStr = [tokenStr stringByReplacingOccurrencesOfString:@" " withString:@""];
}
return tokenStr;
}

Track push click events​

// Callback for notification clicks on versions earlier than iOS 10
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// For an example of the trackAppOpenNotification method, see the Appendix below
[self trackAppOpenNotification:userInfo];
completionHandler(UIBackgroundFetchResultNewData);
}

// Callback for notification clicks on iOS 10 and later
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
NSDictionary *userInfo = response.notification.request.content.userInfo;
// For an example of the trackAppOpenNotification method, see the Appendix below
[self trackAppOpenNotification:userInfo];
// The system requires calling this method
completionHandler();
}

Track message delivery events​

Add the following code to the notification extension target

#import <ThinkingDataPushExtension/ThinkingDataPushExtension.h>

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];

NSString *appId = @"your app id in TE";
NSString *serverUrl = @"your server url in TE";
NSString *accountId = @"user account id. AccountId and DistinctId cannot be empty at the same time";
NSString *distinctId = @"user distinct id. AccountId and DistinctId cannot be empty at the same time";

BOOL result = [TDPushExtension handleNotificationRequest:request withContentHandler:contentHandler appId:appId serverUrl:serverUrl accountId:accountId distinctID:distinctId];
if (result) {
return;
}

self.contentHandler(self.bestAttemptContent);
}

Parameters:

  • appId: The APP ID of your project, which you can view in AE
  • serverUrl: The receiver URL of your project, which you can view in AE
  • accountId: Configure passthrough parameters in the Engage console, and then parse accountId manually here
  • distinctId: Configure passthrough parameters in the Engage console, and then parse distinctId manually here

Where to configure this in the Engage console: In AE, go to Engage > Settings > Channel Settings > Push Notification, create or edit an APNs channel (the same applies to FCM and JPush channels), and add the parameters under Passthrough Parameter at the bottom of the channel configuration drawer:

Handle push messages​

FCM push​

When the user clicks a push notification, you can get the push parameters in the system's push click callback.

Use one of the following two solutions.

  • Parse the message type: Call the handleTEPushAction method.
  • Parse passthrough parameters: Call the handleTEPassThroughAction method.
// use <UserNotifications/UserNotifications.h> framework
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
NSDictionary *userInfo = response.notification.request.content.userInfo;
//To handle regular parameters, call the following method
[self handleTEPushAction:userInfo];
//To handle passthrough parameters, call the following method
[self handleTEPassThroughAction:userInfo];
completionHandler();
}

JPush​

Use one of the following two solutions.

  • Parse the message type: Call the handleTEPushAction method.
  • Parse passthrough parameters: Call the handleTEPassThroughAction method.
// Callback for notification clicks on versions earlier than iOS 10
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// Required, iOS 7 Support
[JPUSHService handleRemoteNotification:userInfo];
//To handle regular parameters, call the following method
[self handleTEPushAction:userInfo];
//To handle passthrough parameters, call the following method
[self handleTEPassThroughAction:userInfo];
completionHandler(UIBackgroundFetchResultNewData);
}

// Callback for notification clicks on iOS 10 and later
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler API_AVAILABLE(ios(10.0)){
// Required
NSDictionary *userInfo = response.notification.request.content.userInfo;
//To handle regular parameters, call the following method
[self handleTEPushAction:userInfo];
//To handle passthrough parameters, call the following method
[self handleTEPassThroughAction:userInfo];
// The system requires calling this method
completionHandler();
}

APNs push​

Use one of the following two solutions.

  • Regular parameters: Call the handleTEPushAction method.
  • Passthrough parameters: Call the handleTEPassThroughAction method.
// Callback for notification clicks on versions earlier than iOS 10
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
//To handle regular parameters, call the following method
[self handleTEPushAction:userInfo];
//To handle passthrough parameters, call the following method
[self handleTEPassThroughAction:userInfo];
completionHandler(UIBackgroundFetchResultNewData);
}

// Callback for notification clicks on iOS 10 and later
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
NSDictionary *userInfo = response.notification.request.content.userInfo;
//To handle regular parameters, call the following method
[self handleTEPushAction:userInfo];
//To handle passthrough parameters, call the following method
[self handleTEPassThroughAction:userInfo];
// The system requires calling this method
completionHandler();
}

Display push images​

You need to integrate the ThinkingDataPushExtension SDK in the notification extension target. If you've already added message delivery event tracking, you don't need to repeat this step.

Add the following code to the notification extension target

#import <ThinkingDataPushExtension/ThinkingDataPushExtension.h>

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];

NSString *appId = @"your app id in TE";
NSString *serverUrl = @"your server url in TE";
NSString *accountId = @"user account id. AccountId and DistinctId cannot be empty at the same time";
NSString *distinctId = @"user distinct id. AccountId and DistinctId cannot be empty at the same time";

BOOL result = [TDPushExtension handleNotificationRequest:request withContentHandler:contentHandler appId:appId serverUrl:serverUrl accountId:accountId distinctID:distinctId];
if (result) {
return;
}

self.contentHandler(self.bestAttemptContent);
}

Parameters:

  • appId: The APP ID of your project, which you can view in AE
  • serverUrl: The receiver URL of your project, which you can view in AE
  • accountId: Configure passthrough parameters in the Engage console, and then parse accountId manually here
  • distinctId: Configure passthrough parameters in the Engage console, and then parse distinctId manually here

Where to configure this in the Engage console: In AE, go to Engage > Settings > Channel Settings > Push Notification, create or edit an APNs channel (the same applies to FCM and JPush channels), and add the parameters under Passthrough Parameter at the bottom of the channel configuration drawer:

Appendix​

Example of push parameters received by the client​

The following shows only the extended field parameters received by the client

{
"te_extras": {
//Jump mode when the push is clicked
"ops_loading_type": "OPEN_APP",
//Passthrough parameters
"passthrough_params": {
"param1": "abc",
"param2": 101,
"param3": [{
"subText1": "xyz",
"subText2": 2
}]
},
//AE operation channel receipt properties
"#ops_receipt_properties": {
"ops_task_id": "0082",
"ops_project_id": 1,
"ops_task_instance_id": "0082_20230331",
"ops_push_language": "default",
"ops_task_exec_detail_id": "55"
}
}
}

How do I verify that the customer's push collection is integrated successfully?​

  1. After integrating push, check whether the following events are reported on first launch or when the push token changes.
    {
    "#type": "user_set",
    "#time": "2023-11-13 15:50:55.729",
    "#distinct_id": "distinct",
    "properties": {
    "jiguang_id": "190e35f7e15c8481caa"
    },
    "#uuid": "9f233c31-a664-46ff-94d6-f767a3098c3a"
    }
  2. Launch the app by clicking a push notification, and check whether the te_ops_push_click event is uploaded and whether the event properties contain #ops_receipt_properties.
    {
    "#type": "track",
    "#time": "2023-03-16 16:08:32.191",
    "#distinct_id": "90d80464-6832-43f1-80d9-bd93fc09c4fe",
    "#event_name": "te_ops_push_click",
    "properties": {
    "#lib_version": "3.0.1-beta.1",
    "#carrier": "中国移动",
    "#os": "Android",
    "#device_id": "6262ca7f71e6aca3",
    "#screen_height": 2400,
    "#bundle_id": "cn.thinkingdata.random",
    "#device_model": "M2012K11AC",
    "#screen_width": 1080,
    "#system_language": "zh",
    "#install_time": "2023-03-10 11:24:44.285",
    "#simulator": false,
    "#lib": "Android",
    "#manufacturer": "Xiaomi",
    "#os_version": "11",
    "#app_version": "1.0",
    "#fps": 60,
    "#network_type": "WIFI",
    "#ram": "2.7\/7.4",
    "#disk": "4.6\/106.3",
    "#device_type": "Phone",
    "#ops_receipt_properties": {
    "ops_project_id": 1,
    "ops_request_id": "3b21d2a8-8d3d-44fa-b460-3bb311ed3bcd"
    },
    "#zone_offset": 8
    },
    "#uuid": "7a977e23-b78a-4433-baae-ead17ad2fde9"
    }

trackAppOpenNotification​

Push click event

- (void)trackAppOpenNotification:(NSDictionary *)userInfo{
@try {
if ([userInfo.allKeys containsObject:@"te_extras"] && [userInfo[@"te_extras"] isKindOfClass:[NSString class]]) {
NSData *jsonData = [userInfo[@"te_extras"] dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSDictionary *responseDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
NSDictionary *opsReceiptProperties = responseDic[@"#ops_receipt_properties"];
if ([opsReceiptProperties isKindOfClass:[NSString class]]) {
NSString *opsStr = (NSString *)opsReceiptProperties;
opsReceiptProperties = [NSJSONSerialization JSONObjectWithData:[opsStr dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&err];
}
if (opsReceiptProperties && [opsReceiptProperties isKindOfClass:[NSDictionary class]]) {
NSMutableDictionary *pushProperties = [NSMutableDictionary dictionary]; // track dictionary
pushProperties[@"#ops_receipt_properties"] = opsReceiptProperties;
[TDAnalytics track:@"te_ops_push_click" properties:pushProperties];
[TDAnalytics flush];
}
}
} @catch (NSException *exception) {

}
}

handleTEPushAction​

Parse the push message type

- (void)handleTEPushAction:(NSDictionary *)userInfo{
@try {
NSString *jsonString = userInfo[@"te_extras"];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSDictionary *sfDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
if (!sfDictionary || error) {
return;
}
NSString *sf_landing_type = sfDictionary[@"ops_loading_type"];
if ([sf_landing_type isEqualToString:@"OPEN_APP"]) {
// Open the App
}
else if ([sf_landing_type isEqualToString:@"OPEN_URL"]) {
// Open the URL
NSString *url = sfDictionary[@"ops_url"];
}
else if ([sf_landing_type isEqualToString:@"CUSTOMIZED"]) {
// Handle the custom message
NSString *customized = sfDictionary[@"ops_customized"];
}

} @catch (NSException *exception) {

}
}

handleTEPassThroughAction​

Parse passthrough parameters

- (void)handleTEPassThroughAction:(NSDictionary *)userInfo{
@try {
NSString *jsonString = userInfo[@"te_extras"];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSDictionary *sfDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
if (!sfDictionary || error) {
return;
}
NSString *params = sfDictionary[@"passthrough_params"];
if (params) {
// params contains the passthrough parameters. Implement your business logic next
}
} @catch (NSException *exception) {

}
}
Was this page helpful?