行動 SDK應用程式內事件
iOS
AdStage 應用程式內事件串接指南(iOS)
目錄
概述
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 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 是 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 自動完成無法運作
解決:
- 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. 查看除錯記錄
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 模式完整支援。
支援
聯絡方式:
- 電子郵件:support@nbase.io
© 2025 NBase. All rights reserved.

