menu
Is this helpful?

# iOS 앱 푸시 통합

# 앱 푸시 자동 수집 방법

자동 수집 방법은 SDK 내부에서 각 플랫폼(현재 단계에서는 FCM)의 푸시 토큰 및 푸시 메시지 클릭 이벤트(te_ops_push_click)를 자동으로 전송하는 방법입니다.

  1. 데이터 수집 SDK의 버전을 3.0.1-beta.1로 업그레이드하십시오.
pod 'ThinkingSDK', '3.0.1-beata.1'
  1. 자동 수집 푸시 링크를 활성화합니다.
TDConfig *config = [[TDConfig alloc] initWithAppId:appId serverUrl:serverUrl];
config.enableAutoPush = YES;//자동 수집을 활성화로 설정하기
[TDAnalytics startAnalyticsWithConfig:config];
  1. 계정 전환

계정을 전환한 후에는 login을 호출해야 합니다. SDK 내부에서는 자동으로 푸시 토큰이 user_set 인터페이스를 통해 새 계정으로 전송됩니다.

[TDAnalytics login:@"new_account_id"];

# 1. FCM 푸시

# 1.1 "pushID" 전송

  • 로그인하거나 계정을 전환한 후, FCM의 토큰을 업로드합니다.
NSString *appid = @"appid";
NSString *url = @"url";
TDConfig *config = [TDConfig new];
config.appid = appid;
config.configureURL = url;
ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK startWithConfig:config];

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

# 1.2. 클릭 이벤트 수집 및 전송

유저가 푸시를 클릭할 때 푸시 클릭 이벤트를 업로드할 수 있으며, onCreate나 onNewIntent에서 푸시 파라미터를 얻을 수 있습니다.

// Handle notification messages after display notification is tapped by the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void(^)(void))completionHandler {
    NSDictionary *userInfo = response.notification.request.content.userInfo;
    trackAppOpenNotification(userInfo)
    completionHandler();
}

# 1.3. 푸시 메시지 처리

유저가 푸시 알림을 클릭하면 시스템의 푸시 클릭 콜백에서 푸시 파라미터를 얻을 수 있습니다. 다음의 두 옵션 중 하나를 선택하세요.

  • 일반 파라미터: handleTEPushAction 메소드를 호출합니다.
  • 투명 파라미터: handlePassThroughAction 메소드를 호출합니다.
// Handle notification messages after display notification is tapped by the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void(^)(void))completionHandler {
    NSDictionary *userInfo = response.notification.request.content.userInfo;
     //일반 파라미터

    handleTEPushAction(userInfo)
    //파라미터를 통해
    handlePassThroughAction(userInfo)
    completionHandler();
}

# 2. APNs push

# 2.1 "pushID" 전송

  • 로그인하거나 계정을 전환한 후 APNs Device Token을 업로드합니다.
// 로그인 후, 다시 APNs 디바이스 토큰을 전송합니다.
[TDAnalytics login:<#login ID#>];
NSString *token = [self getDviceToken];
[TDAnalytics userSet:@{ @"#apns_token": token }];
  • application:didRegisterForRemoteNotificationsWithDeviceToken: 콜백에서 APNs Device Token을 전송합니다.
    // TA SDK 초기화 후, APNs 콜백에서 APNs 디바이스 토큰을 리포팅합니다.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSString *token = [self formatDeviceTokenToHexStr:deviceToken];
    [TDAnalytics userSet:@{ @"#apns_token": token }];
    // APNs 디바이스 토큰을 기록하고, 어카운트를 전환할 때 다시 전송할 수 있도록 합니다.
    [self holdDviceToken:token];
}

    // (NSData *)디바이스Token을 (NSString *)디바이스Token으로 변화합니다
- (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;
}

# 2.2 클릭 이벤트 수집

    //iOS10 이하인 경우, 푸시를 클릭하면 콜백이 됩니다.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    trackAppOpenNotification(userInfo);
    completionHandler(UIBackgroundFetchResultNewData);
}

    // iOS10 이상인 경우, 푸시를 클릭하면 콜백이 됩니다
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
    NSDictionary *userInfo = response.notification.request.content.userInfo;
    trackAppOpenNotification(userInfo);
completionHandler();  // 시스템이 이 메소드를 실행할 것을 요구하고 있습니다.
}

# 2.3 푸시 메시지 처리

다음의 두 방법 중 하나를 선택하세요.

  • 일반 파라미터: handleTEPushAction 메소드를 호출합니다.
  • 투명 파라미터: handlePassThroughAction 메소드를 호출합니다.
  // iOS10 이하인 경우, 푸시를 클릭하면 콜백이 됩니다.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  //일반 파라미터
  handleTEPushAction(userInfo)
  //파라미터를 통해
  handlePassThroughAction(userInfo)
  completionHandler(UIBackgroundFetchResultNewData);
}
 
  // iOS10 이상인 경우, 푸시를 클릭하면 콜백이 됩니다
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
    NSDictionary *userInfo = response.notification.request.content.userInfo;
     //일반 파라미터

    handleTEPushAction(userInfo)
    //파라미터를 통해
    handlePassThroughAction(userInfo)
