Skadnetwork 3.0 S2S 구현 안내서

최신 버전의 SKAdNetwork를 구현하려면 [베타] SKAdNetwork 4.0 S2S 구현을 참조하세요

 

SKAdNetwork 개요

SKAdNetwork는 iOS에서 앱 설치 광고 캠페인을 개인정보 보호 친화적으로 측정할 수 있도록 Apple에서 제공하는 프레임워크입니다(자세히 알아보기). 이 프레임워크는 사용자의 식별 정보를 손상시키지 않고 앱 설치 캠페인의 전환율을 측정하는 데 도움이 됩니다.

SKAdNetwork는 어떻게 작동하나요?

SKAdNetwork에서 어트리뷰션 프로세스는 앱스토어가 Apple의 서버를 통해 수행합니다. 그런 다음 어트리뷰션 정보는 사용자 식별자 및 시간 정보에서 분리되어 네트워크로 전송됩니다.

Screen_Shot_2020-09-16_at_18.57.56.png

광고를 클릭하고 스토어가 열리면 퍼블리싱 앱과 네트워크는 네트워크, 퍼블리셔, 캠페인 ID와 같은 몇 가지 기본 정보를 제공합니다. 광고주 앱이 실행되고 SKAdNetwork에 등록한 경우, 디바이스는 전환 성공 알림을 네트워크에 전송합니다. 이 알림은 광고 앱에서 보고할 수 있는 전환 값과 함께 첨부된 값을 보고합니다.

이 알림은 첫 실행 후 최소 24시간 후에 전송되며, 기기 또는 사용자 식별 정보는 포함되지 않습니다.

또한, 앱 스토어는 광고가 게재된 앱이 원본 광고와 퍼블리셔를 알지 못하도록 프로세스를 진행합니다. 따라서 네트워크는 인스톨하는 사용자에 대해 전혀 알지 못한 채 인스톨에 대한 알림을 받게 됩니다.

SKAdNetwork를 사용하면 어떤 이점이 있나요?

SKAdNetwork에는 몇 가지 주요 장점이 있습니다. 다음과 같은 정보를 모두 제공합니다:

  • 동의 없이 작동하는 라스트 클릭 어트리뷰션
  • 소스, 캠페인, 퍼블리셔분석 정보
  • 인스톨 후 전환 값(최대 64개의 불연속형 값)
  • 인스톨 검증을 위한 암호화 서명

그러나 현재 형태의 SKAdNetwork는 기본 기능만 제공하므로, 제대로 작동하려면 여러 주체 간의 신중한 구현과 조정이 필요합니다.

현재 SKAdNetwork의 몇 가지 한계는 다음과 같습니다:

  • 사용자 수준 데이터 없음
  • 뷰스루 어트리뷰션 없음
  • 제한된 범위의 전환 값:
    • 설치/재설치당 하나의 전환 이벤트만 가능
    • 최대 64개의 전환 값(6비트)
  • 제한된 세분성:
    • 최대 100개의 캠페인 값
    • 광고 그룹 및 크리에이티브 레벨에 대한 표시 없음
  • LTV 없음/긴 코호트
  • 사기 노출:
    • 전환 값 자체에 서명되지 않음(조작 가능)
    • 포스트백은 중복될 수 있습니다.

이러한 문제점을 극복하기 위해 싱귤러는 SKAdNetwork 구현을 위한 공개 표준을 발표하고, SKAdNEtwork를 탐색하는 데 도움이 되는 여러 블로그 게시물을 공개했습니다:

SKAdNetwork S2S 구현

