# iOS App-push統合
# ダウンロード:
ThinkingDataPushExtension
正式バージョン: v1.0.0 Framework ダウンロード (opens new window)
GitHub:ThinkingDataPushExtension (opens new window)
ThinkingDataAnalyticsExtension
正式バージョン: v1.0.0 Framework ダウンロード (opens new window)
GitHub:ThinkingDataAnalyticsExtension (opens new window)
ThinkingDataCore
正式バージョン: v1.0.1 Framework ダウンロード (opens new window)
GitHub:ThinkingDataCore (opens new window)
# 概要
ThinkingSDK v3.0.1以降のバージョンでは、プッシュ通知のクリックイベントを送信することができます。ThinkingDataPushExtension SDK(iOS 10以上で利用可能)は、プッシュ通知到達イベントの送信を実現します。
# 主Targetには以下のSDKを統合
- Cocoapodsを使用してインポートすることをおすすめします。
 - ThinkingSDKはデータ収集機能やプッシュ通知のクリックイベントレポートを実現するために使用されます。
 
# 通知拡張 Target には以下のSDKを統合
- cocoapodsを使用してインポートすることをお勧めします。
 - ThinkingDataPushExtensionは通知の到達イベントの自動レポートを実現するために使用されます。
 
以上のSDKはすべてThinkingDataCoreライブラリに依存しており、cocoapodsを使用して統合する場合は自動的にインポートされます。Framework方式を使用する場合は、
ThinkingDataCoreのFrameworkライブラリを手動でインポートする必要があります。cocoapodsを使用する場合、ThinkingDataPushExtensionは自動的にThinkingDataAnalyticsExtensionライブラリをインポートして軽量なデータ収集機能を実現します。Framework方式を使用する場合は、ThinkingDataAnalyticsExtensionのFrameworkライブラリを手動でインポートする必要があります。
# アクセス手順
- appのメインtargetに収集SDKバージョン(>= 3.0.1)を実装
 
pod 'ThinkingSDK', '3.0.1'
- NotificationServiceを開始
 
プロジェクトには、通知拡張のTargetを新しく作成する必要があります。例えば:TANotification

- 通知拡張にThinkingDataPushExtensionを統合
 
pod 'ThinkingDataPushExtension'
# クリックイベントを自動収集
# 自動収集をON
NSString *appId = @"appId";
NSString *serverUrl = @"serverUrl";
TDConfig *config = [[TDConfig alloc] initWithAppId:appId serverUrl:serverUrl];
// プッシュ自動回収を開始
config.enableAutoPush = YES;
[TDAnalytics startAnalyticsWithConfig:config];
# FCM 統合
# プッシュIDを送信
- FCMプッシュSDKを統合した後、ThinkingSDKは自動的に送信されます(ThinkingSDKのバージョンはv3.0.1以上である必要があります)。
 - アカウント切り替え:
 
アカウントを切り替えた後、loginインターフェースを呼び出す必要があります。SDK内部では、プッシュトークンがuserSetインターフェースを介して新しいアカウントに送信されます。
[TDAnalytics login:@"new_account_id"];
# クリックイベントを収集
自動的にクリックイベントを送信し、手動操作は必要ありません。
# APNs統合
# プッシュIDを送信
APNsチャネルは一時的に手動でのみサポートされています。
- loginまたはアカウント切り替えを呼び出した後、 APNs Device Tokenを手動でアップロードしてください。
 
// ログイン後、再度APNsデバイストークンを送信
[TDAnalytics login:@"test_id"];
// 例:システムに登録されたRemoteNotificationsWithDeviceTokenを取得
NSString *token = [self getDviceTokenDemoFunction];
[TDAnalytics userSet:@{ @"#apns_token": token }];
application:didRegisterForRemoteNotificationsWithDeviceToken: コールバックで APNs Device Tokenを送信します。
// SDK 初期化後、APNs コールバックで APNs Device Tokenを送信
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    // アカウントを切り替える際に、ここにAPNs Device Tokenを記録しておくことをお勧めします。
    NSString *token = [self formatDeviceTokenToHexStr:deviceToken];
    [TDAnalytics userSet:@{ @"#apns_token": token }];
}
// (NSData *)deviceToken を (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;
}
- アカウント切り替え
 
