모바일 SDK인앱 이벤트
iOS
AdStage 인앱 이벤트 통합 가이드 (iOS)
목차
개요
AdStage 인앱 이벤트는 사용자 행동과 앱 이벤트를 추적하여 마케팅 분석 및 최적화를 지원합니다.
주요 기능
- 전역 컨텍스트 관리: 사용자/디바이스 정보를 한 번 설정하면 자동 포함
- 간단한 API: 이벤트 이름과 파라미터만으로 간편하게 전송
- 자동 세션 추적: 세션 ID 자동 생성 및 관리
- 비동기 처리: 네트워크 통신이 UI를 블록하지 않음
- 오프라인 지원: 네트워크 복구 시 자동 재전송
기본 설정
CocoaPods 설정
Podfile
platform :ios, '12.0'
target 'YourApp' do
use_frameworks!
# AdStage SDK
pod 'AdapterAdStage', '3.0.5'
# 의존성 (자동 설치됨)
# Alamofire, SwiftyJSON 등
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
end
end
end설치:
pod install2. SDK 초기화
import AdapterAdStage
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// AdStage 초기화
AdStageManager.shared.initialize(
apiKey: "your-api-key-here",
serverUrl: "https://api.adstage.app"
)
print("✅ AdStage SDK 초기화 완료")
return true
}
}3. Info.plist 설정
<!-- 네트워크 사용 권한 -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
</dict>타입 안전 이벤트 전송
1. 가장 간단한 방식
import AdapterAdStage
// 파라미터 없는 이벤트
AdStageManager.shared.trackEvent(AdStageEvent.FirstOpen())
// 로그인 이벤트
AdStageManager.shared.trackEvent(
AdStageEvent.Login(method: .email)
)2. 구매 이벤트 (검증 예제)
// ✅ GOOD: value와 currency 함께 제공
AdStageManager.shared.trackEvent(
AdStageEvent.Purchase(
value: 29.99,
currency: "USD",
transactionId: "TXN_123456"
)
)
// ❌ BAD: value만 제공 시 컴파일 에러!
AdStageManager.shared.trackEvent(
AdStageEvent.Purchase(
value: 29.99 // ❌ 에러: currency 필수!
)
)
// ✅ GOOD: currency 없이 transactionId만
AdStageManager.shared.trackEvent(
AdStageEvent.Purchase(
transactionId: "TXN_123456"
)
)3. 상품 조회
// 파라미터와 함께
AdStageManager.shared.trackEvent(
AdStageEvent.ProductDetailsView(
itemId: "PROD_123",
itemName: "Wireless Earbuds"
)
)
// 파라미터 없이
AdStageManager.shared.trackEvent(AdStageEvent.ProductListView())4. 커스텀 이벤트 (event_name 자동 승격)
표준 이벤트 외에 앱 고유의 이벤트를 전송할 때는 AdStageEvent.Custom을 사용합니다.
파라미터에 event_name 키를 포함하면, 해당 값이 실제 이벤트 이름으로 자동 승격되어 대시보드에 저장됩니다.
// 예: 'promotion_click'이라는 커스텀 이벤트 전송
AdStageManager.shared.trackEvent(
AdStageEvent.Custom(
params: [
"event_name": "promotion_click",
"promotion_id": "summer_sale_2025",
"screen": "home_banner"
]
)
)
// 대시보드에는 "promotion_click" 이벤트로 수집됨
// event_name 없이 일반 커스텀 이벤트
AdStageManager.shared.trackEvent(
AdStageEvent.Custom(
params: [
"action": "button_click",
"button_id": "promo_banner",
"screen": "home",
"timestamp": Date().timeIntervalSince1970
]
)
)
// 대시보드에는 "custom" 이벤트로 수집됨
// Click, View도 자유 파라미터 지원
AdStageManager.shared.trackEvent(
AdStageEvent.Click(
params: [
"campaign_id": "SUMMER2025",
"ad_group": "electronics"
]
)
)표준 이벤트 카탈로그
AdStage SDK는 46개의 표준 이벤트를 제공합니다.
📌 광고 추적 (4개)
// 1. 광고 클릭
AdStageManager.shared.trackEvent(
AdStageEvent.Click(
params: ["campaign_id": "CAMP_123"]
)
)
// 2. 광고 노출
AdStageManager.shared.trackEvent(
AdStageEvent.View(
params: ["impression_id": "IMP_456"]
)
)
// 3. 앱 설치
AdStageManager.shared.trackEvent(AdStageEvent.Install())
// 4. 커스텀 이벤트 (event_name으로 이벤트 이름 지정)
AdStageManager.shared.trackEvent(
AdStageEvent.Custom(
params: [
"event_name": "promotion_click",
"promotion_id": "summer_sale_2025"
]
)
)👤 사용자 라이프사이클 (5개)
// SignUpMethod enum
public enum SignUpMethod: String {
case email = "email"
case google = "google"
case apple = "apple"
case facebook = "facebook"
case kakao = "kakao"
case naver = "naver"
}
// 1. 회원가입 완료
AdStageManager.shared.trackEvent(
AdStageEvent.SignUp(method: .google)
)
// 2. 회원가입 시작
AdStageManager.shared.trackEvent(AdStageEvent.SignUpStart())
// 3. 로그인
AdStageManager.shared.trackEvent(
AdStageEvent.Login(method: .email)
)
// 4. 로그아웃
AdStageManager.shared.trackEvent(AdStageEvent.Logout())
// 5. 앱 최초 실행
AdStageManager.shared.trackEvent(AdStageEvent.FirstOpen())📄 콘텐츠 조회 (6개)
// 1. 홈 화면
AdStageManager.shared.trackEvent(AdStageEvent.HomeView())
// 2. 상품 목록
AdStageManager.shared.trackEvent(
AdStageEvent.ProductListView(itemCategory: "electronics")
)
// 3. 검색 결과
AdStageManager.shared.trackEvent(
AdStageEvent.SearchResultView(searchTerm: "wireless headphones")
)
// 4. 상품 상세
AdStageManager.shared.trackEvent(
AdStageEvent.ProductDetailsView(
itemId: "PROD_123",
itemName: "Wireless Earbuds"
)
)
// 5. 페이지 조회 (웹)
AdStageManager.shared.trackEvent(
AdStageEvent.PageView(
pageUrl: "https://example.com/products",
pageTitle: "Products"
)
)
// 6. 화면 조회 (앱)
AdStageManager.shared.trackEvent(
AdStageEvent.ScreenView(
screenName: "product_detail",
screenClass: "ProductDetailViewController"
)
)🛒 전자상거래 (8개)
// 1. 장바구니 추가
AdStageManager.shared.trackEvent(
AdStageEvent.AddToCart(
value: 99000.0,
currency: "KRW",
items: [
EcommerceItem(
itemId: "PROD_123",
itemName: "Wireless Earbuds",
price: 99000.0,
quantity: 1
)
]
)
)
// 2. 장바구니 제거
AdStageManager.shared.trackEvent(
AdStageEvent.RemoveFromCart(
value: 50000.0,
currency: "KRW"
)
)
// 3. 위시리스트 추가
AdStageManager.shared.trackEvent(
AdStageEvent.AddToWishlist(
itemId: "PROD_456",
itemName: "Smart Watch"
)
)
// 4. 결제 정보 입력
AdStageManager.shared.trackEvent(
AdStageEvent.AddPaymentInfo(paymentType: "credit_card")
)
// 5. 결제 시작
AdStageManager.shared.trackEvent(
AdStageEvent.BeginCheckout(
value: 150000.0,
currency: "KRW"
)
)
// 6. 구매 완료 ⭐⭐⭐
AdStageManager.shared.trackEvent(
AdStageEvent.Purchase(
value: 129000.0,
currency: "KRW",
transactionId: "ORDER_20250105_001",
tax: 12900.0,
shipping: 3000.0,
coupon: "SUMMER2025",
items: [
EcommerceItem(
itemId: "PROD_123",
itemName: "Wireless Earbuds",
itemCategory: "electronics",
price: 126000.0,
quantity: 1
)
]
)
)
// 7. 환불
AdStageManager.shared.trackEvent(
AdStageEvent.Refund(
transactionId: "ORDER_20250105_001",
value: 129000.0,
currency: "KRW"
)
)🎮 진행/성취 (4개)
// 1. 튜토리얼 시작
AdStageManager.shared.trackEvent(
AdStageEvent.TutorialBegin(
params: ["tutorial_id": "intro"]
)
)
// 2. 튜토리얼 완료
AdStageManager.shared.trackEvent(
AdStageEvent.TutorialComplete(
params: ["duration_seconds": 120]
)
)
// 3. 레벨 업
AdStageManager.shared.trackEvent(
AdStageEvent.LevelUp(
level: 25,
character: "warrior"
)
)
// 4. 업적 달성
AdStageManager.shared.trackEvent(
AdStageEvent.Achievement(achievementId: "first_win")
)💬 상호작용 (3개)
// 1. 검색
AdStageManager.shared.trackEvent(
AdStageEvent.Search(searchTerm: "gaming laptop")
)
// 2. 공유
AdStageManager.shared.trackEvent(
AdStageEvent.Share(
contentType: "product",
itemId: "PROD_789",
method: "kakao"
)
)
// 3. 광고 클릭
AdStageManager.shared.trackEvent(
AdStageEvent.AdClick(adId: "AD_12345")
)🎮 게임 특화 (4개)
// 1. 게임 플레이
AdStageManager.shared.trackEvent(
AdStageEvent.GamePlay(
level: 10,
levelName: "Dragon's Lair",
character: "mage",
contentType: "dungeon"
)
)
// 2. 보너스 획득
AdStageManager.shared.trackEvent(
AdStageEvent.AcquireBonus(
contentType: "reward",
itemId: "ITEM_123",
itemName: "Gold Chest",
quantity: 1
)
)
// 3. 게임 서버 선택
AdStageManager.shared.trackEvent(
AdStageEvent.SelectGameServer(
contentId: "SERVER_01",
contentType: "pvp",
itemName: "Asia Server"
)
)
// 4. 패치 완료
AdStageManager.shared.trackEvent(
AdStageEvent.CompletePatch(
contentId: "PATCH_2.1.0",
contentType: "update"
)
)📅 구독/체험 (3개)
// 1. 무료 체험 시작
AdStageManager.shared.trackEvent(
AdStageEvent.StartTrial(
value: 9900.0,
currency: "KRW",
trialDays: 14
)
)
// 2. 구독 시작
AdStageManager.shared.trackEvent(
AdStageEvent.Subscribe(
value: 9900.0,
currency: "KRW",
subscriptionId: "premium_monthly"
)
)
// 3. 구독 취소
AdStageManager.shared.trackEvent(
AdStageEvent.Unsubscribe(subscriptionId: "premium_monthly")
)💰 가상 화폐 (2개)
// 1. 가상 화폐 획득
AdStageManager.shared.trackEvent(
AdStageEvent.EarnVirtualCurrency(
virtualCurrencyName: "gold",
value: 500.0
)
)
// 2. 가상 화폐 사용
AdStageManager.shared.trackEvent(
AdStageEvent.SpendVirtualCurrency(
virtualCurrencyName: "gold",
value: 100.0,
itemName: "health_potion"
)
)🎯 기타 (7개)
// 1. 일정 등록
AdStageManager.shared.trackEvent(
AdStageEvent.Schedule(
params: ["event_type": "appointment"]
)
)
// 2. 크레딧 사용
AdStageManager.shared.trackEvent(
AdStageEvent.SpendCredits(
value: 10.0,
itemName: "premium_feature"
)
)
// 3. 프로모션 조회
AdStageManager.shared.trackEvent(
AdStageEvent.ViewPromotion(
promotionId: "PROMO_SUMMER",
promotionName: "Summer Sale 2025",
creativeSlot: "home_banner_1"
)
)
// 4. 프로모션 선택
AdStageManager.shared.trackEvent(
AdStageEvent.SelectPromotion(
promotionId: "PROMO_SUMMER",
promotionName: "Summer Sale 2025"
)
)이벤트 타입별 예제
1. 앱 라이프사이클
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// SDK 초기화
AdStageManager.shared.initialize(
apiKey: "your-api-key",
serverUrl: "https://api.adstage.app"
)
// 첫 실행 이벤트는 SDK가 자동으로 전송
print("✅ 앱 시작")
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
print("📱 앱 백그라운드 진입")
}
func applicationWillEnterForeground(_ application: UIApplication) {
print("📱 앱 포그라운드 진입")
}
}2. 사용자 인증
class AuthManager {
// 회원가입
func onSignUpComplete(method: String) {
let signUpMethod: SignUpMethod
switch method {
case "email": signUpMethod = .email
case "google": signUpMethod = .google
case "apple": signUpMethod = .apple
case "kakao": signUpMethod = .kakao
case "naver": signUpMethod = .naver
default: signUpMethod = .email
}
AdStageManager.shared.trackEvent(
AdStageEvent.SignUp(method: signUpMethod)
)
print("✅ 회원가입 완료: \(method)")
}
// 로그인
func onLoginSuccess(userId: String, method: String) {
let loginMethod: SignUpMethod = method == "google" ? .google : .email
AdStageManager.shared.trackEvent(
AdStageEvent.Login(method: loginMethod)
)
print("✅ 로그인: \(userId)")
}
// 로그아웃
func onLogout() {
AdStageManager.shared.trackEvent(AdStageEvent.Logout())
print("🚪 로그아웃")
}
}3. 화면 추적 (BaseViewController 패턴)
class BaseViewController: UIViewController {
// 하위 클래스에서 오버라이드
var screenName: String {
return String(describing: type(of: self))
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
trackScreenView()
}
private func trackScreenView() {
AdStageManager.shared.trackEvent(
AdStageEvent.ScreenView(
screenName: screenName,
screenClass: String(describing: type(of: self))
)
)
print("📺 \(screenName)")
}
}
// 사용 예제
class HomeViewController: BaseViewController {
override var screenName: String { "home" }
}
class ProductDetailViewController: BaseViewController {
override var screenName: String { "product_detail" }
var productId: String?
func loadProduct(_ productId: String) {
self.productId = productId
// 데이터 로딩...
// 상품 상세 조회 이벤트
AdStageManager.shared.trackEvent(
AdStageEvent.ProductDetailsView(
itemId: productId,
itemName: product.name
)
)
}
}4. 전자상거래 전체 플로우
class EcommerceManager {
// 1. 상품 목록 조회
func trackProductList(category: String) {
AdStageManager.shared.trackEvent(
AdStageEvent.ProductListView(itemCategory: category)
)
}
// 2. 검색
func trackSearch(query: String) {
AdStageManager.shared.trackEvent(
AdStageEvent.Search(searchTerm: query)
)
}
// 3. 검색 결과 조회
func trackSearchResults(query: String) {
AdStageManager.shared.trackEvent(
AdStageEvent.SearchResultView(searchTerm: query)
)
}
// 4. 상품 상세 조회
func trackProductView(product: Product) {
AdStageManager.shared.trackEvent(
AdStageEvent.ProductDetailsView(
itemId: product.id,
itemName: product.name
)
)
}
// 5. 장바구니 추가
func trackAddToCart(product: Product, quantity: Int) {
let item = EcommerceItem(
itemId: product.id,
itemName: product.name,
itemCategory: product.category,
price: product.price,
quantity: quantity
)
AdStageManager.shared.trackEvent(
AdStageEvent.AddToCart(
value: product.price * Double(quantity),
currency: "KRW",
items: [item]
)
)
}
// 6. 위시리스트 추가
func trackAddToWishlist(product: Product) {
AdStageManager.shared.trackEvent(
AdStageEvent.AddToWishlist(
itemId: product.id,
itemName: product.name
)
)
}
// 7. 결제 시작
func trackBeginCheckout(cart: Cart) {
let items = cart.items.map { cartItem in
EcommerceItem(
itemId: cartItem.product.id,
itemName: cartItem.product.name,
itemCategory: cartItem.product.category,
price: cartItem.product.price,
quantity: cartItem.quantity
)
}
AdStageManager.shared.trackEvent(
AdStageEvent.BeginCheckout(
value: cart.totalAmount,
currency: "KRW",
items: items
)
)
}
// 8. 결제 정보 입력
func trackAddPaymentInfo(paymentMethod: String) {
AdStageManager.shared.trackEvent(
AdStageEvent.AddPaymentInfo(paymentType: paymentMethod)
)
}
// 9. 구매 완료 ⭐⭐⭐
func trackPurchase(order: Order) {
let items = order.items.map { orderItem in
EcommerceItem(
itemId: orderItem.product.id,
itemName: orderItem.product.name,
itemCategory: orderItem.product.category,
price: orderItem.product.price,
quantity: orderItem.quantity
)
}
AdStageManager.shared.trackEvent(
AdStageEvent.Purchase(
value: order.totalAmount,
currency: "KRW",
transactionId: order.id,
tax: order.tax,
shipping: order.shippingFee,
coupon: order.couponCode,
items: items
)
)
print("✅ 구매 완료: \(order.id), \(order.totalAmount)원")
}
// 10. 환불
func trackRefund(order: Order) {
AdStageManager.shared.trackEvent(
AdStageEvent.Refund(
transactionId: order.id,
value: order.totalAmount,
currency: "KRW"
)
)
}
// 11. 공유
func trackShare(product: Product, method: String) {
AdStageManager.shared.trackEvent(
AdStageEvent.Share(
contentType: "product",
itemId: product.id,
method: method
)
)
}
}5. 게임 이벤트
class GameEventManager {
// 튜토리얼
func trackTutorialBegin(tutorialId: String) {
AdStageManager.shared.trackEvent(
AdStageEvent.TutorialBegin(
params: ["tutorial_id": tutorialId]
)
)
}
func trackTutorialComplete(tutorialId: String, duration: TimeInterval) {
AdStageManager.shared.trackEvent(
AdStageEvent.TutorialComplete(
params: [
"tutorial_id": tutorialId,
"duration_seconds": Int(duration)
]
)
)
}
// 레벨
func trackLevelUp(level: Int, character: String) {
AdStageManager.shared.trackEvent(
AdStageEvent.LevelUp(
level: level,
character: character
)
)
}
// 업적
func trackAchievement(achievementId: String) {
AdStageManager.shared.trackEvent(
AdStageEvent.Achievement(achievementId: achievementId)
)
}
// 게임 플레이
func trackGameStart(level: Int, levelName: String, character: String) {
AdStageManager.shared.trackEvent(
AdStageEvent.GamePlay(
level: level,
levelName: levelName,
character: character,
contentType: "pvp"
)
)
}
// 보너스 획득
func trackBonusAcquired(itemId: String, itemName: String, quantity: Int) {
AdStageManager.shared.trackEvent(
AdStageEvent.AcquireBonus(
contentType: "reward",
itemId: itemId,
itemName: itemName,
quantity: quantity
)
)
}
// 가상 화폐
func trackEarnCurrency(currencyName: String, amount: Double) {
AdStageManager.shared.trackEvent(
AdStageEvent.EarnVirtualCurrency(
virtualCurrencyName: currencyName,
value: amount
)
)
}
func trackSpendCurrency(currencyName: String, amount: Double, itemName: String) {
AdStageManager.shared.trackEvent(
AdStageEvent.SpendVirtualCurrency(
virtualCurrencyName: currencyName,
value: amount,
itemName: itemName
)
)
}
}베스트 프랙티스
1. Extension 활용
// UIViewController+AdStage.swift
extension UIViewController {
func trackScreen() {
let screenName = String(describing: type(of: self))
.replacingOccurrences(of: "ViewController", with: "")
.lowercased()
AdStageManager.shared.trackEvent(
AdStageEvent.ScreenView(
screenName: screenName,
screenClass: String(describing: type(of: self))
)
)
}
}
// 사용
class ProfileViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
trackScreen() // ✅ 간단!
}
}2. ViewModel에서 이벤트 추적
class ProductViewModel: ObservableObject {
@Published var product: Product?
func loadProduct(productId: String) {
Task {
do {
let product = try await repository.getProduct(productId)
self.product = product
// 상품 조회 이벤트
AdStageManager.shared.trackEvent(
AdStageEvent.ProductDetailsView(
itemId: product.id,
itemName: product.name
)
)
} catch {
print("Failed to load product: \(error)")
}
}
}
func addToCart(product: Product, quantity: Int) {
Task {
do {
try await repository.addToCart(product, quantity: quantity)
let item = EcommerceItem(
itemId: product.id,
itemName: product.name,
price: product.price,
quantity: quantity
)
AdStageManager.shared.trackEvent(
AdStageEvent.AddToCart(
value: product.price * Double(quantity),
currency: "KRW",
items: [item]
)
)
} catch {
print("Failed to add to cart: \(error)")
}
}
}
}3. SwiftUI에서 사용
import SwiftUI
import AdapterAdStage
struct ProductDetailView: View {
let productId: String
@StateObject private var viewModel = ProductViewModel()
var body: some View {
VStack {
// UI...
Button("장바구니에 추가") {
viewModel.addToCart(viewModel.product, quantity: 1)
// 버튼 클릭 이벤트
AdStageManager.shared.trackEvent(
AdStageEvent.Custom(
params: [
"action": "add_to_cart_button",
"product_id": productId
]
)
)
}
}
.onAppear {
viewModel.loadProduct(productId: productId)
// 화면 조회 이벤트
AdStageManager.shared.trackEvent(
AdStageEvent.ScreenView(
screenName: "product_detail",
screenClass: "ProductDetailView"
)
)
}
}
}4. 이벤트 래퍼 클래스
class AnalyticsManager {
static let shared = AnalyticsManager()
private init() {}
func trackScreen(_ screenName: String, screenClass: String? = nil) {
AdStageManager.shared.trackEvent(
AdStageEvent.ScreenView(
screenName: screenName,
screenClass: screenClass
)
)
print("📺 Screen: \(screenName)")
}
func trackPurchase(_ order: Order) {
let items = order.items.map {
EcommerceItem(
itemId: $0.product.id,
itemName: $0.product.name,
price: $0.product.price,
quantity: $0.quantity
)
}
AdStageManager.shared.trackEvent(
AdStageEvent.Purchase(
value: order.totalAmount,
currency: "KRW",
transactionId: order.id,
items: items
)
)
print("💰 Purchase: \(order.id)")
}
func trackError(_ error: Error, context: String) {
AdStageManager.shared.trackEvent(
AdStageEvent.Custom(
params: [
"event_type": "error",
"error_message": error.localizedDescription,
"context": context
]
)
)
print("❌ Error: \(context) - \(error)")
}
}5. 성능 최적화: Throttling
class ThrottledEventTracker {
private var lastTrackTimes: [String: TimeInterval] = [:]
private let throttleInterval: TimeInterval = 2.0 // 2초
func trackEvent(_ event: AdStageEventProtocol) {
let eventKey = event.eventName
let now = Date().timeIntervalSince1970
let lastTime = lastTrackTimes[eventKey] ?? 0
if now - lastTime >= throttleInterval {
AdStageManager.shared.trackEvent(event)
lastTrackTimes[eventKey] = now
} else {
print("⏱️ Throttled: \(eventKey)")
}
}
}
// 사용 (스크롤 이벤트 등)
let throttledTracker = ThrottledEventTracker()
func scrollViewDidScroll(_ scrollView: UIScrollView) {
throttledTracker.trackEvent(
AdStageEvent.Custom(
params: [
"action": "scroll",
"offset_y": scrollView.contentOffset.y
]
)
)
}변환 예제
// 1. 기본 이벤트
// OLD: EventTrackingManager.shared.trackEvent(...)
// NEW:
AdStageManager.shared.trackEvent(AdStageEvent.FirstOpen())
// 2. 로그인
// NEW:
AdStageManager.shared.trackEvent(AdStageEvent.Login(method: .email))
// 3. 화면 조회
// NEW:
AdStageManager.shared.trackEvent(AdStageEvent.ScreenView(screenName: "home"))
// 4. 커스텀
// NEW:
AdStageManager.shared.trackEvent(
AdStageEvent.Custom(params: ["action": "click"])
)트러블슈팅
1. 컴파일 에러: "currency 파라미터 필수"
문제:
// ❌ 에러
AdStageManager.shared.trackEvent(
AdStageEvent.Purchase(value: 9900.0)
)해결:
// ✅ currency 추가 (ISO 4217, 3자리)
AdStageManager.shared.trackEvent(
AdStageEvent.Purchase(
value: 9900.0,
currency: "KRW"
)
)2. Xcode 자동완성 작동 안 함
해결:
- Clean Build Folder:
Product → Clean Build Folder (⇧⌘K) - Derived Data 삭제:
~/Library/Developer/Xcode/DerivedData - Pod 재설치:
pod deintegrate
pod install3. 통화 코드 검증 에러
// ❌ 2자리 코드
AdStageEvent.Purchase(value: 100.0, currency: "KR")
// ValidationException: "Currency must be 3-letter ISO 4217 code"
// ✅ 3자리 ISO 4217 코드
AdStageEvent.Purchase(value: 100.0, currency: "KRW") // 한국 원
AdStageEvent.Purchase(value: 100.0, currency: "USD") // 미국 달러
AdStageEvent.Purchase(value: 100.0, currency: "JPY") // 일본 엔4. SwiftUI Preview 충돌
문제: Preview에서 AdStageManager 초기화 에러
해결:
#if DEBUG
struct ProductDetailView_Previews: PreviewProvider {
static var previews: some View {
ProductDetailView(productId: "PROD_123")
.onAppear {
// Preview에서는 초기화하지 않음
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == nil {
AdStageManager.shared.initialize(
apiKey: "test-key",
serverUrl: "https://api.adstage.app"
)
}
}
}
}
#endif5. 디버그 로그 확인
// AppDelegate.swift
func application(...) -> Bool {
#if DEBUG
AdStageManager.shared.setDebugMode(true)
#endif
AdStageManager.shared.initialize(...)
return true
}콘솔 필터:
# Xcode Console
filter: AdStage참고 자료
표준 참조
FAQ
Q: 커스텀 이벤트는 어떻게 전송하나요?
A: AdStageEvent.Custom(params: [...]) 사용
Q: Android SDK와 호환되나요?
A: 예, v3.0은 Android SDK와 100% 동일한 46개 이벤트를 지원합니다.
Q: SwiftUI에서 사용할 수 있나요?
A: 예, ObservableObject 패턴으로 완벽히 지원됩니다.
지원
연락처:
- 이메일: support@nbase.io
© 2025 NBase. All rights reserved.

