전제 조건
Singular SDK 연동의 단계를 완료합니다: 계획 및 사전 요구 사항의 단계를 완료한 후에 이 연동을 진행하세요.
중요: 이러한 필수 단계는 모든 Singular SDK 연동에 필요합니다.
설치
원하는 설치 방법을 선택합니다. 대부분의 프로젝트에는 CocoaPods를 권장합니다.
빠른 결정:
- 이미 CocoaPods를 사용하고 계신가요? 방법 1 사용
- SPM 전용 프로젝트인가요? 방법 2 사용
- 패키지 관리자가 없으신가요? 방법 3 사용
설치 방법
방법 1: CocoaPods(권장)
요구 사항
- CocoaPods 설치(설치 가이드)
- 프로젝트 디렉토리에 대한 터미널 액세스
설치 단계:
-
포드파일 초기화 (이미 있는 경우 건너뛰기):
cd /path/to/your/project pod init -
Podfile에 Singular SDK를 추가합니다:
platform :ios, '12.0' target 'YourAppName' do use_frameworks! # Singular SDK pod 'Singular-SDK' end -
종속 요소를 설치한다:
pod install -
워크스페이스 열기: 이제부터는
.xcodeproj대신.xcworkspace을 엽니다. - Swift 프로젝트만 해당: 브리징 헤더를 생성합니다(아래 참조).
방법 2: Swift 패키지 관리자
설치 단계:
- Xcode: 파일 → 패키지 추가
-
리포지토리 URL 입력:
https://github.com/singular-labs/Singular-iOS-SDK - 버전을 선택하고 패키지 추가를클릭합니다.
-
필요한 프레임워크를 추가합니다:
빌드 단계 → 라이브러리와 바이너리 연결로 이동하여 추가합니다:-
필요한 라이브러리를 연결합니다:
빌드 단계 → 라이브러리와 바이너리 링크로 이동하여 추가합니다:- Libsqlite3.0.tbd
- SystemConfiguration.framework
- Security.framework
- Libz.tbd
- AdSupport.framework
- WebKit.framework
- StoreKit.프레임워크
- AdServices.framework(선택 사항으로 표시)
-
필요한 라이브러리를 연결합니다:
- Swift 프로젝트만 해당: 브리징 헤더 생성(아래 참조)
방법 3: 수동 프레임워크 설치
사용 시기: 코코아팟 또는 SPM을 사용할 수 없는 경우에만 이 방법을 사용하세요.
프레임워크 다운로드:
- Xcode 12+:.xcframework 다운로드
- Xcode 11 이하:.framework 다운로드
설치 단계:
- 다운로드한 프레임워크의 압축을 풉니다.
- Xcode에서: 프로젝트 → [프로젝트]에 파일 추가를마우스 오른쪽 버튼으로 클릭합니다.
- 그룹 생성을 선택하고 프레임워크 폴더를 추가합니다.
-
필요한 라이브러리를 연결합니다:
빌드 단계 → 라이브러리와 바이너리 링크로이동하여 추가합니다:- Libsqlite3.0.tbd
- SystemConfiguration.framework
- Security.framework
- Libz.tbd
- AdSupport.framework
- WebKit.framework
- StoreKit.프레임워크
- AdServices.framework(선택 사항으로 표시)
-
프레임워크 임베드:
일반 → 프레임워크, 라이브러리 및 임베드된 콘텐츠로 이동합니다.
Singular 프레임워크를 임베드 및 서명으로설정합니다.
스위프트 브리징 헤더
중요: 코코아팟 또는 SPM을 사용하는 Swift 프로젝트에 필요합니다.
-
헤더 파일을 생성합니다:
Xcode → 파일 → 새로 만들기 → 파일 → 헤더 파일
이름 지정:YourProjectName-Bridging-Header.h -
가져오기를 추가합니다:
#import <Singular/Singular.h> -
빌드 설정에서 링크:
빌드 설정 → Objective-C 브리징 헤더
로 설정합니다:YourProjectName/YourProjectName-Bridging-Header.h
SDK 구성 및 초기화
구성 오브젝트를 생성하고 앱의 진입점에서 SDK를 초기화합니다.
구성 개체 생성
기본 구성
SDK 자격 증명과 선택적 기능을 사용하여 SingularConfig 개체를 만듭니다. 이 구성은 모든 앱 아키텍처에 공통적으로 적용됩니다.
자격 증명 가져오기: 개발자 도구 → SDK 연동의 Singular 플랫폼에서 SDK 키와 SDK 시크릿을 찾을 수 있습니다.
// MARK: - Singular Configuration
private func getConfig() -> SingularConfig? {
// Create config with your credentials
guard let config = SingularConfig(
apiKey: "YOUR_SDK_KEY",
andSecret: "YOUR_SDK_SECRET"
) else {
return nil
}
// OPTIONAL: Wait for ATT consent (if showing ATT prompt)
// Remove this line if NOT using App Tracking Transparency
config.waitForTrackingAuthorizationWithTimeoutInterval = 300
// OPTIONAL: Support custom ESP domains for deep links
config.espDomains = ["links.your-domain.com"]
// OPTIONAL: Handle deep links
config.singularLinksHandler = { params in
if let params = params {
self.handleDeeplink(params)
}
}
return config
}
// MARK: - OPTIONAL: Deep link handler implementation
private func handleDeeplink(_ params: SingularLinkParams) {
// Guard clause: Exit if no deep link provided
guard let deeplink = params.getDeepLink() else {
return
}
// Extract deep link parameters
let passthrough = params.getPassthrough()
let isDeferred = params.isDeferred()
let urlParams = params.getUrlParameters()
#if DEBUG
// Debug logging only - stripped from production builds
print("Singular Links Handler")
print("Singular deeplink received:", deeplink)
print("Singular passthrough received:", passthrough ?? "none")
print("Singular isDeferred received:", isDeferred ? "YES" : "NO")
print("Singular URL Params received:", urlParams ?? [:])
#endif
// TODO: Navigate to appropriate screen based on deep link
// Add deep link handling code here. Navigate to appropriate screen.
}
#pragma mark - Singular Configuration
- (SingularConfig *)getConfig {
// Create config with your credentials
SingularConfig *config = [[SingularConfig alloc]
initWithApiKey:@"YOUR_SDK_KEY"
andSecret:@"YOUR_SDK_SECRET"];
// OPTIONAL: Wait for ATT consent (if showing ATT prompt)
// Remove this line if NOT using App Tracking Transparency
config.waitForTrackingAuthorizationWithTimeoutInterval = 300;
// OPTIONAL: Support custom ESP domains for deep links
config.espDomains = @[@"links.your-domain.com"];
// OPTIONAL: Handle deep links
config.singularLinksHandler = ^(SingularLinkParams *params) {
[self handleDeeplink:params];
};
return config;
}
#pragma mark - OPTIONAL: Deep link handler implementation
- (void)handleDeeplink:(SingularLinkParams *)params {
// Guard clause: Exit if params is nil
if (!params) {
return;
}
// Guard clause: Exit if no deep link provided
NSString *deeplink = [params getDeepLink];
if (!deeplink) {
return;
}
// Extract deep link parameters
NSString *passthrough = [params getPassthrough];
BOOL isDeferred = [params isDeferred];
NSDictionary *urlParams = [params getUrlParameters];
#ifdef DEBUG
// Debug logging only - stripped from production builds
NSLog(@"Singular Links Handler");
NSLog(@"Singular deeplink received: %@", deeplink);
NSLog(@"Singular passthrough received: %@", passthrough);
NSLog(@"Singular isDeferred received: %@", isDeferred ? @"YES" : @"NO");
NSLog(@"Singular URL Params received: %@", urlParams);
#endif
// TODO: Navigate to appropriate screen based on deep link
// Add deep link handling code here. Navigate to appropriate screen.
}
SKAdNetwork 자동 활성화: SDK 버전 12.0.6부터 SKAdNetwork가 기본적으로 활성화됩니다. 추가 구성이 필요하지 않습니다.
SDK 초기화
앱 아키텍처 선택
모든 앱 진입 지점에서 SDK를 초기화하세요. 초기화 패턴은 앱의 아키텍처에 따라 다릅니다.
어떤 아키텍처를사용하나요?
-
SceneDelegate: 프로젝트에
SceneDelegate.swift또는SceneDelegate.m -
SwiftUI: 앱이
@main struct YourApp: App - AppDelegate 전용: iOS 13 이전 앱 또는 SceneDelegate가 없는 앱
최신 iOS: SceneDelegate(iOS 13+)
코드를 추가할 위치: SceneDelegate.swift
또는 SceneDelegate.m
초기화할 엔트리 포인트:
-
willConnectTo session- 앱 실행 -
continue userActivity- 유니버설 링크 -
openURLContexts- 딥링크 방식
import Singular
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
// 1️⃣ App launch
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
// ANTI-SWIZZLING: Capture deep link parameters IMMEDIATELY before any
// other code runs. This prevents third-party SDKs from intercepting
// or modifying these values.
let userActivity = connectionOptions.userActivities.first
let urlContext = connectionOptions.urlContexts.first
let openUrl = urlContext?.url
#if DEBUG
// Log captured values to detect swizzling interference
print("[SWIZZLE CHECK] UserActivity captured:", userActivity?.webpageURL?.absoluteString ?? "none")
print("[SWIZZLE CHECK] URL Context captured:", openUrl?.absoluteString ?? "none")
print("IDFV:", UIDevice.current.identifierForVendor?.uuidString ?? "N/A")
#endif
// Create window from windowScene
guard let windowScene = scene as? UIWindowScene else { return }
window = UIWindow(windowScene: windowScene)
window?.rootViewController = UIViewController() // Replace with your root VC
window?.makeKeyAndVisible()
// Singular initialization - uses captured values to avoid swizzling conflicts
guard let config = getConfig() else { return }
// Pass Universal Link if available
if let userActivity = userActivity {
config.userActivity = userActivity
}
// Pass URL scheme if available
// CRITICAL for custom URL scheme attribution
if let openUrl = openUrl {
config.openUrl = openUrl
}
// Initialize Singular SDK
Singular.start(config)
}
// 2️⃣ Universal Links
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard let config = getConfig() else { return }
config.userActivity = userActivity
// Initialize Singular SDK
Singular.start(config)
}
// 3️⃣ Deep link schemes
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let config = getConfig() else { return }
if let url = URLContexts.first?.url {
config.openUrl = url
}
// Initialize Singular SDK
Singular.start(config)
}
}
#import <Singular/Singular.h>
@implementation SceneDelegate
// 1️⃣ App launch
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session
options:(UISceneConnectionOptions *)connectionOptions {
// ANTI-SWIZZLING: Capture deep link parameters IMMEDIATELY before any
// other code runs. This prevents third-party SDKs from intercepting
// or modifying these values.
NSUserActivity *userActivity =
[[[connectionOptions userActivities]
allObjects] firstObject];
UIOpenURLContext *urlContext =
[[connectionOptions URLContexts]
allObjects].firstObject;
NSURL *openUrl = urlContext.URL;
#ifdef DEBUG
// Log captured values to detect swizzling interference
NSLog(@"[SWIZZLE CHECK] "
"UserActivity captured: %@",
userActivity.webpageURL);
NSLog(@"[SWIZZLE CHECK] "
"URL Context captured: %@",
openUrl);
#endif
// Create window from windowScene
UIWindowScene *windowScene = (UIWindowScene *)scene;
self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
self.window.rootViewController = [[UIViewController alloc] init];
[self.window makeKeyAndVisible];
#ifdef DEBUG
// Print IDFV for testing in SDK Console
NSLog(@"IDFV: %@", [[[UIDevice currentDevice] identifierForVendor] UUIDString]);
#endif
// Singular initialization - uses captured values to avoid swizzling
SingularConfig *config = [self getConfig];
// Pass Universal Link if available
if (userActivity) {
config.userActivity = userActivity;
}
// Pass URL scheme if available
// CRITICAL for custom URL scheme
if (openUrl) {
config.openUrl = openUrl;
}
// Initialize Singular SDK
[Singular start:config];
}
// 2️⃣ Universal Links
- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity {
// ANTI-SWIZZLING: Capture userActivity immediately at method entry. Called
// when Universal Link is opened while app is running or backgrounded.
NSUserActivity *capturedActivity = userActivity;
#ifdef DEBUG
NSLog(@"[SWIZZLE CHECK] "
"continueUserActivity captured: %@",
capturedActivity.webpageURL);
#endif
SingularConfig *config = [self getConfig];
config.userActivity = capturedActivity;
// Initialize Singular SDK
[Singular start:config];
}
// 3️⃣ Deep link schemes
- (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLContexts {
// ANTI-SWIZZLING: Capture URL immediately at method entry. Called
// when URL scheme is opened while app is running or backgrounded.
NSURL *capturedUrl =
[[URLContexts allObjects]
firstObject].URL;
#ifdef DEBUG
NSLog(@"[SWIZZLE CHECK] "
"openURLContexts captured: %@",
capturedUrl);
#endif
SingularConfig *config = [self getConfig];
if (capturedUrl) {
config.openUrl = capturedUrl;
}
// Initialize Singular SDK
[Singular start:config];
}
@end
최신 iOS: SwiftUI 앱(iOS 14+)
코드를 추가할 위치: 기본 App구조 파일
초기화할 엔트리 포인트:
-
.onOpenURL(of: scenePhase)- 사용자 정의 URL 체계 처리 -
.onContinueUserActivity(of: scenePhase)- 유니버설 링크 처리(Singular 딥링크) -
.onChange.active- 딥링크가 발생하지 않은 경우 첫 실행 시 초기화를 처리합니다. 디퍼드 딥링크를 처리합니다.
import SwiftUI
import Singular
@main
struct simpleSwiftUIApp: App {
@Environment(\.scenePhase) var scenePhase
@State private var hasInitialized = false
var body: some Scene {
WindowGroup {
ContentView()
// 1️⃣ Handle custom URL schemes (e.g., myapp://path)
.onOpenURL { url in
#if DEBUG
print("[Singular] URL Scheme:", url.absoluteString)
#endif
guard let config = getConfig() else { return }
config.openUrl = url
Singular.start(config)
hasInitialized = true
}
// 2️⃣ Handle Universal Links (e.g., https://links.your-domain.com)
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
#if DEBUG
print("[Singular] Universal Link:", userActivity.webpageURL?.absoluteString ?? "none")
#endif
guard let config = getConfig() else { return }
config.userActivity = userActivity
Singular.start(config)
hasInitialized = true
}
}
.onChange(of: scenePhase) { oldPhase, newPhase in
switch newPhase {
case .active:
// 3️⃣ Initialize ONLY on first launch if no deep link occurred
guard !hasInitialized else {
#if DEBUG
print("[Singular] Already initialized, skipping")
#endif
return
}
#if DEBUG
if let idfv = UIDevice.current.identifierForVendor?.uuidString {
print("[Singular] IDFV:", idfv)
}
#endif
guard let config = getConfig() else { return }
Singular.start(config)
hasInitialized = true
case .background:
#if DEBUG
print("[Singular] App backgrounded")
#endif
case .inactive:
#if DEBUG
print("[Singular] App inactive")
#endif
@unknown default:
break
}
}
}
// Add your getConfig() function here
// MARK: - Singular Configuration
private func getConfig() -> SingularConfig? {
// ... (same as above)
}
// Add your handleDeeplink() function here
// MARK: - Deep Link Handler
private func handleDeeplink(_ params: SingularLinkParams) {
// ... (same as above)
}
}
레거시 iOS: 앱디렉티브(iOS 13 이전)
코드를 추가할 위치: AppDelegate.swift
또는 AppDelegate.m
초기화할 엔트리 포인트:
-
didFinishLaunchingWithOptions- 앱 실행 -
continue userActivity- 유니버설 링크 -
open url- 딥링크 방식
import Singular
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
// 1️⃣ App launch
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// ANTI-SWIZZLING: Capture deep link parameters IMMEDIATELY before any
// other code runs. This prevents third-party SDKs from intercepting
// or modifying these values.
let launchUrl = launchOptions?[.url] as? URL
let userActivityDictionary = launchOptions?[.userActivityDictionary] as? [String: Any]
let userActivity = userActivityDictionary?["UIApplicationLaunchOptionsUserActivityKey"] as? NSUserActivity
#if DEBUG
// Log captured values to detect swizzling interference
print("[SWIZZLE CHECK] Launch URL captured:", launchUrl?.absoluteString ?? "none")
print("[SWIZZLE CHECK] UserActivity captured:", userActivity?.webpageURL?.absoluteString ?? "none")
print("IDFV:", UIDevice.current.identifierForVendor?.uuidString ?? "N/A")
#endif
// Singular initialization - uses captured values to avoid swizzling conflicts
guard let config = getConfig() else { return true }
// Pass the entire launchOptions dictionary for Singular's internal processing
config.launchOptions = launchOptions
// Explicitly pass Universal Link if available
// CRITICAL for universal link attribution
if let userActivity = userActivity {
config.userActivity = userActivity
}
// Explicitly pass URL scheme if available
// CRITICAL for custom URL scheme attribution
if let launchUrl = launchUrl {
config.openUrl = launchUrl
}
// Initialize Singular SDK
Singular.start(config)
return true
}
// 2️⃣ Universal Links
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([any UIUserActivityRestoring]?) -> Void) -> Bool {
#if DEBUG
print("[SWIZZLE CHECK] Universal Link handler called:", userActivity.webpageURL?.absoluteString ?? "none")
#endif
guard let config = getConfig() else { return true }
config.userActivity = userActivity
Singular.start(config)
return true
}
// 3️⃣ Deep link schemes
func application(_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
#if DEBUG
print("[SWIZZLE CHECK] URL Scheme handler called:", url.absoluteString)
#endif
guard let config = getConfig() else { return true }
config.openUrl = url
Singular.start(config)
return true
}
// Add your getConfig() function here
// MARK: - Singular Configuration
private func getConfig() -> SingularConfig? {
// ... (same as above)
}
// Add your handleDeeplink() function here
// MARK: - Deep Link Handler
private func handleDeeplink(_ params: SingularLinkParams) {
// ... (same as above)
}
}
#import "AppDelegate.h"
#import <Singular/Singular.h>
@implementation AppDelegate
// 1️⃣ App launch
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ANTI-SWIZZLING: Capture deep link parameters IMMEDIATELY before any
// other code runs. This prevents third-party SDKs from intercepting
// or modifying these values.
NSURL *launchUrl = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey];
NSDictionary *userActivityDictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey];
NSUserActivity *userActivity = [userActivityDictionary objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"];
#if DEBUG
// Log captured values to detect swizzling interference
NSLog(@"[SWIZZLE CHECK] Launch URL captured: %@", launchUrl.absoluteString ?: @"none");
NSLog(@"[SWIZZLE CHECK] UserActivity captured: %@", userActivity.webpageURL.absoluteString ?: @"none");
NSLog(@"IDFV: %@", [UIDevice currentDevice].identifierForVendor.UUIDString ?: @"N/A");
#endif
// Singular initialization - uses captured values to avoid swizzling conflicts
SingularConfig *config = [self getConfig];
if (!config) {
return YES;
}
// Pass the entire launchOptions dictionary for Singular's internal processing
config.launchOptions = launchOptions;
// Explicitly pass Universal Link if available
// CRITICAL for universal link attribution
if (userActivity) {
config.userActivity = userActivity;
}
// Explicitly pass URL scheme if available
// CRITICAL for custom URL scheme attribution
if (launchUrl) {
config.openUrl = launchUrl;
}
// Initialize Singular SDK
[Singular start:config];
return YES;
}
// 2️⃣ Universal Links
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
#if DEBUG
NSLog(@"[SWIZZLE CHECK] Universal Link handler called: %@", userActivity.webpageURL.absoluteString ?: @"none");
#endif
SingularConfig *config = [self getConfig];
if (!config) {
return YES;
}
config.userActivity = userActivity;
[Singular start:config];
return YES;
}
// 3️⃣ Deep link schemes
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
#if DEBUG
NSLog(@"[SWIZZLE CHECK] URL Scheme handler called: %@", url.absoluteString);
#endif
SingularConfig *config = [self getConfig];
if (!config) {
return YES;
}
config.openUrl = url;
[Singular start:config];
return YES;
}
#pragma mark - Singular Configuration
- (SingularConfig *)getConfig {
}
#pragma mark - OPTIONAL: Deep link handler implementation
- (void)handleDeeplink:(SingularLinkParams *)params {
}
@end
설치 확인
비행 전 체크리스트
연동을 빌드하고 테스트하기 전에 다음 항목을 확인하세요.
- 코코아팟, SPM 또는 수동 프레임워크를 통해 설치된 SDK
- 스위프트 브리징 헤더 생성(스위프트를 사용하는 경우)
-
getConfig()구현된 함수 -
Singular.start(config)모든 진입점에서 호출됨 - 구성에 SDK 키 및 SDK 시크릿 추가됨
- ATT 타임아웃 구성됨(ATT 프롬프트가 표시되는 경우에만)
- 딥링크 핸들러 구성됨(딥링크를 사용하는 경우에만)
- 오류 없이 앱 빌드
다음 단계:
- 앱 빌드 및 실행
- 콘솔에서 IDFV 인쇄 문 확인
- IDFV를 사용하여 Singular SDK 콘솔에서테스트하기
- 1~2분 이내에 SDK 콘솔에 세션이 표시되는지 확인합니다.
선택 사항: 앱 추적 투명성(ATT)
IDFA 액세스에 대한 사용자 권한을 요청하고 어트리뷰션 정확도를 개선하도록 ATT를 구성합니다.
다음과 같은 경우 이 섹션을 건너뛰세요: 앱에 ATT 프롬프트가 표시되지 않는 경우.
ATT 동의를 요청하는 이유는 무엇인가요?
IDFA의 이점
iOS 14.5부터 앱은 기기의 IDFA(광고주 식별자)에 액세스하기 위해 사용자에게 권한을 요청해야 합니다.
IDFA가 있는 경우와 없는 경우의 어트리뷰션 비교:
- IDFA 사용: 정밀한 디바이스 수준 어트리뷰션 및 정확한 인스톨 매칭
- IDFA 미사용: IP, 사용자 에이전트, 디바이스 핑거프린팅을 사용한 확률론적 어트리뷰션
권장 사항: 어트리뷰션 정확도를 높이기 위해 ATT 동의를 요청하세요. IDFA 없이도 Singular 어트리뷰션이 가능하지만 정확도가 떨어집니다.
ATT 지연 구성
SDK 초기화 지연
첫 번째 세션을 Singular로 보내기 전에 사용자의 ATT 응답을 기다리는 시간 제한을 추가합니다.
중요: SDK는 첫 번째 세션을 전송하기 전에 ATT 동의를 기다려야 합니다. 그렇지 않으면 초기 어트리뷰션 이벤트에 IDFA가 포함되지 않습니다.
func getSingularConfig() -> SingularConfig? {
guard let config = SingularConfig(
apiKey: "YOUR_SDK_KEY",
andSecret: "YOUR_SDK_SECRET"
) else {
return nil
}
// Wait up to 300 seconds for ATT response
config.waitForTrackingAuthorizationWithTimeoutInterval = 300
return config
}
- (SingularConfig *)getSingularConfig {
SingularConfig *config = [[SingularConfig alloc]
initWithApiKey:@"YOUR_SDK_KEY"
andSecret:@"YOUR_SDK_SECRET"];
// Wait up to 300 seconds for ATT response
config.waitForTrackingAuthorizationWithTimeoutInterval = 300;
return config;
}
ATT 흐름 타임라인
ATT 지연을 구성하면 다음과 같은 일이 발생합니다:
- 앱 실행: SDK가 이벤트 기록을 시작하지만 아직 이벤트를 전송하지 않습니다.
- ATT 프롬프트: 앱에 ATT 동의 대화 상자가 표시됩니다.
- 사용자 응답: 사용자가 권한을 부여하거나 거부합니다.
- SDK가 데이터를 전송합니다: SDK가 대기 중인 이벤트를 IDFA로 즉시 전송합니다(허용된 경우).
- 시간 초과 폴백: 응답 없이 300초가 지나면 SDK가 데이터를 전송합니다.
모범 사례: 어트리뷰션에 대한 IDFA 가용성을 극대화하려면 가능한 한 빨리(이상적으로는 앱을 처음 실행할 때) ATT 프롬프트를 표시하세요.