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"
    )
)

🛒 E コマース(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. E コマースの全体フロー

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 と 100% 同じ 46 個のイベントに対応しています。

Q: SwiftUI で使用できますか?
A: はい。ObservableObject パターンで完全に対応しています。


サポート

連絡先:


© 2025 NBase. All rights reserved.

目次