단일 SKAdNetwork 솔루션은 다음과 같은 구성 요소로 이루어져 있습니다:

  • SKAdNetwork를 구현하기 위한 클라이언트 측 코드
  • 모든 네트워크의 포스트백 검증 및 집계
  • 사기 방지:
    • 서명 유효성 검사 및 트랜잭션 ID 중복 제거.
    • 서명되지 않은 파라미터(전환 값 및 지오데이터)를 검증하기 위한 네트워크 보안 설정.
  • 전환 가치 관리: 싱귤러의 대시보드에서 동적으로 구성할 수 있는 기능을 제공하여 설치 후 활동을 SKAdNetwork 전환 가치로 인코딩할 수 있습니다.
  • 리포팅: 제한된 SKAdNetwork 캠페인 ID를 변환하고 더 많은 마케팅 파라미터와 세분화로 데이터를 보강합니다.
  • 파트너 포스트백: 이벤트 및 수익으로 디코딩된 전환 가치를 이벤트 및 수익으로 변환하여 SKAdNetwork 포스트백을 전송하는 기능으로, 최적화에 매우 중요합니다.

서버 간 싱귤러 통합을 사용하는 고객의 경우, SKAdNetwork 구현은 크게 두 부분으로 구성됩니다:

  1. SKAdNetwork 클라이언트 측 구현: 이 부분은 앱을 SKAdNetwork에 등록하고 SKAdNetwork 전환 가치를 지능적으로 관리하는 데 매우 중요합니다. 즉, 이를 구현하면 SKAdNetwork 어트리뷰션 및 관련 인스톨 후 활동을 기반으로 캠페인을 최적화할 수 있습니다.
  2. S2S 통합 업데이트(선택 사항): 이 부분은 클라이언트 측 구현의 유효성을 검사하고 문제를 해결하는 데 중요합니다. 현재 싱귤러의 S2S 세션 및 이벤트 엔드포인트를 통해 싱귤러로 전송되는 이벤트와 세션을 SKAdNetwork 데이터(전환 값 및 업데이트 타임스탬프)로 보강함으로써, 싱귤러는 앱 측에서 구현이 제대로 이루어졌는지 검증할 수 있습니다.

SKAdNetwork 클라이언트 측 구현

싱귤러는 SKAdNetwork 등록 및 전환 값 관리를 지원하는 코드 샘플을 제공합니다. 이 코드 샘플은 다음과 같은 부분을 담당합니다:

  1. SKAdnetwork 지원 및 등록
  2. 전환 가치 관리:
    • 이 코드는 싱귤러의 엔드포인트와 동기식으로 통신하여 구성된 전환 모델에 따라 다음 전환 값을 수신합니다. 이벤트/세션/수익을 보고하고, 이에 대한 응답으로 다음 전환 값(Singular의 대시보드에서 측정하도록 구성된 설치 후 활동을 나타내는 인코딩된 숫자)을 가져옵니다.
    • 또한 이 코드는 유효성 검사 및 다음 전환 값 계산에 사용되는 SKAdnetwork 메타데이터를 수집합니다:
      • 기본 SKAdNetwork 프레임워크에 대한 첫 번째 호출 타임스탬프
      • 기본 SKAdNetwork 프레임워크에 대한 마지막 호출 타임스탬프
      • 마지막으로 업데이트된 전환 값

S2S 연동 업데이트

필수

활성 전환 모델이 있으면 S2S 엔드포인트는 클라이언트 측 코드에서 업데이트할 다음 값을 포함하는 "conversion_value"라는 새 int 필드를 반환하기 시작합니다.

선택사항

통합을 검증하고 잠재적인 구현 문제를 해결하려면 코드 샘플을 사용하여 현재 S2S 통합을 확장하는 것이 좋습니다. 싱귤러의 S2S 이벤트 및 세션 엔드포인트는 이미 다음과 같은 추가 SKAdNetwork 파라미터 가져오기를 지원합니다:

  • 최근 전환 값
  • 기본 SKAdNetwork 프레임워크에 대한 호출의 마지막 타임스탬프
  • 기본 SKAdNetwork 프레임워크에 대한 호출의 첫 번째 타임스탬프

통합 흐름

Screen_Shot_2020-09-16_at_18.59.13.png

