adstage
行動 SDK應用程式內事件

iOS

AdStage 應用程式內事件串接指南(iOS)

目錄

  1. 概述
  2. 基本設定
  3. 型別安全的事件傳送
  4. 標準事件目錄
  5. 各事件類型範例
  6. 最佳實務
  7. 疑難排解

概述

AdStage 應用程式內事件透過追蹤使用者行為與應用程式事件,支援行銷分析與最佳化。

主要功能

  • 全域情境管理:使用者/裝置資訊只需設定一次即可自動附帶
  • 簡潔的 API:僅憑事件名稱與參數即可輕鬆傳送
  • 自動工作階段追蹤:自動產生並管理工作階段 ID
  • 非同步處理:網路通訊不會阻擋 UI
  • 離線支援:網路恢復時自動重新傳送

基本設定

CocoaPods 設定

Podfile

platform :ios, '15.0'
 
target 'YourApp' do
  use_frameworks!
  
  # AdStage SDK
  pod 'AdapterAdStage', '3.0.11'
end
 
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
    end
  end
end

安裝:

pod install

2. 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 是 SDK 提供的 enum(請勿自行定義)
// 可用的值:.email, .google, .apple, .facebook, .kakao, .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",
                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",
        method: "kakao"
    )
)
 
// 3. 應用程式內廣告點擊
AdStageManager.shared.trackEvent(
    AdStageEvent.AdClick(
        adPlatform: "admob",
        adFormat: "rewarded_video",
        adUnitName: "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. 取得虛擬貨幣(點數)- 使用 AcquireBonus
AdStageManager.shared.trackEvent(
    AdStageEvent.AcquireBonus(
        contentType: "currency",
        itemName: "gold",
        quantity: 500
    )
)
 
// 2. 使用虛擬貨幣(點數)- 使用 SpendCredits
AdStageManager.shared.trackEvent(
    AdStageEvent.SpendCredits(
        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. 瀏覽推廣(以 Custom 事件傳送)
AdStageManager.shared.trackEvent(
    AdStageEvent.Custom(
        params: [
            "event_name": "view_promotion",
            "promotion_id": "PROMO_SUMMER",
            "promotion_name": "Summer Sale 2025",
            "creative_slot": "home_banner_1"
        ]
    )
)
 
// 4. 選擇推廣(以 Custom 事件傳送)
AdStageManager.shared.trackEvent(
    AdStageEvent.Custom(
        params: [
            "event_name": "select_promotion",
            "promotion_id": "PROMO_SUMMER",
            "promotion_name": "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,
            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,
                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,
                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",
                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
            )
        )
    }
    
    // 取得虛擬貨幣(點數)- 使用 AcquireBonus
    func trackEarnCurrency(currencyName: String, amount: Int) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.AcquireBonus(
                contentType: "currency",
                itemName: currencyName,
                quantity: amount
            )
        )
    }
    
    // 使用虛擬貨幣(點數)- 使用 SpendCredits
    func trackSpendCurrency(currencyName: String, amount: Double, itemName: String) {
        AdStageManager.shared.trackEvent(
            AdStageEvent.SpendCredits(
                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 自動完成無法運作

解決:

  1. Clean Build Folder:Product → Clean Build Folder (⇧⌘K)
  2. 刪除 Derived Data:~/Library/Developer/Xcode/DerivedData
  3. 重新安裝 Pod:
pod deintegrate
pod install

3. 貨幣代碼驗證錯誤

// ❌ 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"
                    )
                }
            }
    }
}
#endif

5. 查看除錯記錄

SDK 會自動將初始化及事件傳送過程的記錄輸出至主控台。 無須另行設定除錯模式,即可直接在 Xcode 主控台中查看。

主控台篩選:

# Xcode Console
filter: AdStage   (或 ADSTAGE)

參考資料

標準參考


FAQ

Q: 自訂事件該如何傳送?
A: 使用 AdStageEvent.Custom(params: [...])

Q: 與 Android SDK 相容嗎?
A: 是的,v3.0 支援與 Android SDK 完全相同的 46 個事件。

Q: 可以在 SwiftUI 中使用嗎?
A: 可以,透過 ObservableObject 模式完整支援。


支援

聯絡方式:


© 2025 NBase. All rights reserved.

目錄