completionHandler();  // 시스템이 이 메소드를 실행할 것을 요구하고 있습니다.
}

# 푸시 도달 이벤트 수집

# NotificationService 활성화

프로젝트에는 알림 확장의 Target을 새로 생성해야 합니다. 예를 들어: TANotification

# ThinkingDataPushExtension 통합

CocoaPods를 사용하여 ThinkingDataPushExtension을 통합합니다.

pod 'ThinkingDataPushExtension'

# 이벤트 수집집

NotificationService.m 파일에 다음 코드를 추가해주세요:

- (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의 백엔드에서 프로젝트의 receiver url을 확인할 수 있습니다.
  • accountId: 현재 유저에게 전송되는 푸시의 고유 ID입니다. 구체적인 정의는 TE의 유저 식별 규칙을 참조하세요. 이 필드는 푸시 데이터 구조에 수동으로 추가해야 하며, 푸시를 받았을 때 수동으로 분석해야 합니다.
  • distinctId: 현재 유저에게 전송되는 푸시의 게스트 ID입니다. 구체적인 정의는 TE의 유저 식별 규칙을 참조하세요. 이 필드는 푸시 데이터 구조에 수동으로 추가해야 하며, 푸시를 받았을 때 수동으로 분석해야 합니다.

# 부록

# 클라이언트가 받은 푸시 파라미터의 예시

{
    "te_extras": {
        //푸시 알림의 링크 방법

        "ops_loading_type": "OPEN_APP",
       //파라미터를 통해

        "passthrough_params": {
                "param1": "abc",
                "param2": 101,
                "param3": [{
                        "subText1": "xyz",
                        "subText2": 2
                }]
        },

        "#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"
        }
    }
}

# 자동 수집이 성공했는지 확인하는 방법은 무엇인가요?

  1. 푸시 알림에 접근한 후, 첫 실행이나 토큰 변경 시점에 다음 이벤트가 전송되었는지 확인하세요.
{
    "#type": "user_set",
    "#time": "2023-11-13 15:50:55.729",
    "#distinct_id": "distinct",
    "properties": {
        "FCM_id": "190e35f7e15c8481caa"
    },
    "#uuid": "9f233c31-a664-46ff-94d6-f767a3098c3a"
}
  1. 푸시 알림을 클릭하여 앱을 시작하고, 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": "au",
        "#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": "Galaxy",
        "#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": 9
    },
    "#uuid": "7a977e23-b78a-4433-baae-ead17ad2fde9"
}

# trackAppOpenNotification

public static void trackAppOpenNotification(String extras) {
    try {
        if (TextUtils.isEmpty(extras)) {
            return;
        }
        JSONObject jsonObject = new JSONObject(extras);
        JSONObject properties = new JSONObject();
        Object obj = json.opt("#ops_receipt_properties");
        JSONObject ops = null;
        if (obj instanceof String) {
            ops = new JSONObject(( String ) obj);
        } else if (obj instanceof JSONObject) {
            ops = ( JSONObject ) obj;
        }
        properties.put("#ops_receipt_properties", ops);
        ThinkingAnalyticsSDK instance = ThinkingAnalyticsSDK.sharedInstance(null, "appId", "serverUrl");
        instance.track("te_ops_push_click", properties);
         //즉시 전송

        instance.flush();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# handleTEPushAction

public static void handleTEPushAction(String extras) {
    try {
        if (TextUtils.isEmpty(extras)) {
            return;
        }
        JSONObject jsonObject = new JSONObject(extras);
        String type = jsonObject.optString("ops_loading_type");
        if ("OPEN_APP".equals(type)) {
         // TODO 앱을 열 메시지 처리--> App을 시작해주세요

        } else if ("OPEN_URL".equals(type)) {
            String url = jsonObject.optString("ops_url");
            if (!TextUtils.isEmpty(url)) {
        // TODO 업로드 URL을 열 메시지의 처리, --> 업로드 URL을 처리해주세요
            }
        } else if ("CUSTOMIZED".equals(type)) {
            String custom = jsonObject.optString("ops_customized");
            if (!TextUtils.isEmpty(custom)) {
         // TODO 맞춤형 메시지의 처리,--> 사용자 정의 메시지를 처리해 주세요

            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# handleTEPassThroughAction

public static void handleTEPassThroughAction(String extras) {
    try {
        if (TextUtils.isEmpty(extras)) {
            return;
        }
        JSONObject jsonObject = new JSONObject(extras);
        String params = jsonObject.optString("passthrough_params");
    //params는 투명 파라미터이며, 다음에 구체적인 로직의 구현이 이어집니다.
    } catch (Exception e) {
        e.printStackTrace();
    }
}