위의 다이어그램은 S2S 고객에 대한 SKAdNetwork 플로우를 보여줍니다:

  1. 첫째, 앱의 코드가 SKAdNetwork 전용 엔드포인트를 통해 싱귤러 서버와 통신하여 앱에서 발생하는 이벤트/세션/매출 이벤트를 기반으로 최신 전환 값을 동기식으로 가져오고, 이 값으로 SKAdNetwork 프레임워크를 업데이트합니다.
  2. 둘째, 앱은 나중에 유효성 검사에 사용될 SKAdNetwork 데이터로 기존 이벤트와 세션을 보강합니다.
  3. 앱이 새로운 전환 값으로 SKAdNetwork 프레임워크 업데이트를 완료하고 SKAdNetwork 타이머가 만료되면, SKAdNetwork 포스트백이 네트워크로 전송됩니다.
  4. 네트워크는 보안 설정 또는 일반 설정을 통해 이를 싱귤러로 전달합니다.
  5. 싱귤러는 포스트백을 처리합니다:
    • 서명 유효성 검사
    • 구성된 전환 모델을 기반으로 전환 값을 디코딩합니다.
    • 파트너와의 통합 및 SKAdNetwork 내부 캠페인 ID를 기반으로 더 많은 외부 마케팅 파라미터로 포스트백을 보강합니다.
    • 디코딩된 포스트백을 BI 및 파트너에게 보내기

중요한 점은 인스톨 및 디코딩된 이벤트를 포함한 SKAdNetwork 정보는 기존 데이터 세트와 섞이지 않도록 다른 보고서/API/ETL 테이블 및 포스트백 세트를 통해 액세스할 수 있다는 것입니다. 이는 기존 캠페인 활동과 SKAdNetwork를 나란히 측정하고 테스트할 수 있도록 하기 위해 앞으로 몇 주 동안 특히 중요합니다.

SKAdNetwork 네이티브 iOS 코드 샘플

SKAdNetwork 인터페이스

이 인터페이스에는 다음과 같은 SKAdNetwork 구성 요소가 포함되어 있습니다:

SKAdNetwork 등록:

  • 이 메서드는 앱을 SKAdNetwork에 등록하는 작업을 담당합니다. 기본 Apple API 메서드는 기기에 해당 앱에 대한 어트리뷰션 데이터가 있는 경우 알림을 생성하고 24시간 타이머를 시작합니다.
  • 디바이스는 타이머가 만료된 후 0~24시간 이내에 애드 네트워크의 포스트백 엔드포인트로 인스톨 알림을 전송합니다.
  • 전환 값을 이전 값보다 더 큰 값으로 업데이트하면 첫 번째 타이머가 24시간 간격으로 재설정됩니다(자세한 내용은 여기를 참조하세요).

전환 가치 관리 및 업데이트:

  • 전환 값은 아래 방법으로 캡처된 디바이스의 설치 후 활동과 사용자가 동적으로 구성할 수 있는 선택된 전환 모델을 기반으로 계산됩니다.
  • 이 섹션의 메서드는 선택한 전환 모델과 보고된 설치 후 활동에 따라 싱귤러의 엔드포인트에서 다음 전환 값을 가져오는 역할을 합니다(위의 문서 참조).
  • 아래 방법은 다음과 같은 인스톨 후 활동을 기반으로 전환 값을 업데이트합니다:
      • 세션: SKAdNetwork를 사용하는 사용자의 리텐션 측정에 중요합니다.
      • 전환 이벤트: SKAdNetwork를 통한 인스톨 후 전환 이벤트 측정에 중요합니다.
      • 수익 이벤트: SKAdNetwork를 통한 수익 측정에 필수적입니다.