アカウントを切り替えた後、loginインターフェースを呼び出す必要があります。SDK内部では、自動的にプッシュtokenをuserSetインターフェースを介して新しいアカウントに送信します。
[TDAnalytics login:@"new_account_id"];
# クリックイベントを収集
自動的にクリックイベントを送信し、手動操作は必要ありません。
# クリックイベントを手動収集
# FCM 統合
# プッシュIDを送信
- loginまたはアカウントの切り替え後にFCM_tokenをアップロードします。
 
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}];
      }
}];
# クリックイベントを収集
ユーザーがプッシュ通知をクリックすると、プッシュクリックイベントを送信することができます。
// use <UserNotifications/UserNotifications.h> framework
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
    NSDictionary *userInfo = response.notification.request.content.userInfo;
    // trackAppOpenNotificationメソッドの例は、以下の「附録」を参照してください。
    [self trackAppOpenNotification:userInfo];
    
    completionHandler();
}
# APNs統合
# プッシュIDを送信
- TA loginまたはアカウント切り替え後に、APNs Device Tokenをアップロードしてください。
 
// login後、再度APNs Device Tokenを送信
[TDAnalytics login:@"test_id"];
// 例:登録されたRemoteNotificationsWithDeviceTokenを取得
NSString *token = [self getDviceTokenDemoFunction];
[TDAnalytics userSet:@{ @"#apns_token": token }];
- application:didRegisterForRemoteNotificationsWithDeviceToken: コールバックでAPNs Device Tokenを送信します。
 
// SDK 初期化後、APNs コールバックで APNs Device Tokenを送信
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    // アカウントを切り替える際に、ここにAPNs Device Tokenを記録しておくことをお勧めします。
    NSString *token = [self formatDeviceTokenToHexStr:deviceToken];
    [TDAnalytics userSet:@{ @"#apns_token": token }];
}
// (NSData *)deviceToken を (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;
}
# クリックイベントを収集
//  iOS10未満の場合、通知をクリックした際のコールバック
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // trackAppOpenNotificationメソッドの例は、以下の「附録」を参照してください。
    [self trackAppOpenNotification:userInfo];
    completionHandler(UIBackgroundFetchResultNewData);
}
// iOS10以上、通知をタップした際のコールバック
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
    NSDictionary *userInfo = response.notification.request.content.userInfo;
    // trackAppOpenNotificationメソッドの例は、以下の「附録」を参照してください。
    [self trackAppOpenNotification:userInfo];
    // システムはこのメソッドを実行する必要があります。
    completionHandler();
}
# メッセージ着信イベントを収集
通知拡張の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);
}
パラメータの説明:
- appId:TEバックエンドでプロジェクトのAPP IDを確認
 - serverUrl:TEバックエンドでプロジェクトのreciver_URLを確認
 - accountId: エンゲージバックエンドで透過パラメータを設定し、ここでは手動でaccountIdを解析する必要があります。
 - distinctId: エンゲージバックエンドで透過パラメータを設定し、ここでは手動でdistinctIdを解析する必要があります。
 
エンゲージバックエンドの設定エントリー:

# プッシュメッセージ処理
# FCMプッシュ
ユーザーがプッシュ通知をクリックすると、システムのプッシュクリックコールバックでプッシュパラメータを取得できます。
以下の2つのオプションから1つを選択してください。
- 普通パラメータ解析:handleTEPushActionメソッドを呼び出します。
 - 透過パラメータ解析:handlePassThroughActionメソッドを呼び出します。
 
// use <UserNotifications/UserNotifications.h> framework
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
    NSDictionary *userInfo = response.notification.request.content.userInfo;
    //普通パラメータを処理する場合、以下の方法を呼び出します。
    [self handleTEPushAction:userInfo];
    //透過パラメータを処理する場合、以下の方法を呼び出します。
    [self handleTEPassThroughAction:userInfo];
    completionHandler();
}
# APNsプッシュ
以下の2つのオプションから1つを選択してください。
- 普通パラメータ解析:handleTEPushActionメソッドを呼び出します。
 - 透過パラメータ解析:handlePassThroughActionメソッドを呼び出します。
 
