요약: 앱스플라이어 SDK와 iOS 앱을 연동하여 인스톨, 인앱이벤트, 미디어소스 등을 측정합니다.
중요!
iOS SDK V5.4.4는 iOS 14에서 정상 작동합니다. 그러나 향후 출시되는 iOS 업데이트 버전과 호환할 수 있도록 SDK V6.X로 업데이트하기를 권장합니다. iOS SDK V6로 업데이트하기 & 플러그인 가이드를 참고하십시오.
Apple의 App Clip 어트리뷰션이 SDK V6.0.8 부터 지원됩니다.
Apple에 따르면, 앱 추적 투명성(ATT)은 iOS 14.5(2021년 봄)부터 시행될 예정입니다. ATT를 지원하는 iOS SDK 설정법을 숙지하세요.
1. 개요
iOS SDK V6은 앱 설치 및 이벤트 기록 기능을 지원합니다. iOS SDK V6은 안정적이고 안전하며, 가볍고 설치하기 쉽습니다.
설치, 업데이트, 세션 및 인앱 이벤트를 기록할 수 있습니다. 인앱 이벤트로 인앱 구매, 게임 레벨 등을 측정하여 ROI와 유저 가치를 평가할 수 있습니다.
iOS 버전 | 지원되는 어트리뷰션 모델 |
---|---|
11.3 이전 |
|
11.3–13 |
|
14+ |
|
* iOS SDK V5.4 이하
|
1.1 SDK 연동 - 필요한 작업
항목 | 목적 | 완료 후 |
---|---|---|
SDK 연동 |
SDK 연동 및 설정 방법을 설명합니다. |
|
핵심 API |
SDK 핵심 API 사용 방법을 설명합니다. 핵심 API로 인앱 이벤트와 수익을 측정하고, 딥링크를 구동시키고, 전환 데이터를 수집할 수 있습니다. |
인앱 이벤트 및 수익이 대시보드에 표시됩니다. 딥링킹을 동작시킬 수 있습니다. |
API 옵션 구현 방법과 용도를 설명합니다. 예를 들어, 앱 삭제 측정 API, 앱 추천 어트리뷰션 API, 푸시 알림 API 등이 있습니다. |
앱 삭제와 앱 추천 및 푸시 알림을 통한 사용자 참여 등을 측정할 수 있고, 개인정보보호 시나리오 등 여러 상황을 처리할 수 있습니다. |
|
개발자를 위한 SDK API 관련 빠른 참조 |
|
1.2 iOS 플랫폼과 SDK 호환성
- iOS SDK V6 베타는:
- iOS 버전 8 이상 및 tvOS 버전 9 이상이 설치된 모든 iOS 및 tvOS 기기(iPhone, iPod, iPad, Apple TV)와 호환됩니다.
- 애플의 IPv6 DNS64/NAT64 네트워크를 지원합니다.
참고
애플의 iOS 14 버전 앱 클립(App Clip)이 곧 출시됩니다!
앱스플라이어의 앱 클립 개발 가이드에서 상세 내용을 확인하세요.
이 탭은 앱 개발자를 대상으로 SDK를 구현하고 구동시키는 방법을 설명합니다. 이 탭의 내용을 완료하면, 앱 대시보드에 오가닉 인스톨 하나, 논오가닉 인스톨 하나, 총 두 인스톨이 표시됩니다.
2. 앱에 SDK 추가
2.1 SDK 다운로드하고 Xcode에 추가합니다.
- 최신 버전의 CocoaPods 를 다운로드하여 설치하십시오.
- 다음 행을
Podfile
에 추가하십시오.
pod 'AppsFlyerFramework'
pod install
을 실행하십시오.- 이 시전부터는
.xcodeproj
대신.xcworkspace
파일을 이용하여 Xcode에서 프로젝트를 엽니다.
참고
tvOS 앱을 개발하는 경우, CocoaPods는 AppsFlyerFramework의 관련 디펜던시를 자동으로 추가합니다.
- 최신 버전의 Carthage 를 설치하십시오.
- 다음 행을 Cartfile binary에 추가하십시오:
https://raw.githubusercontent.com/AppsFlyerSDK/AppsFlyerFramework/master/Carthage/appsflyer-ios.json
참고
위의 링크는 정적 라이브러리에 연결됩니다. 최신 iOS 버전으로 업그레이드하는 경우, 다음을 수행하십시오:
- Xcode에서 copy-frameworks를 실행하는 Run Script 단계를 제거하십시오.
- 라이브러리가 포함되어 있지 않도록 확인하십시오.
자세한 내용은 Carthage docs 안내를 참조하십시오.
현재는 tvOS 앱을 지원하지 않습니다.
- iOS SDK를 static framework로 다운로드하세요.여기에서 SDK static framework가 정상적으로 다운로드되었는지 검증하세요.
- 방금 다운받은 AppsFlyerLib.framework.zip 파일 압축을 해제합니다.
- AppsFlyerLib.framework을 드래그하여 Xcode 프로젝트에 드롭합니다.
- Copy items if needed(필요할 경우 항목 복사) 란에 체크 표시했는지 꼭 확인하세요.
참고
이 접근은 iOS 8 이상에만 호환됩니다.
tvOS 앱의 경우, 다른 AppsFlyerFramework가 필요합니다.
- 이 리포지토리를 복제(clone) 하십시오.
- 복제된 리포지토리의 이 폴더에서 AppsFlyerLib.framework를 찾으십시오.
- 3단계와 4단계를 반복하십시오.
2.2 iOS 네이티브 프레임워크 의존성(dependency)
앱스플라이어 iOS SDK V6.X 은 다음 네이티브 프레임워크를 자동 추가하고 사용합니다.
- AdSupport.framework
- 이 프레임워크는 기기에서 IDFA를 수집할 때 필요합니다.
IDFA가 없으면 페이스북, 트위터, 구글 애즈 및 기타 네트워크를 어트리뷰션할 수 없습니다. - iAd.framework
- 광고주 앱에서 Apple Search Ads의 퍼포먼스를 측정합니다.
- AdServices Framework (
V6.1.3+
) - 광고주 앱에서 Apple Search Ads의 퍼포먼스를 측정합니다.
이러한 프레임 워크를 제거하고 IDFA 수집을 비활성화 하고 싶으신 경우:
- IDFA 수집을 비활성화합니다.
- iAd.framework를 비활성화합니다.
Strict mode
앱스플라이어 SDK V6.X의 strict mode를 사용하여 IDFA 수집 기능 및 AdSupport 프레임워크 의존성을 완전히 제거합니다. (예를 들어 어린이용 앱 개발 시)
일러두기: IDFV는 계속 사용가능합니다.
Podfile
에서 AppsFlyerLib
의존성을 다음으로 대체합니다.
pod 'AppsFlyerFramework/Strict','6.1.1'
일러두기: strict mode를 사용하여 disableAdvertisingIdentifier
를 호출하면, 컴파일 오류가 발생합니다.
참고
앱스플라이어 SDK 4.10.4 이전 버전을 사용중인 경우, ad support 프레임워크를 수작업으로 추가해야 합니다.
add ad support 프레임워크 추가하기:
- Xcode 프로젝트에서 프로젝트 타겟을 선택하십시오.
- target에서 General 탭을 선택하십시오.
- Linked Frameworks and Libraries 섹션을 펼치십시오.
- 플러스 기호 (+) 를 클릭하여 프레임워크를 추가하십시오.
- AdSupport.framework를 검색하십시오.
- 목록에서 AdSupport.framework를 선택하십시오.
iAd.framework를 위해 프로세스를 반복하십시오.
3. SDK 연동 및 실행
이 섹션은 앱스플라이어 iOS SDK 구동 방법을 설명합니다.
3.1 앱스플라이어 dev key 가져오기
dev key는 앱스플라이어 계정 식별자 입니다. 앱스플라이어 SDK는 dev key를 이용해 귀사의 계정에 속한 데이터를 안전하게 송수신합니다.
SDK 키 받기
- 앱스플라이어 대시보드에서 설정 < 앱 설정 메뉴로 갑니다.
- dev키를 복사하십시오. 다음 단계에 필요합니다.
3.2 SDK 초기화 하기
참고
SceneDelegate
를 사용하는 iOS앱을 지원하려면 SceneDelegate로 SDK 초기화를 참조하십시오.
아래 코드는 예시입니다. 필요에 따라 <AF_DEV_KEY>
및 <APPLE_APP_ID>
플레이스홀더를 변경하십시오.
AppDelegate.h
파일에서
AppsFlyerLib/AppsFlyerLib.h
헤더를 import 합니다.AppsFlyerLibDelegate
를AppDelegate
인터페이스 선언에 추가합니다.
#import <UIKit/UIKit.h>
#import <AppsFlyerLib/AppsFlyerLib.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate, AppsFlyerLibDelegate>
@end
AppDelegate.m
파일에서
didFinishLaunchingWithOptions
에서- 전 단계에서 복사한 앱스플라이어 dev key
- Apple 앱 ID
- IDFA를 수집하기 위해, 앱 추적 투명성(ATT, App Tracking Transparency) 지원을 설정하십시오.
- 추가 설정
onConversionDataSuccess
와onConversionDataFail
콜백을 오버라이드(override)하여 전환을 처리하고 디퍼드 딥링킹이 가능하도록 합니다.onAppOpenAttribution
와onAppOpenAttributionFailure
콜백을 오버라이드(override)하여 어트리뷰션과 다이렉트 딥링킹이 가능하도록 합니다.didReceiveRemoteNotification
콜백에서handlePushNotification
을 호출하여 푸시 알림 리인게이지먼트를 어트리뷰션합니다.applicationDidBecomeActive
콜백에서start
를 호출합니다.
#import "AppDelegate.h"
#import <UserNotifications/UserNotifications.h>
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
/** APPSFLYER INIT **/
[AppsFlyerLib shared].appsFlyerDevKey = @"<AF_DEV_KEY>";
[AppsFlyerLib shared].appleAppID = @"<APPLE_APP_ID>";
[AppsFlyerLib shared].delegate = self;
/* Set isDebug to true to see AppsFlyer debug logs */
[AppsFlyerLib shared].isDebug = true;
if (@available(iOS 10, *)) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
} else {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes: UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}
[[UIApplication sharedApplication] registerForRemoteNotifications];
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[AppsFlyerLib shared] start];
}
// Deep linking
// Open URI-scheme for iOS 9 and above
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *) options {
[[AppsFlyerLib shared] handleOpenUrl:url options:options];
return YES;
}
// Open URI-scheme for iOS 8 and below
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation {
[[AppsFlyerLib shared] handleOpenURL:url sourceApplication:sourceApplication withAnnotation:annotation];
return YES;
}
// Open Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
[[AppsFlyerLib shared] continueUserActivity:userActivity restorationHandler:restorationHandler];
return YES;
}
// Report Push Notification attribution data for re-engagements
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[AppsFlyerLib shared] handlePushNotification:userInfo];
}
// AppsFlyerLib implementation
//Handle Conversion Data (Deferred Deep Link)
-(void)onConversionDataSuccess:(NSDictionary*) installData {
id status = [installData objectForKey:@"af_status"];
if([status isEqualToString:@"Non-organic"]) {
id sourceID = [installData objectForKey:@"media_source"];
id campaign = [installData objectForKey:@"campaign"];
NSLog(@"This is a non-organic install. Media source: %@ Campaign: %@",sourceID,campaign);
} else if([status isEqualToString:@"Organic"]) {
NSLog(@"This is an organic install.");
}
}
-(void)onConversionDataFail:(NSError *) error {
NSLog(@"%@",error);
}
//Handle Direct Deep Link
- (void) onAppOpenAttribution:(NSDictionary*) attributionData {
NSLog(@"%@",attributionData);
}
- (void) onAppOpenAttributionFailure:(NSError *)error {
NSLog(@"%@",error);
}
@end
AppDelegate.swift
파일에서
AppsFlyerLib
를 import 합니다.AppsFlyerLibDelegate
를AppDelegate
클래스 선언에 추가합니다.didFinishLaunchingWithOptions
에서- AppsFlyer dev key
- app ID를 설정합니다.
- IDFA를 수집하기 위해, 앱 추적 투명성(ATT, App Tracking Transparency) 지원을 설정하십시오.
- 추가 설정
onConversionDataSuccess
와onConversionDataFail
콜백을 오버라이드(override)하여 전환을 처리하고 디퍼드 딥링킹이 가능하도록 합니다.onAppOpenAttribution
와onAppOpenAttributionFailure
콜백을 오버라이드(override)하여 어트리뷰션과 다이렉트 딥링킹이 가능하도록 합니다.didReceiveRemoteNotification
콜백에서handlePushNotification
을 호출하여 푸시 알림 리인게이지먼트를 어트리뷰션합니다.applicationDidBecomeActive
콜백에서start
를 호출합니다.
import UIKit
import AppsFlyerLib
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let defaults = UserDefaults.standard
//MARK: LifeCycle
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppsFlyerLib.shared().appsFlyerDevKey = "<AF_DEV_KEY>"
AppsFlyerLib.shared().appleAppID = "<APPLE_APP_ID>"
AppsFlyerLib.shared().delegate = self
AppsFlyerLib.shared().isDebug = true
// iOS 10 or later
if #available(iOS 10, *) {
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .alert, .sound]) { _, _ in }
application.registerForRemoteNotifications()
}
// iOS 9 support - Given for reference. This demo app supports iOS 13 and above
else {
application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil))
application.registerForRemoteNotifications()
}
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Start the SDK (start the IDFA timeout set above, for iOS 14 or later)
AppsFlyerLib.shared().start()
}
// Open Univerasal Links
// For Swift version < 4.2 replace function signature with the commented out code:
// func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
print(" user info \(userInfo)")
AppsFlyerLib.shared().handlePushNotification(userInfo)
}
// Open Deeplinks
// Open URI-scheme for iOS 8 and below
private func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
AppsFlyerLib.shared().continue(userActivity, restorationHandler: restorationHandler)
return true
}
// Open URI-scheme for iOS 9 and above
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
AppsFlyerLib.shared().handleOpen(url, sourceApplication: sourceApplication, withAnnotation: annotation)
return true
}
// Report Push Notification attribution data for re-engagements
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
AppsFlyerLib.shared().handleOpen(url, options: options)
return true
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
AppsFlyerLib.shared().handlePushNotification(userInfo)
}
// Reports app open from deep link for iOS 10 or later
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
AppsFlyerLib.shared().continue(userActivity, restorationHandler: nil)
return true
}
}
//MARK: AppsFlyerLibDelegate
extension AppDelegate: AppsFlyerLibDelegate{
// Handle Organic/Non-organic installation
func onConversionDataSuccess(_ installData: [AnyHashable: Any]) {
print("onConversionDataSuccess data:")
for (key, value) in installData {
print(key, ":", value)
}
if let status = installData["af_status"] as? String {
if (status == "Non-organic") {
if let sourceID = installData["media_source"],
let campaign = installData["campaign"] {
print("This is a Non-Organic install. Media source: \(sourceID) Campaign: \(campaign)")
}
} else {
print("This is an organic install.")
}
if let is_first_launch = installData["is_first_launch"] as? Bool,
is_first_launch {
print("First Launch")
} else {
print("Not First Launch")
}
}
}
func onConversionDataFail(_ error: Error) {
print(error)
}
//Handle Deep Link
func onAppOpenAttribution(_ attributionData: [AnyHashable : Any]) {
//Handle Deep Link Data
print("onAppOpenAttribution data:")
for (key, value) in attributionData {
print(key, ":",value)
}
}
func onAppOpenAttributionFailure(_ error: Error) {
print(error)
}
}
3.2.1 SceneDelegate로 SDK를 초기화합니다.
SceneDelegate
를 사용할 경우에만 이 초기화 메서드를 사용합니다.
SceneDelegate
를 사용하면, applicationDidBecomeActive
를 호출할 수 없기 때문에, UIApplicationDidBecomeActiveNotification
를 대신 사용하여 SDK를 초기화합니다. 아래 예시를 확인하십시오.
아래 코드는 예시입니다. 필요에 따라 <AF_DEV_KEY>
및 <APPLE_APP_ID>
플레이스홀더를 변경하십시오.
AppDelegate.h
파일에서
AppsFlyerLib/AppsFlyerLib.h
헤더를 import 합니다.AppsFlyerLibDelegate
를AppDelegate
인터페이스 선언에 추가합니다.
#import <AppsFlyerLib/AppsFlyerLib.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate, AppsFlyerLibDelegate>
@end
AppDelegate.m
파일에서
didFinishLaunchingWithOptions
콜백에서 다음을 설정합니다.- AppsFlyer dev key
- app ID를 설정합니다.
- IDFA를 수집하기 위해, 앱 추적 투명성(ATT, App Tracking Transparency) 지원을 설정하십시오.
- 추가 설정
onConversionDataSuccess
와onConversionDataFail
콜백을 오버라이드(override)하여 전환을 처리하고 디퍼드 딥링킹이 가능하도록 합니다.onAppOpenAttribution
와onAppOpenAttributionFailure
콜백을 오버라이드(override)하여 어트리뷰션과 다이렉트 딥링킹이 가능하도록 합니다.didReceiveRemoteNotification
콜백에서handlePushNotification
을 호출하여 푸시 알림 리인게이지먼트를 어트리뷰션합니다.sendLaunch
콜백에서start
를 호출합니다.
#import "AppDelegate.h"
#import <AppsFlyerLib/AppsFlyerLib.h>
#import <UserNotifications/UserNotifications.h>
@interface AppDelegate ()
@end
@implementation AppDelegate
// Start the AppsFlyer SDK
- (void)sendLaunch:(UIApplication *)application {
[[AppsFlyerLib shared] start];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
/** APPSFLYER INIT **/
[AppsFlyerLib shared].appsFlyerDevKey = @"<AF_DEV_KEY>";
[AppsFlyerLib shared].appleAppID = @"<APPLE_APP_ID>";
[AppsFlyerLib shared].delegate = self;
/* Set isDebug to true to see AppsFlyer debug logs */
[AppsFlyerLib shared].isDebug = true;
// Use UIApplicationDidBecomeActiveNotification to start the SDK
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(sendLaunch:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
if (@available(iOS 10, *)) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
} else {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes: UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}
[[UIApplication sharedApplication] registerForRemoteNotifications];
return YES;
}
// Report Push Notification attribution data for re-engagements
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[AppsFlyerLib shared] handlePushNotification:userInfo];
}
// AppsFlyerLib implementation
//Handle Conversion Data (Deferred Deep Link)
-(void)onConversionDataSuccess:(NSDictionary*) installData {
id status = [installData objectForKey:@"af_status"];
if([status isEqualToString:@"Non-organic"]) {
id sourceID = [installData objectForKey:@"media_source"];
id campaign = [installData objectForKey:@"campaign"];
NSLog(@"This is a none organic install. Media source: %@ Campaign: %@",sourceID,campaign);
} else if([status isEqualToString:@"Organic"]) {
NSLog(@"This is an organic install.");
}
}
-(void)onConversionDataFail:(NSError *) error {
NSLog(@"%@",error);
}
//Handle Direct Deep Link
- (void) onAppOpenAttribution:(NSDictionary*) attributionData {
NSLog(@"%@",attributionData);
}
- (void) onAppOpenAttributionFailure:(NSError *)error {
NSLog(@"%@",error);
}
// support for scene delegate
#pragma mark - UISceneSession lifecycle
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13)){
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
}
- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions API_AVAILABLE(ios(13.0)){
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
@end
SceneDelegate.m
파일에서
openURLContexts
콜백에서,handleOpenUrl
를 호출하십시오.continueUserActivity
콜백에서,continueUserActivity
를 호출하십시오.
#import "SceneDelegate.h"
@interface SceneDelegate ()
@end
@implementation SceneDelegate
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts API_AVAILABLE(ios(13.0)){
NSURL* url = [[URLContexts allObjects] objectAtIndex:0].URL;
if(url){
[[AppsFlyerLib shared] handleOpenUrl:url options:nil];
}
}
- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity API_AVAILABLE(ios(13.0)){
[[AppsFlyerLib shared]continueUserActivity:userActivity restorationHandler:nil];
}
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
NSUserActivity *activity = [[connectionOptions userActivities] allObjects].firstObject;
if (activity) {
[self scene:scene continueUserActivity:activity];
}
[self scene:scene openURLContexts: [connectionOptions URLContexts]];
}
- (void)sceneDidDisconnect:(UIScene *)scene API_AVAILABLE(ios(13.0)){
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
- (void)sceneDidBecomeActive:(UIScene *)scene API_AVAILABLE(ios(13.0)){
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
- (void)sceneWillResignActive:(UIScene *)scene API_AVAILABLE(ios(13.0)){
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
- (void)sceneWillEnterForeground:(UIScene *)scene API_AVAILABLE(ios(13.0)){
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
- (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0)){
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
@end
AppDelegate.swift
파일에서
AppsFlyerLib
를 import 합니다.AppsFlyerLibDelegate
를AppDelegate
클래스 선언에 추가하십시오.didFinishLaunchingWithOptions
콜백에서 다음을 설정하십시오.- AppsFlyer dev key
- app ID를 설정합니다.
- IDFA를 수집하기 위해, 앱 추적 투명성(ATT, App Tracking Transparency) 지원을 설정하십시오.
- 추가 설정
onConversionDataSuccess
와onConversionDataFail
콜백을 오버라이드(override)하여 전환을 처리하고 디퍼드 딥링킹이 가능하도록 합니다.onAppOpenAttribution
와onAppOpenAttributionFailure
콜백을 오버라이드(override)하여 어트리뷰션과 다이렉트 딥링킹이 가능하도록 합니다.didReceiveRemoteNotification
콜백에서handlePushNotification
을 호출하여 푸시 알림 리인게이지먼트를 어트리뷰션합니다.sendLaunch
콜백에서start
를 호출합니다.
import UIKit
import AppsFlyerLib
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, AppsFlyerLibDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppsFlyerLib.shared().appsFlyerDevKey = "<AF_DEV_KEY>"
AppsFlyerLib.shared().appleAppID = "<APPLE_APP_ID>"
AppsFlyerLib.shared().delegate = self
AppsFlyerLib.shared().isDebug = true
NotificationCenter.default.addObserver(self, selector: NSSelectorFromString("sendLaunch"), name: UIApplication.didBecomeActiveNotification, object: nil)
return true
}
@objc func sendLaunch() {
AppsFlyerLib.shared().start()
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
}
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
//MARK: -GCD
func onConversionDataSuccess(_ installData: [AnyHashable: Any]) {
print("onConversionDataSuccess data:")
for (key, value) in installData {
print(key, ":", value)
}
if let status = installData["af_status"] as? String {
if (status == "Non-organic") {
if let sourceID = installData["media_source"],
let campaign = installData["campaign"] {
print("This is a Non-Organic install. Media source: \(sourceID) Campaign: \(campaign)")
}
} else {
print("This is an organic install.")
}
if let is_first_launch = installData["is_first_launch"] as? Bool,
is_first_launch {
print("First Launch")
} else {
print("Not First Launch")
}
}
}
func onConversionDataFail(_ error: Error!) {
if let err = error{
print(err)
}
}
func onAppOpenAttribution(_ attributionData: [AnyHashable : Any]!) {
if let data = attributionData{
print("\(data)")
}
}
func onAppOpenAttributionFailure(_ error: Error!) {
if let err = error{
print(err)
}
}
}
SceneDelegate.swift
파일에서
AppsFlyerLib
를 import 합니다.openURLContexts
콜백에서,AppsFlyerLib.shared().handleOpen
를 호출하십시오.continue
콜백에서,AppsFlyerLib.shared().continue
를 호출하십시오.
import UIKit
import AppsFlyerLib
@available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Processing Universal Link from the killed state
if let userActivity = connectionOptions.userActivities.first {
self.scene(scene, continue: userActivity)
}
// Processing URI-scheme from the killed state
self.scene(scene, openURLContexts: connectionOptions.urlContexts)
guard let _ = (scene as? UIWindowScene) else { return }
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
AppsFlyerLib.shared().continue(userActivity, restorationHandler: nil)
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
AppsFlyerLib.shared().handleOpen(url, options: nil)
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}
3.3 SKAdNetwork 어트리뷰션 지원 설정
참고
SKAdNetwork 귀속을 완전히 지원하려면 iOS SDK V6.2.3+
로 업그레이드하십시오.
SKAdNetwork는 iOS가 사용하는 클래스이며, 앱 설치가 광고에 기인해서 발생했는지 검증합니다. 이 앱 설치 유효성 검증 프로세스에는 소스(source) 앱과 광고 대상 앱이 있습니다.
소스(source) 앱은 광고 네트워크가 서명한 광고를 자신의 지면에 게재하여 광고 캠페인에 참여하는 앱입니다. 광고를 게재하도록 앱을 구성하는 작업은 앱스플라이어 SDK 범위 내에 없습니다. 광고 게재를 위한 작업은Apple 가이드를 따르십시오.
(앱스플라이어 SDK가 설치된) 광고 대상 앱은 SKAdNetwork 솔루션은 SKAdNetwork를 이용하여 어트리뷰션 포스트백을 제공합니다. 앱스플라이어는 데이터를 수집, 해석, 집약하고 동시에 개인정보를 보호합니다. 앱이 설치 후 처음 실행되면, 앱스플라이어 플랫폼은 마케터가 설정한 상태에 따라 SDK에 SKAdNetwork 전환 값을 설정하는 방식을 지시합니다.
SKAdNetwork Solution를 사용하기 위해:
- 개발자가 할 일은 없습니다.
-
앱스플라이어 SDK는 자동으로 필요한 SKAdNetwork API인
registerAppForAdNetworkAttribution()
와updateConversionValue()
를 호출합니다. 이 API들을 개발자가 임의로 호출해서는 안됩니다. - 앱스플라이어 외 다른 SDK가 SKAdNet API를 호출하지 않도록 하십시오. 다른 SDK가 호출하면, iOS가 앱스플라이어에 보내는 포스트백이 지연될 수 있고, 앱스플라이어 SKAdNetwork 대시보드사용자 가치 측정 지표에 사용되는 전환값이 변경될 수 있습니다.
- 마케터는앱스플라이어에서 SKAdNetwork 측정 방식을 설정해야 합니다.
앱스토어에서 개발자나 마케터가 수행해야 할 작업이나 등록 절차는 없습니다.
SKAdNetwork 어트리뷰션을 사용하지 않으려면 disableSKAdNetwork API를 사용하십시오.
3.4 SDK 실행을 늦추도록 설정
사용자의 동의(예: GDPR 또는 COPPA가 요하는 개인 정보 공유 동의)를 얻을 때까지, SDK 실행과 데이터 전송을 지연시킬 수 있습니다.
일러두기: 이는 iOS 14에 도입된 앱 추적 투명성(ATT, App Tracking Transparency)과 관련이 없습니다.
SDK 시행을 지연시키려면:
- 세션이 배경(background)-전경(foreground) 전환 시 전송되지 않도록 합니다. sendLaunch() 메서드 (위 3.3번 참조, applicationDidBecomeActive마다 호출됨)에서,
start
와 컨디션 체크로 호출을 래핑(wrapping)합니다. 예:
-(void)sendLaunch:(UIApplication *)application { if (consent_given) { // check the condition [[AppsFlyerLib shared] start]; // Start } }
-
SDK 실행을 지연할 이유가 사라지면, 즉, 컨디션이 변경되면 바로 세션을 전송합니다. 첫 번째 세션 데이터를 전송할 준비가 되었으면,
start
를 호출합니다. 예:
... consent_given = YES; // change the condition [[AppsFlyerLib shared] start]; // Start ...
4. 인스톨 테스트
이제 오가닉 및 논오가닉 인스톨을 시뮬레이션하여 SDK 연동을 테스트 할 수 있습니다.
더 자세한 테스트 시나리오와 가이드를 SDK 연동 테스트에서 확인하세요.
참고
SDK 연동 테스트 및 디버깅을 완료하면 SDK 로그를 끕니다.
4.1 테스트 기기 등록
앱 인스톨 테스트 사전 준비사항
- 테스트 기기에 테스트할 앱이 설치되지 않았음을 확인하십시오.
- 테스트에 사용하려는 기기를 등록하세요.
4.2 오가닉 인스톨 시뮬레이션
오가닉 설치는 보통 앱스토어에서 직접 다운로드 받는 인스톨로 어트리뷰트 되지 않는 앱 설치입니다.
오가닉 인스톨을 시뮬레이션하려면:
- 컴퓨터에 모바일 기기가 잘 연결되어 있는지 확인하십시오.
- Xcode에서 디버그 터미널을 엽니다.
- Xcode Studio에서 모바일 기기 또는 시뮬레이터에 앱을 설치하십시오.
- 앱이 시작될 때까지 기다립니다.
- 디버그 터미널에서 앱 패키지 이름을 찾으십시오.
다음이 표시되어야 합니다:
로그의 Send start()는 SDK가 인스톨을 보고함을 의미합니다. 이 데이터는 app delegate의 onConversionDataSuccess
메서드에서 가져옵니다. 전환 데이터 가져오기는 이 가이드 후반부에 설명되어 있습니다.
참고: SDK 버전 5 부터, onConversionDataSuccess
는 전환 데이터를 가져 오는 메소드의 이름입니다. SDK 버전 5.0.0 보다 낮은 버전을 사용하는 경우 메소드 이름은 onConversionDataReceived
입니다. SDK 5.0.0 이상으로 업그레이드 하는 것을 추천드립니다. 더 자세한 내용은 여기를 클릭하여 보실 수 있습니다.
이제 오가닉 인스톨이 앱 대시보드의 개요 페이지에 나타납니다.
앱 대시보드에서 오가닉 인스톨 숫자를 확인할 수 없으면 SDK 문제 해결 안내서 를 참조하십시오.
4.3 논오가닉 인스톨 시뮬레이션
논오가닉 인스톨은 보통 마케팅 캠페인에 참여하고 일어나는 앱 설치로, 어트리뷰트 성과로 기록되는 인스톨입니다. 어트리뷰션 링크를 사용하여 논오가닉 인스톨을 시뮬레이션 할 수 있습니다.
절차:
- 앱의 아이튠즈 ID 이름이 무엇인지 확인하십시오. 예: id123456789.
- 아래 URL에서 <APP_ID>부분 을 앱의 아이튠즈 ID로 바꾸십시오:
예시:https://app.appsflyer.com/<APP_ID>?pid=sdk_test&c=sdk_test
https://app.appsflyer.com/id0123456789?pid=sdk_test&c=sdk_test
pid
파라미터는 미디어 소스 이름을 나타냅니다.c
파라미터는 캠페인 이름을 나타냅니다. - 이 url을 기기로 전송합니다. 이메일이나 메신저 앱을 통해 url 링크를 보내면 됩니다.
- 기기에서 url을 클릭하십시오.
- 만약 앱이 이미 앱스토어에 등록되어 있다면 앱스토어로 리디렉트 될 것입니다. 앱스토어에서 앱을 다운로드 받지 마십시오. 5번 단계로 진행하십시오.
- 아직 앱이 앱 마켓에 등록되지 않았고 개발단계라면, 링크를 클릭했을 때 앱스토어에서 해당 앱을 찾을 수 없다는 메시지가 보여질 것입니다. 무시하고 5번 단계를 진행하십시오.
- Xcode Studio에서 디버그 터미널을 엽니다.
- USB 케이블을 통해 기기를 컴퓨터에 연결하십시오.
- Xcode를 통해 기기에 앱을 설치하십시오.
- 디버그 터미널에서 앱의 아이튠즈 ID 이름을 찾으십시오.
다음이 표시되어야 합니다:
이제 논오가닉 인스톨이 앱 대시보드의 개요 페이지에 나타납니다.
이 탭에서는 인앱 이벤트 및 수익을 기록하는 방법과 딥링크를 설정하는 방법에 대해 설명합니다.
인앱 이벤트 및 수익을 기록하면 사용자 퀄리티를 측정할 수 있습니다. 딥링크를 사용하면 사용자에게 더 나은 사용자 경험을 제공할 수 있습니다.
이 탭은 개발자 분들을 위한 안내이지만, 다음과 같은 이유로 마케팅 담당자가 꼭 입력해야하는 부분이 있습니다:
- 마케팅 담당자는 사용자 퀄리티를 측정하기 위해 어떤 인앱 이벤트를 기록해야하는지 결정해야합니다.
- 마케팅 담당자는 원링크(OneLink)를 통한 딥링킹을 설정하기 위해 앱스플라이어 대시보드에 접속해야만 합니다.
5. 인앱 이벤트 기록하기
인앱 이벤트를 통해 앱에서 어떤 일이 일어나는지 인사이트를 얻을 수 있습니다. 제일 먼저, 어떤 이벤트를 기록하고 싶은지 정의하는 것을 추천드립니다. 인앱 이벤트를 기록하면 ROI (Return on Investment)나 LTV (Lifetime Value)와 같은 성과지표들을 측정할 수 있습니다.
인앱 이벤트를 기록하는 방법에는 여러 가지가 있습니다. 가장 일반적인 방법은 SDK를 통해 이벤트를 보내는 것으로 아래에서 설명하고 있습니다. 인앱 이벤트를 기록하는 다른 방법에 대한 자세한 내용은 인앱 이벤트 개요 가이드 참고하십시오.
여행, 게임, 이커머스 등 앱이 속한 업종에 따라, 여기에서 업종별 추천 측정 인앱 이벤트를 참고하십시오.
5.1 인앱 이벤트 이름 및 파라미터
SDK에는 인앱 이벤트 관련 정보를 표현하는 두 가지 유형의 상수가 있습니다.
- 이벤트 이름 - 이 상수는
AFEventEventName
형식입니다.
예를 들어,AFEventPurchase
,AFEventAddToCart
등 입니다. - 이벤트 파라미터 - 이 상수는
AFEventParameterParameterName
형식입니다.
예를 들어,AFEventParameterRevenue
,AFEventParamaterContentId
등 입니다.
아래의 이유로, 이 두가지 상수를 사용하시는 것을 강하게 추천드립니다:
- 표준 방식의 이름 지정을 통해 앱스플라이어는 자동으로 페이스북, 구글, 트위터, 스냅챗과 같은 Self-reporting networks(SRNs)의 이벤트와 매핑할 수 있습니다.
- 이전 버전과의 호환성 - 앱스플라이어가 특정 이벤트의 이름이나 이벤트 파라미터를 변경하더라도, 기존의 구현 내용이 이전 버전과 호환됩니다.
이 두 상수를 쓰려면, Objective-C를 사용시 AppsFlyerLib.h를, Swift 사용시 AppsFlyerLib를 사용하십시오.
이것을 클래스 구현 파일에 넣으십시오.
#import AppsFlyerLib.h
이것을 Swift 클래스 파일에 넣으십시오.
import AppsFlyerLib
여기에서 추천하는 이벤트 이름과 구조의 리스트를 확인하십시오.
5.2 수익 값 (revenue) 기록
어떤 인앱 이벤트로도 수익 값을 보낼 수 있습니다. af_revenue
(AFEventParameterRevenue
) 이벤트 파라미터를 사용하여 수익을 인앱 이벤트의 일부로서 계산할 수 있습니다. 양수 또는 음수의 모든 숫자 값을 입력할 수 있습니다.
앱스플라이어가 대시보드와 로데이터에서 실제 수익으로 계산하는 유일한 이벤트 파라미터는 af_revenue
입니다. 자세한 사항은 여기를 확인하세요.
수익 값이 있는 이벤트를 보낼 때 다음을 명심하십시오:
- 화폐 통화 코드를 설정하려면 (아래 예시 참고), 반드시 3 글자 ISO 4217 코드 이어야 합니다. (기본은 USD).
- 다음 속성을 설정하여 어떤 이벤트에 대해서도 화폐 통화 코드를 설정할 수 있습니다:
- Objective-C:
[AppsFlyerLib shared].currencyCode = @"ZZZ";
, - Swift:
AppsFlyerLib.shared().currencyCode = "ZZZ"
- Objective-C:
- 수익 값에는 쉼표 구분 기호, 통화 기호 또는 텍스트가 절대로 포함되면 안됩니다. 예를 들어, 수익 값은 1234.56과 같은 형태가 되어야 합니다.
예 : 수익 값이 있는 인앱 구매 이벤트
[[AppsFlyerLib shared] logEvent: @"purchase"
withValues:@{
AFEventParamContentId:@"1234567",
AFEventParamContentType : @"category_a",
AFEventParamRevenue: @200,
AFEventParamCurrency:@"USD"
}];
AppsFlyerLib.shared().logEvent("purchase",
withValues: [
AFEventParamContentId:"1234567",
AFEventParamContentType : "category_a",
AFEventParamRevenue: 200,
AFEventParamCurrency:"USD"
]);
이 구매 이벤트는 수익 $200를 포함하고, 대시보드에 수익으로 표시됩니다.
마이너스 수익 기록하기
마이너스 수익을 기록해야하는 상황이 있을 수 있습니다.
예를 들어 사용자가 환불을 받거나 구독을 취소하는 경우,
[[AppsFlyerLib shared] logEvent: @"cancel_purchase"
withValues:@{
AFEventParamContentId:@"1234567",
AFEventParamContentType : @"category_a",
AFEventParamRevenue: @-1.99,
AFEventParamCurrency:@"USD"
}];
AppsFlyerLib.shared().logEvent("cancel_purchase",
withValues: [
AFEventParamContentId:"1234567",
AFEventParamContentType : "category_a",
AFEventParamRevenue: -1.99,
AFEventParamCurrency:"USD"
]);
참고
위의 코드에서 다음 사항을 참고하십시오:
- 수익 값은 마이너스 기호와 함께 표시됩니다.
- 대시보드 및 로데이터 보고서에서 마이너스 수익 이벤트를 식별할 수 있도록 이벤트 이름에는 "cancel_purchase"라는 특정한 값이 있습니다.
5.3 인앱 구매 검증
앱스플라이어 SDK는 인앱 구매에 대해서 서버 인증을 통해 유효성을 검증합니다. 인앱 구매를 검증하기 위해, validateAndLogInAppPurchase
를 호출합니다.
구매가 확인되면 이 호출은 자동으로 af_purchase
인앱 이벤트를 생성합니다.
이 호출에는 성공한 경우와 검증 실패 등의 원인으로 '실패'한 경우에 대한 두 개의 콜백 블록이 있습니다. 성공시 Apple의 서버에서 제공한 영수증 검증 데이터가 딕셔너리와 함께 반환됩니다.
구매 검증 사용 예시:
- (void) validateAndLogInAppPurchase:(NSString *) productIdentifier
price:(NSString *) price
currency:(NSString *) currency
transactionId:(NSString *) tranactionId
additionalParameters:(NSDictionary *) params
success:(void (^)(NSDictionary *response)) successBlock
failure:(void (^)(NSError *error, id reponse)) failedBlock;
[[AppsFlyerLib shared] validateAndLogInAppPurchase:@"ProductIdentifier" price:@"price"
currency:@"USD"
transactionId:@"transactionID"
additionalParameters:@{@"test": @"val" , @"test1" : @"val 1"}
success:^(NSDictionary *result){
NSLog(@"Purchase succeeded And verified!!! response: %@", result[@"receipt"]);
} failure:^(NSError *error, id response) {
NSLog(@"response = %@", response);
if([response isKindOfClass:[NSDictionary class]]) {
if([response[@"status"] isEqualToString:@"in_app_arr_empty"]){
// retry with 'SKReceiptRefreshRequest' because
// Apple has returned an empty response
// <YOUR CODE HERE>
}
} else {
//handle other errors
return;
}
}];
AppsFlyerLib
.shared()?
.validateAndLogInAppPurchase ("productIdentifier",
price: "price",
currency: "currency",
transactionId: "transactionId",
additionalParameters: [:],
success: {
guard let dictionary = $0 as? [String:Any] else { return }
dump(dictionary)
}, failure: { error, result in
guard let emptyInApp = result as? [String:Any],
let status = emptyInApp["status"] as? String,
status == "in_app_arr_empty" else {
// Try to handle other errors
return
}
// retry with 'SKReceiptRefreshRequest' because
// Apple has returned an empty response
// <YOUR CODE HERE>
})
샌드박스 환경에서 구매 검증을 테스트 할 때, 반드시 다음 코드를 추가하십시오:
[AppsFlyerLib shared].useReceiptValidationSandbox = YES;
AppsFlyerLib.shared().useReceiptValidationSandbox = true
이 코드는 프로덕션 빌드에서 제거해야 합니다.
구매 검증을 통과하면 앱스플라이어로 인앱 구매 이벤트가 자동 전송됩니다. 아래에서 event_value 파라미터에 전송된 샘플 데이터를 참고하십시오:
{
"some_parameter":"some_value", // from additional_event_values
"af_currency":"USD", // from currency
"af_content_id":"test_id", // from purchase
"af_revenue":"10", // from revenue
"af_quantity":"1", // from purchase
"af_validated":true // flag that AF verified the purchase
}
참고
validateAndLogInAppPurchase
를 호출하면 af_purchase 인앱 이벤트가 자동 생성됩니다. 그러므로 이 이벤트를 따로 직접 전송하면 인앱 구매 내역이 이중으로 기록됩니다.
5.4 인앱 이벤트 제한점
- 이벤트 이름: 45자 까지만 가능
- 이벤트 밸류: 1000자를 넘지 않아야 합니다. 더 긴 경우, 잘려서 보여집니다.
- 가격 및 수익: 숫자 또는 소수만 사용하십시오. (예: 5 또는 5.2)
- 가격 및 수익 값은 마침표 뒤로 최대 5 자리까지 가능합니다. 예, 5.12345
- 안드로이드 SDK 버전 4.8.1부터 인앱 이벤트나 다른 SDK API에서 영어가 아닌 문자도 사용할 수 있도록 지원합니다.
5.5 인앱 이벤트 기록 예시
logEvent
를 호출하여 이벤트 이름과 이벤트 값 파라미터로 인앱 이벤트를 기록할 수 있습니다. 더 자세한 내용은 인앱 이벤트 문서를 참조하십시오.
아래는 구매 이벤트를 기록한 간단한 예입니다. 업종에 따라 이미 정리해둔 코드 스니펫 리스트를 확인하시려면, 업종 별 리치 인앱 이벤트 가이드 항목을 참고하십시오.
예시: 인앱 구매 이벤트
[[AppsFlyerLib shared] logEvent:AFEventPurchase
withValues: @{
AFEventParamRevenue: @200,
AFEventParamCurrency: @"USD",
AFEventParamQuantity: @2,
AFEventParamContentId: @"092",
AFEventParamReceiptId: @"9277"
}];
참고
- 이벤트 SDK에 전달된 이벤트 밸류 딕셔너리는 NSJSONSerialization에 의한 JSON 변환에 유효해야합니다. 더 자세한 정보는 여기를 참고하십시오.
- 특수 문자 통화 기호는 인식하지 못하므로 수익 값에 부착하여 사용하지 마십시오.
AppsFlyerLib.shared().logEvent(AFEventPurchase,
withValues: [
AFEventParamRevenue: "200",
AFEventParamCurrency: "USD",
AFEventParamQuantity: 2,
AFEventParamContent: "shoes",
AFEventParamContentId: "092",
AFEventParamReceiptId: "9277"]);
5.6 오프라인 인앱 이벤트 기록하기
사용자가 인터넷에 연결되지 않았을 때 이벤트가 발생한 경우라도 앱스플라이어는 여전히 이벤트를 기록할 수 있습니다. 작동원리는 이렇습니다.
- SDK가 앱스플라이어 서버에 이벤트를 전송하고 응답을 기다립니다.
- 만약 SDK가 200 응답을 받지 못한다면, 이벤트가 캐시에 저장됩니다.
- 저장된 이벤트는 다음 200 응답을 받을 때 서버로 다시 전송됩니다.
- 캐시에 다수의 이벤트가 있는 경우에는 순서대로 하나씩 빠르게 서버로 전송됩니다.
참고
SDK의 캐시는 최대 40개의 이벤트를 저장할 수 있습니다. 따라서 오프라인 상태에서 발생한 첫 40개의 이벤트만 저장됩니다. 그 이후 다음 200 응답을 받을 때까지의 모든 이벤트는 저장되지 않고 버려집니다.
로데이터에 표시된 이벤트 시간(event time)은 기기가 다시 온라인 상태가 된 후 앱스플라이어로 이벤트가 전송된 시간입니다. 그러므로 이벤트가 실제로 일어난 시간이 아닙니다.
5.7 인앱 이벤트 기록시 성공 및 실패 처리
- 인앱 이벤트가 성공적으로 기록되었습니다.
- 인앱 이벤트를 기록할 때 오류가 발생했습니다.
[[AppsFlyerLib shared] logEventWithEventName:AFEventPurchase
eventValues: @{
AFEventParamRevenue: @200,
AFEventParamCurrency: @"USD",
AFEventParamQuantity: @2,
AFEventParamContentId: @"092",
AFEventParamReceiptId: @"9277"
}
completionHandler:^(NSDictionary<NSString *,id> * _Nullable dictionary, NSError * _Nullable error){
if(dictionary != nil) {
NSLog(@"In app callback success:");
for(id key in dictionary){
NSLog(@"Callback response: key=%@ value=%@", key, [dictionary objectForKey:key]);
}
}
if(error != nil) {
NSLog(@"In app callback error:", error);
}
}];
AppsFlyerLib.shared().logEvent(name: "In app event name", values: ["id": 12345, "name": "John doe"], completionHandler: { (response: [String : Any]?, error: Error?) in
if let response = response {
print("In app event callback Success: ", response)
}
if let error = error {
print("In app event callback ERROR:", error)
}
})
}
인앱 이벤트를 기록할 때 에러가 발생하면, 아래와 같이 에러 코드와 설명이 제공됩니다.
오류 코드 | 설명 |
---|---|
10 |
"Event timeout. Check 'minTimeBetweenSessions' param |
11 |
"Skipping event because 'isStopTracking' enabled" |
40 |
Network error: Error description comes from Android |
41 |
"No dev key" |
50 |
"Status code failure" + 서버에서 받은 실제 응답 코드 |
6. 원링크(OneLink)를 통한 딥링킹
원링크는 멀티 플랫폼 어트리뷰션과 유저를 앱 내 특정 콘텐츠로 보내는 딥링킹(deep linking)을 지원하는 앱스플라이어 솔루션입니다.
6.1 기기 확인 및 리다이렉션
OneLink는 클릭 시 기기 유형을 감지하고, 사용자를 일치하는 랜딩 페이지로 리다이렉션합니다. 예, 구글 플레이, iOS 앱스토어, 그 외 앱 마켓들 또는 웹 페이지.
원링크 리다이렉션 가이드에서 멀티 플랫폼 어트리뷰션 링크에 대해 설명하고 있습니다. (별도의 SDK 코드는 필요하지 않음). 딥링킹에 대한 기본 설명도 포함하고 있습니다.
6.2 딥링킹
딥링킹을 통해 사용자를 특정 활동 페이지로 보내고 그에 맞춘 컨텐츠를 제공할 수 있습니다. 리타겟팅 캠페인을 운영할 때 특히 유용합니다.
원링크로 딥링크를 설정하려면, 앱스플라이어 대시보드에 접속할 수 있는 마케팅 담당자와 앱 코드에 작업할 수 있는 개발자가 반드시 협업해야만 합니다.
원링크로 딥링킹 설정하기 가이드에서 상세 설명을 확인하십시오.
6.3 디퍼드 딥링킹 (Deferred deep linking)
디퍼드 딥링킹을 사용하면, 신규 사용자가 앱을 다운로드 받은 이후에 앱을 처음 실행할 때 딥링크 동작하여 특정 맞춤 콘텐츠를 제공 할 수 있습니다. 이는, 앱이 사용자 기기에 이미 설치되어 있어야 동작하는 일반적인 딥링크와 다릅니다.
원링크를 통해 디퍼드 딥링킹을 설정하려면, 앱 개발자도 앱스플라이어 대시보드에 접속할 수 있어야 합니다.
디퍼드 딥링킹 설정은 일반 딥링킹 설정과 동일합니다. 유일한 차이점은, 앱을 설치하고 시작한 후에 사용자를 딥링크하여 맞춤 콘텐츠를 보여줄 수 있도록, 앱에서 추가 논리를 구현해야한다는 것입니다.
자세한 내용은 디퍼드 딥링킹에 대한 가이드를 참고하십시오.
6.4 딥링크 데이터 얻기
SDK는 모든 설치와 딥링킹 이벤트 후에 전환(conversion) 또는 참여(engagement) 데이터를 제공합니다. 이 데이터를 사용하여 콘텐츠 및 앱의 동작을 계획적으로 맞춤 지정할 수 있습니다.
디렉트 딥링크가 사용되어 앱이 열렸을 때의 딥링킹 데이터를 얻으려면 onAppOpenAttribution 메서드를 구현하십시오.
언제든지 수동으로 딥링킹 리인게이지먼트 데이터를 얻으려면, performOnAppAttribution 메서드를 구현하십시오. 이를 통해, 새로운 리인게이지먼트를 기록하지 않고도 리인게이지먼트 데이터에 액세스 할 수 있습니다.
자세한 내용은 딥링크 데이터에 대한 가이드를 참고하십시오.
6.5 푸시 알림 딥링크 설정
addPushNotificationDeepLinkPath
메소드로 앱 소유주는 푸시 알림 페이로드에서 딥링크를 추출하는 방식을 유연한 인터페이스를 통해 설정할 수 있습니다.
기본적으로 SDK는 푸시 알림의 JSON
페이로드의 af
에서 딥링크 값을 찾습니다. 많은 푸시 제공사는 자사 소유의 JSON
스키마를 이용하며 앱스플라이어 SDK는 이를 처리하기 위해서는 추가적인 설정이 필요합니다.
addPushNotificationDeepLinkPath
를 이용하여 앱스플라이어 SDK가 딥링크 값을 위하여 사용하는 푸시알림 JSON 페이로드의 키를 설정할 수 있습니다.
앱을 SDK가 예상하는 기본 푸시 알림 JSON 스키마를 사용하지 않는 푸시 제공자와 연결하는 경우, 이 메서드를 사용하십시오.
addPushNotificationDeepLinkPath
를 호출하면, SDK는 다음을 확인합니다.
- 필수 키가 페이로드에 있습니다.
- 이 키는 유효한 원링크 URL을 포함합니다.
기본 설정
addPushNotificationDeepLinkPath
에 대한 다음 호출을 고려하십시오.
[AppsFlyerLib shared] addPushNotificationDeepLinkPath:@[@"deeply", @"nested", @"deep_link"]]
AppsFlyerLib.shared().addPushNotificationDeepLinkPath(["deeply", "nested", "deep_link"])
그리고 이런 시나리오를 고려하십시오.
시나리오 1
사용자의 앱이 푸시 알림을 통해 호출됩니다. 호출은 다음과 같이 구조화된 페이로드를 포함합니다.
{
...
"deeply": {
"nested": {
"deep_link": "https://yourdeeplink2.onelink.me"
}
}
...
}
이 시나리오에서 앱스플라이어 SDK는 deep_link
값을 추출하고 딥링킹 플로우를 진행합니다.
시나리오 2
사용자의 앱이 푸시 알림을 통해 호출됩니다. 호출은 다음과 같이 구조화된 페이로드를 포함합니다.
{
...
"deeply": {
"nested": {
"banana": "https://yourdeeplink2.onelink.me"
}
},
...
}
이 시나리오에서, 호출이 시행될 때, SDK는 deep_link
키를 페이로드에서 찾을 수 없습니다. 그러므로 아무 일도 발생하지 않습니다.
시나리오 3
사용자의 앱이 푸시 알림을 통해 호출됩니다. 호출은 다음과 같이 구조화된 페이로드를 포함합니다.
{
...
"deeply": {
"nested": {
"deep_link": "Corrupted url or regular string"
}
},
...
}
이 시나리오에서, 앱스플라이어 SDK가 deep_link
키를 찾았을지라도, 딥링킹 값은 유효하지 않습니다. 그러므로 위 호출이 실행되었을 때, 아무 일도 발생하지 않습니다.
고급 설정
여러 개의 가능한 페이로드 구조를 구성하려면 addPushNotificationDeepLinkPath
를 여러 번 호출하십시오.
- 유효한 딥링크 값을 생성한 첫 번째 호출이 사용됩니다.
- 다른 호출은 무시됩니다.
페이로드 구조가 일치하지 않거나 유효한 OneLink URL이 페이로드에서 발견되지 않으면 아무 일도 발생하지 않습니다.
예를 들어, 다음 페이로드와
{
...
"deeply": {
"nested": {
“deep_link”: “https://yourdeeplink2.onelink.me”
}
},
“this”: {
“is”: {
"banana": "phone"
}
}
...
}
addPushNotificationDeepLinkPath
에 대한 다음 호출을 고려하십시오.
// this.is.deep_link key isn’t found - nothing happens
[AppsFlyerLib shared] addPushNotificationDeepLinkPath:@[@"this", @"is", @"deep_link"]]
// this.is.banana key found, but contains invalid OneLink URL - nothing happens
[AppsFlyerLib shared] addPushNotificationDeepLinkPath:@[@"this", @"is", @"banana"]]
// deeply.nested.deep_link key found and contains valid OneLink URL - proceed deep linking flow
[AppsFlyerLib shared] addPushNotificationDeepLinkPath:@[@"deeply", @"nested", @"deep_link"]]
// this.is.deep_link key isn’t found - nothing happens
AppsFlyerLib.shared().addPushNotificationDeepLinkPath(["this", "is", "deep_link"]);
// this.is.banana key found, but contains invalid OneLink URL - nothing happens
AppsFlyerLib.shared().addPushNotificationDeepLinkPath(["this", "is", "banana"])
// deeply.nested.deep_link key found and contains valid OneLink URL - proceed deep linking flow
AppsFlyerLib.shared().addPushNotificationDeepLinkPath(["deeply", "nested", "deep_link"])
7. 전환 데이터 얻기
전환 데이터 얻기
신규 앱 설치가 일어날 때마다, SDK 단계에서 직접적으로 사용자 어트리뷰션 데이터에 실시간 액세스 할 수 있습니다.
이렇게 함으로써, 사용자에게 맞춤형 콘텐츠를 제공하거나 앱 내부의 특정 액티비티를 전송하여(디퍼드 딥링킹 항목 참고) 앱에 대한 사용자 참여를 높일 수 있습니다.
전환(conversion) 데이터를 iOS SDK에서 가져오기 위해서 다음 메서드를 구현하십시오.
- (void) onAppOpenAttribution:(NSDictionary*) attributionData {
NSLog(@"onAppOpenAttribution");
for(id key in attributionData){
NSLog(@"onAppOpenAttribution: key=%@ value=%@", key, [attributionData objectForKey:key]);
}
}
- (void)onAppOpenAttributionFailure:(NSError *)error {
NSLog(@"%@", [error description]);
}
-(void)onConversionDataSuccess:(NSDictionary*) installData {
BOOL first_launch_flag = [[installData objectForKey:@"is_first_launch"] boolValue];
NSString *status = [installData objectForKey:@"af_status"];
if(first_launch_flag) {
if ([status isEqualToString:@"Non-organic"]){
NSString *sourceID = [installData objectForKey:@"media_source"];
NSString *campaign = [installData objectForKey:@"campaign"];
NSLog(@"This is a non-organic install. Media source: %@ Campaign: %@", sourceID, campaign);
} else {
NSLog(@"This is an organic install");
}
} else {
NSLog(@"Not first launch");
}
}
-(void)onConversionDataFail:(NSError *) error {
NSLog(@"%@", [error description]);
}
class AppDelegate: UIResponder, UIApplicationDelegate, AppsFlyerLibDelegate{
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
AppsFlyerLib.shared().appsFlyerDevKey = "MY_DEV_KEY"
AppsFlyerLib.shared().appleAppID = "123456789"
AppsFlyerLib.shared().delegate = self
//AppsFlyerLib.shared().isDebug = true
//AppsFlyerLib.shared().appInviteOneLinkID = "ONELINK_ID";
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
AppsFlyerLib.shared().start()
}
func onConversionDataSuccess(_ installData: [AnyHashable: Any]) {
guard let first_launch_flag = installData["is_first_launch"] as? Int else {
return
}
guard let status = installData["af_status"] as? String else {
return
}
if(first_launch_flag == 1) {
if(status == "Non-organic") {
if let media_source = installData["media_source"] , let campaign = installData["campaign"]{
print("This is a Non-Organic install. Media source: \(media_source) Campaign: \(campaign)")
}
} else {
print("This is an organic install.")
}
} else {
print("Not First Launch")
}
}
func onConversionDataFail(_ error: Error!) {
if let err = error{
print(err)
}
}
func onAppOpenAttribution(_ attributionData: [AnyHashable : Any]!) {
if let data = attributionData{
print("\(data)")
}
}
func onAppOpenAttributionFailure(_ error: Error!) {
if let err = error{
print(err)
}
}
가장 중요한 두가지 메서드는 아래와 같습니다:
-
onConversionDataSuccess
- 신규 설치에 대한 전환 데이터를 제공합니다.
참고: SDK 버전 5 부터,
onConversionDataSuccess
는 전환 데이터를 가져 오는 메소드의 이름입니다. SDK 버전 5.0.0 보다 낮은 버전을 사용하는 경우 메소드 이름은onConversionDataReceived
입니다. SDK 5.0.0 이상으로 업그레이드 하는 것을 추천드립니다. 더 자세한 내용은 여기를 클릭하여 보실 수 있습니다. -
onAppOpenAttribution
- 이미 설치되어 있는 앱이 수동이나 딥링킹을 통해 실행되었을 때, 리타겟팅 전환 데이터를 제공합니다.
전환 데이터에 대해 자세히 알아보시려면 전환 데이터 시나리오에 대한 가이드를 참고하십시오.
8. 어트리뷰션
앱 삭제 측정
앱 삭제 측정을 셋팅하는 방법에 대해서는 여기를 읽어보십시오.
SDK 구동 핸들러 설정하기
SDK가 정상적으로 구동되어 앱스플라이어 서버가 어트리뷰션 확인 요청을 수신했다는 확인 메시지를 받기 위해서,startWithCompletionHandler
핸들러를 구현하십시오. 이 논리를 적용하여 SDK 구동 성공 또는 실패를 처리할 수 있습니다.
구현 예시
[[AppsFlyerLib shared] startWithCompletionHandler:^(NSDictionary<NSString *,id> *dictionary, NSError *error) {
if (error) {
NSLog(@"%@", error);
return;
}
if (dictionary) {
NSLog(@"%@", dictionary);
return;
}
}];
AppsFlyerLib.shared()?.start(completionHandler: { (dictionnary, error) in
if (error != nil){
print(error ?? "")
return
} else {
print(dictionnary ?? "")
return
}
})
Request Listner에서 에러가 발생하면, 다음과 같은 에러 코드와 설명이 제공됩니다.
오류 코드 | 설명 |
---|---|
10 |
"Event timeout. Check 'minTimeBetweenSessions' param |
11 |
"Skipping event because 'isStopTracking' enabled" |
40 |
Network error: Error description comes from Android |
41 |
"No dev key" |
50 |
"Status code failure" + 서버에서 받은 실제 응답 코드 |
추가 커스텀 데이터 설정
setAdditionalData
API는 SDK 수준에서 Segment, Adobe및 Urban Airship과 같은 몇몇 외부 파트너 플랫폼과 연동할 때 필요합니다.
이 API는 해당 플랫폼의 연동 안내 문서에서 setAdditionalData API가 필요하다고 특별히 명시된 경우에만
사용하십시오.
setAdditionalData
코드 예시:
NSDictionary* customDataMap = [[NSDictionary alloc] initWithObjectsAndKeys:@"value_of_param_1", @"custom_param_1", nil];
[[AppsFlyerLib shared] setAdditionalData:customDataMap];
let customDataMap: [AnyHashable: Any] = [
"custom_param_1" : "value_of_param_1"
]
AppsFlyerLib.shared().customData = customDataMap
광고주 소유 웹사이트(도메인)로부터 발생한 앱 세션을 어트리뷰션 합니다.
앱 소유주가 원링크 없이 유니버설 링크를 사용하고 앱과 연과된 도메인이 있으면, appendParametersToDeepLinkingURL
메서드를 사용하여 앱 세션을 어트리뷰션 할 수 있습니다.
예를 들어 엔드유저가 구글 검색을 통해 광고주의 도메인, www.example.com을 클릭합니다.
- 이 때, 엔드유저의 기기에 앱이 설치되지 않았으면 웹사이트(www.example.com)으로 리디렉션 됩니다.
- 앱이 설치되어 있으면, www.example.com와 연관된 앱으로 딥링킹됩니다. 이 앱 세션(앱 오픈)은
appendParametersToDeepLinkingURL
에 정의된 미디어 소스(pid
파라미터)에 어트리뷰션 됩니다.
상세 내용은 iOS SDK 참고를 확인하세요.
일러두기: 스마트 배너는 앱 소유주가 웹사이트 방문객을 앱 사용자로 전환시키도록 지원합니다.
9. 세션
세션 간 시간 정의
기본 설정으로는, 앱을 실행할 때의 간격이 적어도 5초 이상이 되어야 두 개의 별도 세션으로 집계합니다(세션 집계에 대해 알아보기).
다음 API를 사용하여 세션 사이의 최소 필요시간을 설정합니다:
[AppsFlyerLib shared].minTimeBetweenSessions = <your_custom_time_in_seconds>;
AppsFlyerLib.shared().minTimeBetweenSessions = <your_custom_time_in_seconds>
너무 높은 값을 세션 간격 시간으로 설정하면 딥링킹과 같이 세션 데이터를 사용하는 API에 문제가 생길 수 있습니다.
유틸리티 앱을 위한 백그라운드 세션
iOS에서는 사용할 수 없습니다.
10. 온드 미디어(Owned Media)
래핑(wrapping)된 딥링크 URL 분석하기
이메일 서비스 제공 업체들 과 같은 몇몇 외부 업체 서비스 사는 이메일 내의 링크를 자체 클릭 기록 도메인으로 래핑(wrapping)합니다. 어떤 업체는 자신의 클릭 기록 도메인을 직접 설정하도록 허용하기도 합니다. 만약 원링크가 이런 도메인들에서 래핑된다면, 원링크의 기능이 제한될 수 있습니다.
이 문제를 해결하기 위해 setResolveDeepLinkURLs
API를 사용할 수 있습니다. 이 API를 사용하여 앱을 시작하는 클릭 도메인에서 원링크를 가져 오십시오. 반드시 SDK 초기화 전에 이 API를 호출해야합니다.
예를 들어, 당신의 원링크(https://mysubdomain.onelink.me/abCD)로 리다이렉션되는 세 개의 클릭 도메인이 있습니다. 이 API를 사용하여 클릭 도메인이 리다이렉션하는 원링크를 가져오십시오. 이 API 메서드는 SDK가 분석하는 도메인 목록을 수신합니다.
[AppsFlyerLib shared].resolveDeepLinkURLs = @[@"example.com",@"click.example.com"];
AppsFlyerLib.shared().resolveDeepLinkURLs = ["example.com", "click.example.com"]
위의 코드를 사용하면 원링크 기능을 유지하면서 자체 클릭 도메인을 사용할 수 있습니다. 여기에서 클릭 도메인은 앱 실행을 담당합니다. 대신 API가 이 클릭 도메인에서 원링크를 가져오고, 이 원링크에서 데이터를 확보하여 맞춤식 콘텐츠로 딥링크 하는데 사용할 수 있습니다.
푸시 알림 기록하기
앱스플라이어를 사용하여, 리타겟팅 캠페인 중 일부인 푸시 알림 성과를 측정할 수 있습니다.
이 기능을 사용하려면 AppDelegate.m 내에서 handlePushNotificationData
메서드를 호출하십시오.
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[AppsFlyerLib shared] handlePushNotification:userInfo];
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
AppsFlyerLib.shared().handlePushNotification(userInfo)
}
푸쉬 알림 측정에 대한 더 자세한 내용은 여기에서 확인하실 수 있습니다.
사용자 초대 어트리뷰션
기존 사용자가 친구나 아는 사람을 앱의 신규 사용자로 초대하도록 하는 것은 앱 사용자를 늘리는데 크게 기여할 수 있습니다. 앱스플라이어를 사용하면 앱 내 사용자 초대에서 발생한 설치를 어트리뷰트하고 기록할 수 있습니다.
더 자세한 내용은 사용자 초대 어트리뷰션 안내를 참조하십시오.
크로스 프로모션 어트리뷰션
주의! IDFV를 사용한 크로스 프로모션 성과측정은 SDK 6.0.2부터 지원됩니다. IDFV는 자동 수집되며 개발자가 별도로 할 작업은 없습니다.
크로스 프로모션은 추가적인 앱 설치를 유도하는 주요 성장 요소가 될 수 있습니다. 앱스플라이어는 사용자가 기존에 가지고 있던 우리 개발사의 다른 앱으로부터 발생한 크로스 프로모션에 대해 설치를 어트리뷰트하고 기록할 수 있습니다.자세한 내용은 여기의 크로스 프로모션 어트리뷰션 안내를 확인하십시오.
11. 사용자 식별자
앱스플라이어 ID 확보하기
앱스플라이어 고유 ID는 앱이 새로 설치될 때마다 생성됩니다. 이 ID는 다양한 목적으로 사용할 수 있습니다.
- 서버 간 인앱 이벤트(server-to-server in-app events) 보내기.
- AppsFlyer ID와 백엔드 시스템의 사용자 기록을 맞추기.
- Pull API 또는 Push API로 수신한 데이터 병합하기
앱스플라이어의 고유 ID를 얻기 위해서 다음 API를 사용하십시오.
NSString *appsflyerId = [AppsFlyerLib shared].getAppsFlyerUID;
let appsflyerId = AppsFlyerLib.shared().getAppsFlyerUID()
고객 사용자 ID 설정하기
다음을 통해 고객 사용자 ID를 설정하세요:
[AppsFlyerLib shared].customerUserID = @"my user id";
AppsFlyerLib.shared().customerUserID = "my user id"
iOS에서는 앱을 시작할 때마다 매번 Customer User ID를 설정해야합니다. Customer User ID를 설정한 이후에만 인앱 이벤트 기록이 연결되어 보고되므로, 앱 사용 과정 초기에 CUID를 설정할 것을 추천드립니다
- 만약,
setCustomerUserId
가startTracking
을 호출하기 이전에 호출된다면, 인스톨 및 이벤트 로데이터 리포트에 CUID(Customer User ID) 정보가 포함되어 표시됩니다. - 만약에 이 호출이 이후에 이루어진다면, 고객 ID 정보는 고객 사용자 ID 셋팅한 이후의 이벤트에서만 연결되어 보여집니다.
고객 사용자 ID 가져오기:
첫 번째 앱 실행 후 고객 사용자 ID 값이 다시 설정되지 않도록 하기 위하고, 고객 사용자 ID를 얻기 위한 서버 호출을 줄이기 위해서, 다음 구문을 사용하여 값이 비어 있는지 여부를 확인할 수 있습니다:
NSString *customerUserID = [AppsFlyerLib shared].customerUserID;
let customerUserID = AppsFlyerLib.shared().customerUserID
고객 사용자 ID에 대해 더 자세히 알아보려면 여기를 클릭하세요.
고객 사용자 ID (customerUserID)를 위해 SDK 초기화 지연시키기
CUID가 설정될 때까지 SDK 동작을 지연시킬 수 있습니다. 앱 설치 및 이벤트 데이터에 고객 사용자 ID가 포함되는 것이 중요한 경우에 유용하게 사용될 수 있습니다.
다음 코드를 구현하십시오:
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSString *customUserId = [[NSUserDefaults standardUserDefaults] stringForKey:@"customerUserId"]; // Your custom logic of retrieving CUID
if (customUserId != nil && ![customUserId isEqual: @""]) {
[AppsFlyerLib shared].customerUserID = customUserId; // Set CUID in AppsFlyer SDK for this session
[[AppsFlyerLib shared] start]; // Start
}
}
func applicationDidBecomeActive(_ application: UIApplication) {
let customUserId = UserDefaults.standard.string(forKey: "customUserId") // your logic to retrieve CUID
if(customUserId != nil && customUserId != ""){
AppsFlyerLib.shared().customerUserID = customUserId // Set CUID in AppsFlyer SDK for this session
AppsFlyerLib.shared().start() // Start
}
}
고객 사용자 ID가 가능할 때까지 SDK 초기화를 지연시키는 것에 대해 더 자세한 내용을 보시려면 여기로 이동하십시오.
경고
이 API는 비즈니스 로직에 적합한 경우에만 사용하십시오. 이 API를 사용하면 수치 차이가 발생할 가능성이 커지고 앱이 fraud에 더 노출될 수도 있습니다.
12. 사용자 프라이버시
옵트아웃
간혹, 법률 및 개인 정보 보호 준수를 위해서 모든 SDK 데이터 기록(logging)을 중단해야 하는 경우가 있을 수 있습니다. 데이터 기록 중단 작업은 isStopped 속성을 설정하여 수행할 수 있습니다. 이 속성이 true로 세팅되면, 앱스플라이어 SDK 작동이 중단되고 더 이상 애스플라이어 서버와 통신하지 않습니다.
서로 다른 몇 가지 사용자 옵트아웃 시나리오가 있습니다. 앱과 관련된 각 시나리오에 맞는 정확한 지침을 따르는 것을 권장합니다.
경고
특정 사용자를 특정 기록 혹은 모든 기록에서 제외하려는 경우에만 isStopped API를 사용하세요. 이 API를 사용하면 어트리뷰션, 데이터 수집 및 딥링킹 동작에 심각한 영향을 줄 수 있습니다.
[AppsFlyerLib shared].isStopped = true;
AppsFlyerLib.shared().isStopped = true
어떤 이벤트에서든 이 API에 false를 넘기면서 호출하면 SDK를 다시 활성화할 수 있습니다.
중요!
isStopped
가 true
로 설정되어 있는 경우, start
를 호출하지 마십시오.
데이터 기록을 다시 시작하시려면, isStopped
를 false
로 설정하십시오.
사용자 데이터 익명 처리
SDK를 초기화하는 동안 다음 API를 사용하면, 사용자의 설치, 이벤트, 세션을 명시적으로 익명처리 할 수 있습니다.
[AppsFlyer shared].disableAdvertisingIdentifier = YES;
AppsFlyerLib.shared().disableAdvertisingIdentifier = true
anonymizeUser
를 false로 설정하여 데이터 로깅을 재시작할 수 있습니다.
경고
사용자를 익명 처리하면 어트리뷰션 정보에 영향을 줄 수 있습니다.사용자 정보 수집이 법적으로 금지된 경우에만 이 옵션을 사용하십시오.
앱 추적 투명성(ATT, App Tracking Transparency) 지원 설정
iOS 14.5부터 Apple은 IDFA 수집 시, 사용자의 동의가 필요하다는 내용을 발표했습니다. 이는 실제로 IDFA 접근을 앱 추적 투명성(App Tracking Transparency, ATT) 프레임워크가 통제한다는 의미입니다. iOS 14.5 이상의 기기에서, 앱스플라이어 iOS SDK는 ATT 프레임워크를 사용하여 기기의 IDFA 접근권한을 획득합니다.
IDFA를 이용하여 어트리뷰션을 할 경우, IDFA는 앱 최초 실행 시점에 전송되어야 합니다. 이를 위해, SDK는 waitForATTUserAuthorization
유틸리티를 제공합니다.
구현 개요
waitForATTUserAuthorization
로 앱스플라이어 SDK가 앱스플라이어 서버로 데이터를 전송하기 전 ATT 상태를 위해 대기하는 시간을 설정할 수 있습니다. ATT 지원을 위해 다음과 같이 구성됩니다.
- 앱 구동 준비(initialization) 단계에서
waitForATTUserAuthorization
설정 - ATT 동의 대화상자가 노출될 때마다
ATTrackingManager.requestTrackingAuthorization
호출
waitForATTUserAuthorization 설명
사용자가 앱을 실행하는 시점에는 ATT 상태가 결정되지 않습니다. waitForATTUserAuthorization
타임아웃 설정 시간 동안,
- SDK는 앱 실행 이벤트와 연속적인 인앱 이벤트 인메모리(in-memory)를 오프라인 이벤트가 기록되는 방식과 유사하게 수차적으로 기록합니다.
- 사용자가 IDFA 수집에 동의하면, SDK는 IDFA를 정렬된 인-메모리 이벤트에 추가하고 타임아웃을 기다리지 않고 바로 구동하기 시작합니다.
- 타임아웃이 종료된 시점에 ATT 동의를 얻지 않거나 동의 여부가 결정되지 않으면, SDK가 구동하기 시작하고 IDFA 없이정렬된 인-메모리 이벤트를 전송합니다.
고려 사항
-
타임아웃이 된 후에도 여전히 ATT 동의 여부가 결정되지 않으면,
- iOS 14.5 이전: 기본적으로 IDFA가 수집됩니다.
- iOS 14.5 이후 기본적으로 IDFA가 수집되지 않습니다.
- 사용자가 타임아웃 중간에 앱을 백그라운드로 이동시키면,
- 앱이 휴대폰 화면(foreground)에 표시될 때까지 타이머 정지
- 이벤트는 인-메모리에 캐쉬됩니다
- 타임아웃 중간에 사용자가 앱을 종료하거나 닫으면,
- 다음 앱 실행 시점에 타이머 재시작
- 캐시 이벤트 손실
잠재 위험
-
waitForATTUserAuthorization
은 iOS 14.5 버전 이상 환경에서 IDFA 수집을 위해 필요합니다.waitForATTUserAuthorization
을 호출하지 않으면 IDFA 없이 실행 요청이 전송됩니다. requestTrackingAuthorization
콜백에서start
를 호출하지 마십시오. 이 경우 어트리뷰션이 불완전해지거나 오류가 발생할 수 있습니다.
iOS SDK에서 ATT 지원 설정하기
iOS SDK에서 ATT 지원 설정하기
-
초기화 코드에서
waitForATTUserAuthorization
설정- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[AppsFlyerLib shared] setAppsFlyerDevKey:@"<AF_DEV_KEY>"]; [[AppsFlyerLib shared] setAppleAppID:@"<APPLE_APP_ID>"]; [[AppsFlyerLib shared] setDelegate:self]; [[AppsFlyerLib shared] waitForATTUserAuthorizationWithTimeoutInterval:60]; return YES; }
class AppDelegate: UIResponder, UIApplicationDelegate, AppsFlyerLibDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { AppsFlyerLib.shared().appsFlyerDevKey = "<AF_DEV_KEY>" AppsFlyerLib.shared().appleAppID = "<APPLE_APP_ID>" AppsFlyerLib.shared().delegate = self AppsFlyerLib.shared().waitForATTUserAuthorization(timeoutInterval: 60) ... } ... }
-
애플리케이션 코드에서
ATTrackingManager.requestTrackingAuthorization
을 호출하여 ATT 동의 대화상자를 표시합니다. 예를 들어, 특정 화면이 로딩되었을 때 사용자에게 ATT 동의 대화상자를 표시하고 싶다면,- (void)viewDidLoad { [super viewDidLoad]; if #available(iOS 14, *) { [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) { NSLog(@"Status: %lu", (unsigned long)status); }]; } }
override func viewDidLoad() { super.viewDidLoad() if #available(iOS 14, *) { ATTrackingManager.requestTrackingAuthorization { (status) in } } }
이 콜이 ATT 동의 대화상자를 트리거합니다.
ATT 동의 대화상자 사용자 정의하기
키즈 앱
앱을 App Store Kids Category에서 사용할 수 있게 하려면, Apple 정책에 따라 앱 광고주가 "개인 식별이 가능한 정보나 기기 정보를 제 3자에 제공할 수 없습니다" 이 규정을 준수하려면, 반드시 AdSupport와 iAD 프레임워크를 비활성화해야 합니다.
ad frameworks 비활성화하기:
[AppsFlyerLib shared].disableCollectASA = YES [AppsFlyerLib shared].disableAdvertisingIdentifier = YES;
AppsFlyerLib.shared().disableCollectASA = true AppsFlyerLib.shared().disableAdvertisingIdentifier = true
파트너사와 데이터 공유 차단하기
광고주는 특정 사용자들의 사용자 레벨 데이터를 애드 네트워크/파트너사와 공유하지 않을 경우가 있습니다. 사용자 정보를 공유하지 않는 이유는 다음과 같습니다.
- CCPA나 GDPR과 같은 개인정보 보호 정책 준수
- 사용자 옵트아웃(개인정보 제 3자와의 공유 거부)
- 파트너사(애드 네트워크, 제 3자 기업)가 경쟁사이기도 한 경우
앱스플라이어는 일부 또는 모든 파트너와 데이터 공유를 중단하는 API 메서드를 다음과 같이 두 가지 제공합니다.
- setSharingFilter: 광고주가 하나 이상의 일부 애드 네트워크/연동 파트너와 데이터를 공유할 때 사용합니다.
- setSharingFilterForAllPartners: 광고주가 모든 애드 네트워크/연동 파트너와 데이터 공유를 중단할 때 사용합니다.
이 필터링 메서드는 SDK V5.4.1.에서부터 지원됩니다.
이 필터링 메서드는 SDK가 구동을 시작할 때마다 호출되어야 하고 전체 세션에 영향을 미칩니다. 파트너와의 데이터 공유 필터를 설정해야할지 여부를 판단하는데 시간이 걸리면, SDK 구동 시작을 지연시키십시오.
이 메서드가 첫 번째 start 호출 이전에 활성화되면
- SRN에서 유입된 사용자들이 오가닉으로 기록되고 사용자 정보는 연동 파트너와 공유되지 않습니다.
- 애드 네트워크(SRN 제외) 클릭으로 유입된 사용자 는 논오가닉으로 기록되지만 사용자 정보는 포스트백, API, 로데이터 리포트 혹은 다른 어떤 방법으로도 애드 네트워크와 공유되지 않습니다.
그런데 앱 삭제 데이터는 이 메서드를 이용하여 필터링할 수 없습니다. 대신, 앱스플라이어 대시보드 설정 화면에서 앱 삭제 이벤트를 파트너사에 전송하는 것을 중단할 수 있습니다.
continueUserActivity
설명 |
iOS 9 또는 그 이상에서 유니버설 링크로 앱이 오픈되었을 때, onAppOpenAttribution 을 호출합니다. |
Method signature |
|
사용법 예시 |
|
currencyCode
설명 |
수익 값이 있는 이벤트의 통화 코드를 설정하십시오. ISO 통화 코드로 설정 가능합니다. |
Method signature |
|
사용법 예시 |
|
anonymizeUser
설명 |
사용자의 앱 설치, 이벤트 및 세션을 익명 처리 합니다. 자세한 정보는 사용자 데이터 익명처리를 참조하십시오. |
Method signature |
|
사용법 예시 |
|
disableCollectASA
설명 |
SDK 버전 4.8.11 부터, 앱스플라이어 SDK는 Apple iAd.framework를 동적으로 로드합니다. 이 프레임워크는 앱 내, Apple Search Ads의 실적을 기록하고 측정하는데 필요합니다. 만약 앱스플라이어 SDK가 이 프레임워크를 동적으로 로드하지 못하도록 하려면 이 속성을 true로 설정하십시오. |
Method signature |
|
사용법 예시 |
|
handleOpenUrl
설명 |
이 메서드는 URI 스킴(URI scheme)을 통한 딥링킹으로 앱이 열렸을 때 URI 스킴을 앱스플라이어 SDK에 보고합니다. 이것은 AppDelegate 안에 입력되어야 합니다. |
Method signature |
|
사용법 예시 |
|
handlePushNotification
설명 |
푸시 알림 캠페인에서 데이터를 측정하고 가져올 수 있게 합니다. 자세한 내용은 푸시 알림 측정하기 가이드를 참고하십시오. |
Method signature |
|
사용법 예시 |
|
isDebug
설명 |
Xcode 콘솔에 앱스플라이어 SDK 로그를 표시합니다.디버깅은 개발 단계에서만 이뤄지는 것으로 제한되어야 합니다. 디버깅이 활성화 된 상태에서 앱을 App Store에 배포하지 마십시오. 중대한 보안이나 개인 정보 위험이 발생할 수 있습니다. |
Method signature |
|
사용법 예시 |
|
isStopped
설명 |
SDK의 모든 기능을 종료합니다. 자세한 내용은 사용자 개인 정보 - 옵트아웃 부분을 참고하십시오. |
Method signature |
|
사용법 예시 |
|
onAppOpenAttribution
설명 |
앱이 딥링크를 통해 열렸을 때 딥링크 정보를 가져옵니다. |
Method signature |
|
사용법 예시 |
|
onAppOpenAttributionFailure
설명 |
딥링크 정보를 가져올 때 발생하는 오류를 처리합니다. |
Method signature |
|
사용법 예시 |
|
onConversionDataSuccess
설명 |
앱 설치 후 전환 데이터를 가져옵니다. 디퍼드 딥링킹에 활용하기에 유용합니다. 참고: SDK 버전 |
Method signature |
|
사용법 예시 |
|
onConversionDataFail
설명 |
앱 설치 후 전환 데이터를 가져 오지 못한 경우 오류를 처리합니다. |
Method signature |
|
사용법 예시 |
|
didResolveDeepLink
설명 |
앱이 오픈되자마자 앱이 설치되어 있는 모바일 유저와 설치되어 있지 않은 모바일 유저를 특정 인앱 액티비티로 보냅니다. 더 알아보기 |
Method signature |
|
performOnAppAttribution
설명 |
이 메서드를 이용하여 새 리어트리뷰션을 기록하지 않고 개발자가 직접 만든 링크(URI나 URL)로onAppOpenAttribution를 다시 트리거할 수 있습니다. 이 메서드는 세션이 사용자 전경(foreground)에 있거나 열려있을 때사용자가 지정된 링크를 클릭시 특정 화면으로 넘어가도록 하거나 앱스플라이어 숏링크 한계를 극복하기 위해 사용할 수 있습니다. 일반적인 onAppOpenAttribution 콜백은 앱이 딥링크로 열리는 경우에만 호출되기 때문에 이 메서드가 필요할 수 있습니다. |
Method signature |
|
사용법 예시 |
|
registerUninstall
설명 |
앱 삭제를 측정합니다. iOS에서 앱 삭제 측정하기 부분을 참고하십시오. |
Method signature |
|
사용법 예시 |
|
resolveDeepLinkURLs
설명 |
클릭 도메인에서 원링크를 분석합니다. 자세한 내용은 래핑된 딥링크 URL 분석하기 항목을 참고하십시오. |
Method signature |
|
사용법 예시 |
|
setAdditionalData
설명 |
외부 파트너 플랫폼으로 보낼 부가 정보를 추가합니다. |
Method signature |
|
사용법 예시 |
부가 정보 추가 설정 항목을 참고하십시오. |
setAppInviteOneLink
설명 |
사용자 초대 캠페인를 위한 커스텀 어트리뷰션 링크용 원링크 템플릿 ID를 설정합니다. |
Method signature |
|
사용법 예시 |
친구 초대 어트리뷰션을 위한 원링크 설정하기 항목을 참조하십시오. |
setAppleAppID
설명 |
SDK가 데이터를 올바른 앱 대시보드로 보낼 수 있도록 앱 ID(itunes ID)를 설정합니다. SDK 초기화 때 사용하십시오. |
Method signature |
|
사용법 예시 |
|
appsFlyerDevKey
설명 |
SDK가 데이터를 올바른 앱 대시보드로 보낼 수 있도록 앱스플라이어 dev key를 설정합니다.SDK 초기화 때 사용하십시오.dev key를 확보하는 방법에 대해서는 여기를 참고하십시오. |
Method signature |
|
사용법 예시 |
|
setCustomerUserID
설명 |
고객 사용자 ID를 설정합니다. 자세한 정보는 고객 사용자 ID 설정하기 안내를 참고하십시오. |
Method signature |
|
사용법 예시 |
자세한 정보는 고객 사용자 ID 설정하기 안내를 참고하십시오. |
setPartnerData
설명 |
파트너와 광고주가 SDK 이벤트에 더 많은 데이터를 추가할 수 있습니다. |
Method signature |
|
사용법 예시 |
|
setSharingFilter
설명 |
광고주가 이 메서드를 이용하여 일부(하나 혹은 복수의) 애드 네트워크/연동 파트너를 데이터 공유 대상에서 제외되도록 설정할 수 있습니다. |
Method signature |
|
사용법 예시 |
|
setSharingFilterForAllPartners
설명 |
광고주가 모든 애드 네트워크/연동 파트너에게 데이터를 보내지 않도록 할 때 사용합니다. |
Method signature |
|
사용법 예시 |
|
setShouldCollectDeviceName
설명 |
SDK가 장치 이름을 수집하는지 여부를 나타냅니다. 기본 값은 false 입니다. |
Method signature |
|
사용법 예시 |
|
setUseUninstallSandbox
설명 |
아직 개발 중인 (Apple App Store에 게시되지 않은) 앱의 삭제를 테스트하려면 이 특성을 true로 설정하십시오. |
Method signature |
|
사용법 예시 |
|
startWithCompletionHandler
설명 |
start 가 성공했는지 확인합니다. SDK 실행이 성공 또는 실패했는지 처리할 자체 로직을 구현할 수 있습니다. |
Method signature |
|
사용법 예시 |
SDK 구동 시작 성공 여부 검증하기를 참고하십시오. |
logEvent
설명 |
앱스플라이어에 인앱 이벤트를 보냅니다. 자세한 내용은 핵심 API - 인앱 이벤트 기록하기 부분을 참조하십시오. |
Method signature |
|
사용법 예시 |
|
validateAndLogInAppPurchase
설명 |
SDK가 데이터를 올바른 앱 대시보드로 보낼 수 있도록 앱스플라이어 dev key를 설정합니다. dev key를 확보하는 방법에 대해서는 여기를 참고하십시오. SDK 초기화 때 사용하십시오. |
Method signature |
|
사용법 예시 |
인앱 구매 검증 부분을 참고하십시오. |
disableAdvertisingIdentifier
설명 |
SDK 버전 4.8.11 부터, 앱스플라이어 SDK는 Apple adSupport.framework를 동적으로 로드합니다. 이 프레임워크는 어트리뷰션을 할 목적으로 IDFA를 수집할 때 필요합니다. 만약 앱스플라이어 SDK가 이 프레임워크를 동적으로 로드하지 못하도록 하려면 이 속성을 true로 설정하십시오. |
Method signature |
|
사용법 예시 |
[AppsFlyerLib shared].disableAdvertisingIdentifier= YES; AppsFlyerLib.shared().disableAdvertisingIdentifier = true |
waitForATTUserAuthorization
설명 |
앱 사용자나 기기 정보(예: IDFA)를 기록하여 앱 관련 데이터로 활용하기 전, 팝업으로 사용자 승인을 요청할 때 사용됩니다. 사용자가 허가하면, IDFA가 SDK에 전달됩니다.타임아웃 길이는 사용자가 IDFA 수집 허용을 선택할 때까지의 제한된 시간입니다. 타이머가 만료된 후에, IDFA는 수집되지 않습니다. |
Method signature |
|
사용법 예시 |
|
start
설명 |
이 API가 호출되면 SDK가 시작되고, 세션이 즉시 전송되며 모든 배경(background)-전경(foreground) 전환이 발생하면 세션이 기록됩니다. |
Method signature |
|
사용법 예시 |
|
logLocation
설명 |
수작업으로 사용자 위치를 기록합니다. |
Method signature |
|
사용법 예시 |
|
appendParametersToDeepLinkingURL
설명 |
원링크 없이 유니버설 링크를 이용하여 딥링킹을 하는 앱 소유주가 앱과 관련된 도메인을 통해 앱 세션을 어트리뷰션할 수 있도록 합니다. start를 호출하기 전 이 메서드를 호출합니다. 이 메서드는 다음이 필요합니다.
|
Method signature |
|
사용법 예시 |
|
addPushNotificationDeepLinkPath
설명 |
앱스플라이어 SDK가 푸시 알림 페이로드에서 딥링크 값을 추출하는 방식을 설정합니다. |
Method signature |
|
사용법 예시 |
이 호출은 다음 페이로드 구조와 일치합니다.
|
disableSKAdNetwork
설명 |
SKAdNetwork 어트리뷰션을 비활성화하도록 합니다. 비활성화하도록 true 로 설정하십시오. |
Method signature |
|
사용법 예시 |
|
댓글
댓글을 남기려면 로그인하세요.