iOS push notification integration
Resource downloads
Release version: v1.1.2 Framework download
GitHub: ThinkingDataPushExtension
Release version: v1.1.2 Framework download
GitHub: ThinkingDataAnalyticsExtension
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
- Integrate the data collection SDK (version >= 3.1.0) in the app's main target
pod 'ThinkingSDK', '3.1.6'
- Enable NotificationService
Your project needs a new notification extension Target, for example: TANotification
- 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"
- After you integrate the FCM push SDK, ThinkingSDK reports it automatically (ThinkingSDK version must be >= v3.0.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.
JPush
Report the "push ID"
- After you integrate the JPush SDK, ThinkingSDK reports it automatically (ThinkingSDK version must be >= v3.0.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.
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
- 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;
}
- 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?
- 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"}
- 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) {
}
}