//  SKANSnippet.h
  
  #import <Foundation/Foundation.h>
  
  @interface
  SKANSnippet : NSObject
  
  // SkAdNetwork 속성에 등록하세요. 앱이 실행되면 가능한 한 빨리 이 메서드를 호출해야 합니다.
  + (void)registerAppForAdNetworkAttribution;
  
  /* 유지율과 코호트를 추적하려면 각 세션 후에 이 메소드를 호출해야 합니다. 세션 세부정보를 보고하고 필요한 경우 이 세션으로 인한 전환 가치를 업데이트합니다. 전환 가치는 새 값이 이전 값보다 큰 경우에만 업데이트됩니다. 메소드에 전달된 콜백은 선택사항이며, 변환 값이 업데이트되면 이를 사용하여 코드를 실행할 수 있습니다.*/
  + (void)updateConversionValueAsync:(void(^)(int, NSError*))handler;
  
  /* SKAdNetwork로 전환 이벤트를 추적하려면 각 이벤트 후에 이 메소드를 호출해야 합니다. 이벤트 세부정보를 보고하고 필요한 경우 이 이벤트로 인한 전환 가치를 업데이트합니다. 메소드에 전달된 콜백은 선택사항이며, 변환 값이 업데이트되면 이를 사용하여 코드를 실행할 수 있습니다.*/
  + (void)updateConversionValueAsync:(NSString*)eventName
withCompletionHandler:(void(^)(int, NSError*))handler; /* SKAdNetwork로 수익을 추적하려면 각 수익 이벤트 전에 이 메소드를 호출해야 합니다. 총 수익이 업데이트되므로 'updateConversionValueAsync'를 호출하면 총 수익에 따라 새로운 전환 가치가 결정됩니다.
메모:
1. 수익이 업데이트되었는지 확인하려면 'updateConversionValueAsync'를 호출하기 전에 이 메서드를 호출하세요.
2. 이벤트를 재시도할 경우 동일한 수익이 중복 계산되지 않도록 이 메서드를 호출하지 마세요.*/ + (void)updateRevenue:(double)amount andCurrency:(NSString*)currency; // 현재 전환 가치를 가져옵니다(없는 경우 nil). + (NSNumber *)getConversionValue; @end

SKAdNetwork 인터페이스 구현