//  iOS10未満の場合、通知をクリックした際のコールバック
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  //普通なパラメータを処理する場合、以下の方法を呼び出します。
  [self handleTEPushAction:userInfo];
  //透過パラメータを処理する場合、以下の方法を呼び出します。
  [self handleTEPassThroughAction:userInfo];
  completionHandler(UIBackgroundFetchResultNewData);
}
 
// iOS10以上、通知をタップした際のコールバック
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
    NSDictionary *userInfo = response.notification.request.content.userInfo;
    //普通なパラメータを処理する場合、以下の方法を呼び出します。
    [self handleTEPushAction:userInfo];
    //透過パラメータを処理する場合、以下の方法を呼び出します。
    [self handleTEPassThroughAction:userInfo];
    // システムはこのメソッドを実行する必要があります。
    completionHandler();
}
# プッシュ画像を表示
通知拡張のtargetにThinkingDataPushExtension SDKを統合する必要があります。メッセージ到達イベントの収集をすでに追加している場合は、この操作を繰り返す必要はありません。以下のコードを通知拡張の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);
}
パラメータの説明:
- appId:TEバックエンドでプロジェクトのAPP IDを確認
 - serverUrl:TEバックエンドでプロジェクトのreciver_URLを確認
 - accountId: エンゲージバックエンドで透過パラメータを設定し、ここでは手動でaccountIdを解析する必要があります。
 - distinctId: エンゲージバックエンドで透過パラメータを設定し、ここでは手動でdistinctIdを解析する必要があります。

 
# 付録
# クライアントが受け取ったプッシュパラメータの例
以下は、クライアントが受け取った拡張フィールドのパラメーターを表示するだけです。
{
    "te_extras": {
        //クリックしてプッシュ通知をジャンプする方法
        "ops_loading_type": "OPEN_APP",
        //通過パラメータ
        "passthrough_params": {
                "param1": "abc",
                "param2": 101,
                "param3": [{
                        "subText1": "xyz",
                        "subText2": 2
                }]
        },
        //TEエンゲージチャネルのコールバックプロパティ
        "#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"
        }
    }
}
# 接続が成功したかどうかを確認する方法はありますか?
- 接続後、初回起動またはプッシュトークンの変更時に、以下のイベントを送信されていますか
 
{
    "#type": "user_set",
    "#time": "2023-11-13 15:50:55.729",
    "#distinct_id": "distinct",
    "properties": {
        "jiguang_id": "190e35f7e15c8481caa"
    },
    "#uuid": "9f233c31-a664-46ff-94d6-f767a3098c3a"
}
- プッシュ通知をクリックしてアプリを起動し、te_ops_push_clickイベントがアップロードされるかどうかを観察し、イベントのプロパティに#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-beata.1",
        "#carrier": "docomo",
        "#os": "Android",
        "#device_id": "6262ca7f71e6aca3",
        "#screen_height": 2400,
        "#bundle_id": "jp.thinkingdata.random",
        "#device_model": "M2012K11AC",
        "#screen_width": 1080,
        "#system_language": "jp",
        "#install_time": "2023-03-10 11:24:44.285",
        "#simulator": false,
        "#lib": "Android",
        "#manufacturer": "sony",
        "#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
プッシュクリックイベント
- (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 字典
                pushProperties[@"#ops_receipt_properties"] = opsReceiptProperties;
                [TDAnalytics track:@"te_ops_push_click" properties:pushProperties];
                [TDAnalytics flush];
            }
        }
    } @catch (NSException *exception) {
        
    }
}
# handleTEPushAction
プッシュメッセージタイプを解析
- (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"]) {
            // Appを起動
        }
        else if ([sf_landing_type isEqualToString:@"OPEN_URL"]) {
            // URLをクリック
           NSString *url = sfDictionary[@"ops_url"];
        }
        else if ([sf_landing_type isEqualToString:@"CUSTOMIZED"]) {
           // カスタムメッセージを処理
           NSString *customized = sfDictionary[@"ops_customized"];
        }
        
    } @catch (NSException *exception) {
        
    }
}
# handleTEPassThroughAction
通過パラメータを解析
- (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は透過パラメータであり、次に具体的な業務ロジックを実装
        }
    } @catch (NSException *exception) {
        
    }
}
