SKAdNetwork 개요
SKAdNetwork는 iOS에서 앱 설치 광고 캠페인을 개인정보 보호 친화적으로 측정할 수 있도록 Apple에서 제공하는 프레임워크입니다(자세히 알아보기). 이 프레임워크는 사용자의 식별 정보를 손상시키지 않고 앱 설치 캠페인의 전환율을 측정하는 데 도움이 됩니다.
SKAdNetwork는 어떻게 작동하나요?
SKAdNetwork에서 어트리뷰션 프로세스는 앱스토어가 Apple의 서버를 통해 수행합니다. 그런 다음 어트리뷰션 정보는 사용자 식별자 및 시간 정보에서 분리되어 네트워크로 전송됩니다.
광고를 클릭하고 스토어가 열리면 퍼블리싱 앱과 네트워크는 네트워크, 퍼블리셔, 캠페인 ID와 같은 몇 가지 기본 정보를 제공합니다. 광고주 앱이 실행되고 SKAdNetwork에 등록한 경우, 디바이스는 전환 성공 알림을 네트워크에 전송합니다. 이 알림은 광고 앱에서 보고할 수 있는 전환 값과 함께 첨부된 값을 보고합니다.
이 알림은 첫 실행 후 최소 24시간 후에 전송되며, 기기 또는 사용자 식별 정보는 포함되지 않습니다.
또한, 앱 스토어는 광고가 게재된 앱이 원본 광고와 퍼블리셔를 알지 못하도록 프로세스를 진행합니다. 따라서 네트워크는 인스톨하는 사용자에 대해 전혀 알지 못한 채 인스톨에 대한 알림을 받게 됩니다.
SKAdNetwork를 사용하면 어떤 이점이 있나요?
SKAdNetwork에는 몇 가지 주요 장점이 있습니다. 다음과 같은 정보를 모두 제공합니다:
- 동의 없이 작동하는 라스트 클릭 어트리뷰션
- 소스, 캠페인, 퍼블리셔분석 정보
- 인스톨 후 전환 값(최대 64개의 불연속형 값)
- 인스톨 검증을 위한 암호화 서명
그러나 현재 형태의 SKAdNetwork는 기본 기능만 제공하므로, 제대로 작동하려면 여러 주체 간의 신중한 구현과 조정이 필요합니다.
현재 SKAdNetwork의 몇 가지 한계는 다음과 같습니다:
- 사용자 수준 데이터 없음
- 뷰스루 어트리뷰션 없음
- 제한된 범위의 전환 값:
- 설치/재설치당 하나의 전환 이벤트만 가능
- 최대 64개의 전환 값(6비트)
- 제한된 세분성:
- 최대 100개의 캠페인 값
- 광고 그룹 및 크리에이티브 레벨에 대한 표시 없음
- LTV 없음/긴 코호트
- 사기 노출:
- 전환 값 자체에 서명되지 않음(조작 가능)
- 포스트백은 중복될 수 있습니다.
이러한 문제점을 극복하기 위해 싱귤러는 SKAdNetwork 구현을 위한 공개 표준을 발표하고, SKAdNEtwork를 탐색하는 데 도움이 되는 여러 블로그 게시물을 공개했습니다:
- SKAN: SKAdNetwork 구현을 위한 실용적인 표준
- SKAdNetwork 101 - 무엇인가요?여러분에게 어떤 의미가있나요?
- SKAdNetwork 코드: 광고 네트워크, 제휴매체사, 광고주를 위한 코드가 포함된 Github 리포지토리공개
- SKAdNetwork 테스트 방법: 단계별 지침
- 싱귤러 , IDFA를 대체하는 최초의 시장 지원 발표
- SKAdNetwork를 사용한 고급 측정: ROAS활용하기
- 보안이 강화된 SKAdNetwork 2.0: 신뢰 구축을 위한 원활한 설정
SKAdNetwork S2S 구현
단일 SKAdNetwork 솔루션은 다음과 같은 구성 요소로 이루어져 있습니다:
- SKAdNetwork를 구현하기 위한 클라이언트 측 코드
- 모든 네트워크의 포스트백 검증 및 집계
- 사기 방지:
- 서명 유효성 검사 및 트랜잭션 ID 중복 제거.
- 서명되지 않은 파라미터(전환 값 및 지오데이터)를 검증하기 위한 네트워크 보안 설정.
- 전환 가치 관리: 싱귤러의 대시보드에서 동적으로 구성할 수 있는 기능을 제공하여 설치 후 활동을 SKAdNetwork 전환 가치로 인코딩할 수 있습니다.
- 리포팅: 제한된 SKAdNetwork 캠페인 ID를 변환하고 더 많은 마케팅 파라미터와 세분화로 데이터를 보강합니다.
- 파트너 포스트백: 이벤트 및 수익으로 디코딩된 전환 가치를 이벤트 및 수익으로 변환하여 SKAdNetwork 포스트백을 전송하는 기능으로, 최적화에 매우 중요합니다.
서버 간 싱귤러 통합을 사용하는 고객의 경우, SKAdNetwork 구현은 크게 두 부분으로 구성됩니다:
- SKAdNetwork 클라이언트 측 구현: 이 부분은 앱을 SKAdNetwork에 등록하고 SKAdNetwork 전환 가치를 지능적으로 관리하는 데 매우 중요합니다. 즉, 이를 구현하면 SKAdNetwork 어트리뷰션 및 관련 인스톨 후 활동을 기반으로 캠페인을 최적화할 수 있습니다.
- S2S 통합 업데이트(선택 사항): 이 부분은 클라이언트 측 구현의 유효성을 검사하고 문제를 해결하는 데 중요합니다. 현재 싱귤러의 S2S 세션 및 이벤트 엔드포인트를 통해 싱귤러로 전송되는 이벤트와 세션을 SKAdNetwork 데이터(전환 값 및 업데이트 타임스탬프)로 보강함으로써, 싱귤러는 앱 측에서 구현이 제대로 이루어졌는지 검증할 수 있습니다.
SKAdNetwork 클라이언트 측 구현
싱귤러는 SKAdNetwork 등록 및 전환 값 관리를 지원하는 코드 샘플을 제공합니다. 이 코드 샘플은 다음과 같은 부분을 담당합니다:
- SKAdnetwork 지원 및 등록
- 전환 가치 관리:
- 이 코드는 싱귤러의 엔드포인트와 동기식으로 통신하여 구성된 전환 모델에 따라 다음 전환 값을 수신합니다. 이벤트/세션/수익을 보고하고, 이에 대한 응답으로 다음 전환 값(Singular의 대시보드에서 측정하도록 구성된 설치 후 활동을 나타내는 인코딩된 숫자)을 가져옵니다.
- 또한 이 코드는 유효성 검사 및 다음 전환 값 계산에 사용되는 SKAdnetwork 메타데이터를 수집합니다:
- 기본 SKAdNetwork 프레임워크에 대한 첫 번째 호출 타임스탬프
- 기본 SKAdNetwork 프레임워크에 대한 마지막 호출 타임스탬프
- 마지막으로 업데이트된 전환 값
S2S 연동 업데이트
필수
활성 전환 모델이 있으면 S2S 엔드포인트는 클라이언트 측 코드에서 업데이트할 다음 값을 포함하는 "conversion_value"라는 새 int 필드를 반환하기 시작합니다.
선택사항
통합을 검증하고 잠재적인 구현 문제를 해결하려면 코드 샘플을 사용하여 현재 S2S 통합을 확장하는 것이 좋습니다. 싱귤러의 S2S 이벤트 및 세션 엔드포인트는 이미 다음과 같은 추가 SKAdNetwork 파라미터 가져오기를 지원합니다:
- 최근 전환 값
- 기본 SKAdNetwork 프레임워크에 대한 호출의 마지막 타임스탬프
- 기본 SKAdNetwork 프레임워크에 대한 호출의 첫 번째 타임스탬프
통합 흐름
위의 다이어그램은 S2S 고객에 대한 SKAdNetwork 플로우를 보여줍니다:
- 첫째, 앱의 코드가 SKAdNetwork 전용 엔드포인트를 통해 싱귤러 서버와 통신하여 앱에서 발생하는 이벤트/세션/매출 이벤트를 기반으로 최신 전환 값을 동기식으로 가져오고, 이 값으로 SKAdNetwork 프레임워크를 업데이트합니다.
- 둘째, 앱은 나중에 유효성 검사에 사용될 SKAdNetwork 데이터로 기존 이벤트와 세션을 보강합니다.
- 앱이 새로운 전환 값으로 SKAdNetwork 프레임워크 업데이트를 완료하고 SKAdNetwork 타이머가 만료되면, SKAdNetwork 포스트백이 네트워크로 전송됩니다.
- 네트워크는 보안 설정 또는 일반 설정을 통해 이를 싱귤러로 전달합니다.
- 싱귤러는 포스트백을 처리합니다:
- 서명 유효성 검사
- 구성된 전환 모델을 기반으로 전환 값을 디코딩합니다.
- 파트너와의 통합 및 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 전환 값으로 사용됩니다.
-