//  SKANSnippet.m
  
  #import "SKANSnippet.h"
  #import <StoreKit/SKAdNetwork.h>
  
  #define SESSION_EVENT_NAME @”__SESSION__” 
  #define SINGULAR_API_URL @"https://sdk-api-v1.singular.net/api/v1/conversion_value"
  
  // Keys for UserDefaults storage
  #define CONVERSION_VALUE_KEY @"skan_conversion_value"
  #define FIRST_SKAN_CALL_TIMESTAMP @"skan_first_call_to_skadnetwork_timestamp"
  #define LAST_SKAN_CALL_TIMESTAMP @"skan_last_call_to_skadnetwork_timestamp"
  #define TOTAL_REVENUE_BY_CURRENCY_KEY @"skan_total_revenue_by_currency"
  #define SECONDS_PER_DAY 86400
  
  @implementation
  SKANSnippet
  
  + (void)registerAppForAdNetworkAttribution {
      if ([SKANSnippet getFirstSkanCallTimestamp] != 0) {
          return;
      }
  
      if (@available(iOS 11.3, *) ) {
          [SKAdNetwork registerAppForAdNetworkAttribution];
          [SKANSnippet setFirstSkanCallTimestamp];
          [SKANSnippet setLastSkanCallTimestamp];
      }
  }
  
  + (void)updateConversionValueAsync:(void(^)(int, NSError*))handler {
      [SKANSnippet updateConversionValueAsync:SESSION_EVENT_NAME withCompletionHandler:handler];
  }
  
  + (void)updateConversionValueAsync:(NSString*)eventName withCompletionHandler:(void(^)(int, NSError*))handler {
      if (@available(iOS 14, *)) {
          if ([SKANSnippet isSkanUpdateWindowOver]) {
              return;
          }
          
          [SKANSnippet getConversionValueFromServer:eventName withCompletionHandler:handler];
      }
  }
  
  + (void)updateRevenue:(double)amount andCurrency:(NSString*)currency {
      if (@available(iOS 14, *)) {
          
          // Update total revenues
          if (amount != 0 && currency) {
              NSMutableDictionary* revenues = [[SKANSnippet getTotalRevenue] mutableCopy];
              NSNumber* currentRevenue = 0;
              
              if ([revenues objectForKey:currency]) {
                  currentRevenue = [revenues objectForKey:currency];
              }
              
              currentRevenue = @([currentRevenue floatValue] + amount);
              [revenues setObject:currentRevenue forKey:currency];
              
              [SKANSnippet setTotalRevenue:revenues];
          }
      }
  }
  
  + (NSNumber *)getConversionValue {
      NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
      if (![userDefaults objectForKey:CONVERSION_VALUE_KEY]) {
          return nil;
      }
      return @([userDefaults integerForKey:CONVERSION_VALUE_KEY]);
  }
  
  + (void)getConversionValueFromServer:(NSString*)eventName withCompletionHandler:(void(^)(int, NSError*))handler {
      if (!lockObject) {
          lockObject = [NSLock new];
      }
      
      // Making the lock async so it will not freeze the calling thread
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
          
          [lockObject lock];
          
          NSURLComponents *components = [NSURLComponents componentsWithString:SINGULAR_API_URL];
          
          NSString* bundleIdentifier = @"";
          
          components.queryItems = @[
              [NSURLQueryItem queryItemWithName:@"a" value:@""],
              [NSURLQueryItem queryItemWithName:@"i" value:bundleIdentifier],
              [NSURLQueryItem queryItemWithName:@"app_v" value:@""],
              [NSURLQueryItem queryItemWithName:@"n" value:eventName],
              [NSURLQueryItem queryItemWithName:@"p" value:@"iOS"],
              [NSURLQueryItem queryItemWithName:@"idfv" value:@""],
              [NSURLQueryItem queryItemWithName:@"idfa" value:@""],
              [NSURLQueryItem queryItemWithName:@"conversion_value" 
value:[[SKANSnippet getConversionValue] stringValue]], [NSURLQueryItem queryItemWithName:@"total_revenue_by_currency"
value:[SKANSnippet dictionaryToJsonString:[SKANSnippet getTotalRevenue]]], [NSURLQueryItem queryItemWithName:@"first_call_to_skadnetwork_timestamp"
value:[NSString stringWithFormat:@"%ld", [SKANSnippet getFirstSkanCallTimestamp]]], [NSURLQueryItem queryItemWithName:@"last_call_to_skadnetwork_timestamp"
value:[NSString stringWithFormat:@"%ld", [SKANSnippet getLastSkanCallTimestamp]]], ]; [[[NSURLSession sharedSession] dataTaskWithURL:components.URL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { [lockObject unlock]; if (handler) { handler(-1, error); } return; } NSDictionary* parsedResponse = [SKANSnippet jsonDataToDictionary:data]; if (!parsedResponse) { [lockObject unlock]; if (handler) { handler(-1, [NSError errorWithDomain:bundleIdentifier code:0 userInfo:@{NSLocalizedDescriptionKey:@"Failed parsing server response"}]); } return; } NSNumber *conversionValue = [parsedResponse objectForKey:@"conversion_value"]; if (!conversionValue) { [lockObject unlock]; NSString *status = [parsedResponse objectForKey:@"status"]; if (!status || ![status isEqualToString:@"ok"]) { if (handler) { NSString *reason = [parsedResponse objectForKey:@"reason"]; if (!reason) { reason = @"Got error from server"; } handler(-1, [NSError errorWithDomain:bundleIdentifier code:0 userInfo:@{NSLocalizedDescriptionKey:reason}]); } } return; } NSNumber* currentValue = [SKANSnippet getConversionValue]; if ([conversionValue intValue] <= [currentValue intValue]) { [lockObject unlock]; return; } [SKANSnippet setConversionValue:[conversionValue intValue]]; [SKANSnippet setLastSkanCallTimestamp]; if (![SKANSnippet getFirstSkanCallTimestamp]) { [SKANSnippet setFirstSkanCallTimestamp]; } [lockObject unlock]; if (handler) { handler([conversionValue intValue], error); } }] resume]; }); } + (BOOL)isSkanUpdateWindowOver { NSInteger timeDiff = [SKANSnippet getCurrentUnixTimestamp] -
[SKANSnippet getLastSkanCallTimestamp]; return SECONDS_PER_DAY <= timeDiff; }

SKAdNetwork 메타데이터 및 유틸리티 메서드

이 섹션에서는 위의 구현에서 사용되는 유틸리티를 구현합니다. 이러한 유틸리티는 다음 전환 값을 계산하는 데 필요한 메타데이터를 유지하고 저장하는 데 매우 중요합니다.

다음과 같은 유틸리티가 구현되어 있습니다:

사용자 기본값에 값 저장(및 검색):

  • 기본 SKAdNetwork API에 대한 첫 번째 호출 타임스탬프
  • 기본 SKAdNetwork API에 대한 마지막 호출 타임스탬프
  • 통화별 총 수익

서로 다른 표현 간의 값 변환:

  • 사전을 JSON 문자열로
  • JSON을 사전으로
+ (NSInteger)getFirstSkanCallTimestamp {
      NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
      return [userDefaults integerForKey:FIRST_SKAN_CALL_TIMESTAMP];
  }
  
  + (NSInteger)getLastSkanCallTimestamp {
      NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
      return [userDefaults integerForKey:LAST_SKAN_CALL_TIMESTAMP];
  }
  
  + (NSDictionary*)getTotalRevenue {
      NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
      if (![userDefaults objectForKey:TOTAL_REVENUE_BY_CURRENCY_KEY]) {
          return [NSDictionary new];
      }
      return [userDefaults objectForKey:TOTAL_REVENUE_BY_CURRENCY_KEY];
  }
  
  + (NSInteger)getCurrentUnixTimestamp {
      return [[NSDate date]timeIntervalSince1970];
  }
  
  + (void)setConversionValue:(int)value {
      if (@available(iOS 14.0, *)) {
          if (value <= [[SKANSnippet getConversionValue] intValue]) {
              return;
          }
          
          [SKAdNetwork updateConversionValue:value];
          
          NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
          [userDefaults setInteger:value forKey:CONVERSION_VALUE_KEY];
          [userDefaults synchronize];
      }
  }
  
  + (void)setFirstSkanCallTimestamp {
      NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
      [userDefaults setInteger:[SKANSnippet getCurrentUnixTimestamp] 
forKey:FIRST_SKAN_CALL_TIMESTAMP]; [userDefaults synchronize]; } + (void)setLastSkanCallTimestamp { NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; [userDefaults setInteger:[SKANSnippet getCurrentUnixTimestamp]
forKey:LAST_SKAN_CALL_TIMESTAMP]; [userDefaults synchronize]; } + (void)setTotalRevenue:(NSDictionary *)values { NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; [userDefaults setObject:values forKey:TOTAL_REVENUE_BY_CURRENCY_KEY]; [userDefaults synchronize]; } + (NSString*)dictionaryToJSONString:(NSDictionary*)dictionary { if (!dictionary || [dictionary count] == 0){ return @"{}"; } NSError *error; NSData *JSONData = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:&error]; if (error || !JSONData) { return @"{}"; } return [[NSString alloc] initWithData:JSONData
encoding:NSUTF8StringEncoding]; } + (NSDictionary*)JSONDataToDictionary:(NSData*)JSONData { if (!JSONData) { return nil; } NSError * error; NSDictionary * parsedData = [NSJSONSerialization
JSONObjectWithData:JSONData options:kNilOptions error:&error]; if (error || !parsedData) { return nil; } return parsedData; } @end

S2S 연동 업데이트 - 구현(선택 사항)

다음 SKAdNetwork 메타데이터로 S2S 연동을 업데이트하세요(이 메타데이터는 SKAdNetwork 구현 유효성 검사를 위해 싱귤러에 보고되는 모든 세션 및 이벤트에 대해 전달되어야 합니다):

  • skan_conversion_value - 최근 전환 값
  • skan_first_call_timestamp - 기본 SkAdNetwork API에 대한 첫 번째 호출의 유닉스 타임스탬프입니다.
  • skan_last_call_timestamp - 기본 SkAdNetwork API에 대한 마지막 호출의 유닉스 타임스탬프입니다.

다음 코드 스니펫은 이러한 값을 추출하는 방법을 보여줍니다:

NSDictionary *values = @{
      @"skan_conversion_value":
        [[SKANSnippet getConversionValue] stringValue]],
      @"skan_first_call_timestamp": 
[NSString stringWithFormat:@"%ld", [SKANSnippet getFirstSkanCallTimestamp]], @"skan_last_call_timestamp":
[NSString stringWithFormat:@"%ld", [SKANSnippet getLastSkanCallTimestamp]] };

이제 이러한 파라미터를 서버 측으로 전송한 후에는 서버 간 API 엔드포인트를 통해 파라미터를 전달할 수 있습니다. 자세한 내용은 서버 간 API 참조에서 이러한 매개변수를 검색하세요.

앱 라이프사이클 흐름 예시

// 앱 실행 시
  [SKANSnippet registerAppForAdNetworkAttribution];
  
  // 각 세션이 처리된 후
  [SKANSnippet updateConversionValueAsync:handler];
  
  // 비수익 이벤트 처리 후
  [SKANSnippet updateConversionValueAsync:@"event_name" 
withCompletionHandler:handler]; // 수익 이벤트 처리 후 [SKANSnippet updateRevenue:15.53 andCurrency:@"KRW"]; [SKANSnippet updateConversionValueAsync:@"revenue_event_name" withCompletionHandler:handler];

코드 변경 로그

  • 2020년 10월 1일
    • 중요] 수정:`getConversionValueFromServer`의 잠금 메커니즘이올바르게 초기화되지않았습니다.
    • 개선됨: 서버 응답에서 실패 이유 반환
  • 2020년 9월 23일
    • 수정 [중요]:`setConversionValue`가 이제 `updateConversionValue`를 호출합니다.
    • 개선: 서버에서 최신 전환 값 검색 시 오류 처리 개선
  • 2020년 9월 15일
    • 변경됨 [중요]: 전환 값이 설정되지 않은 경우, `getConversionValue`가 반환하는 기본값은 null이 아닌 0입니다.
    • 개선되었습니다: 수익 보고 및 처리
    • 개선되었습니다: 비동기적으로 다음 전환 값 가져오기

구현 테스트

전환 관리

앱 흐름을 시뮬레이션하고 구현을 테스트하려면 전환 관리 프로덕션 엔드포인트(SINGULAR_API_URL )를 다음 테스트 엔드포인트로 바꾸세요.

    • https://skadnetwork-testing.singular.net/api/v1/conversion_value
    • API 키는 필요하지 않습니다.

제안된 테스트 흐름:

  • 앱 내에서 3개의 서로 다른 이벤트를 생성합니다.
      • 모든 것이 올바르게 구현되면
        • 각 이벤트가 발생할 때마다 updateConversionValueAsync가 호출되어야 합니다(기본 전환 값이 0으로 초기화되었는지 확인).
        • 테스트 엔드포인트는 앱에서 현재 전환 값을 수신하고 다음 값을 반환합니다.
    • 반환된 전환 값을 로깅하여 모든 것이 예상대로 작동하는지 확인합니다.
      • 테스트 엔드포인트는 0-2 사이의 값을 반환하도록 구현되어 있습니다.
      • 따라서 세 번째 호출 이후에는 빈 문자열을 반환하고 마지막 값이 최종 SKAdNetwork 전환 값으로 사용됩니다.