OMNIBOX iOS SDK Swift
Интеграция баннерной рекламы в iOS-приложения с помощью Prebid Mobile SDK 3.3.1 и SwiftUI.
Архитектура
┌─────────────────────────────────────────────────────┐
│ ContentView (SwiftUI) │
│ └─ IABoxBanner (UIViewRepresentable) │
│ └─ IABoxBannerContainerView (UIView) │
│ ├─ BannerView (Prebid) — bid request │
│ └─ WKWebView — rendering HTML creative │
└─────────────────────────────────────────────────────┘
PrebidMobile.BannerViewотправляет OpenRTB bid-запрос на Prebid Server- Сервер возвращает bid-ответ с HTML/JS креативом (adm)
WKWebViewрендерит полученный adm
Почему WKWebView вместо встроенного рендерера PrebidMobile?
Сервер OMNIBOX возвращает HTML/JS креатив со своим SDK (adboxsdk.new.js), содержащим трекинг событий на JavaScript, а не на уровне протокола OpenRTB. Встроенный рендерер PrebidMobile ожидает трекеры событий в формате OpenRTB (burl,eventtrackers), поэтому не может отрендерить такой креатив. WKWebView рендерит HTML как есть.
Требования
- iOS 14.0+
- Xcode 15.0+
- Swift 5.9+
Установка
1. Добавление Prebid Mobile SDK
PrebidMobile — сторонний open-source пакет от Prebid.org, размещённый на GitHub.
В Xcode: File → Add Package Dependencies, введите URL:
https://github.com/prebid/prebid-mobile-ios
Выберите только:
- ✅
PrebidMobile
Не добавляйте PrebidMobileAdMobAdapters, PrebidMobileGAMEventHandlers, PrebidMobileMAXAdapters — они не нужны для standalone-режима и подтягивают Google Mobile Ads SDK.
2. Настройка параметров
В IABoxBannerView.swift задайте параметры аккаунта:
let bannerConfigID = "test_banner" // Config ID placement
let accountID = "com.iabox.ios-sdk-test-1" // Account ID
let serverURL = "https://ia.box/ads/prebid" // Prebid Server URL
3. App Tracking Transparency (IDFA)
Чтобы передавать реальный deviceId (IDFA) на рекламный сервер, необходимо получить разрешение пользователя через App Tracking Transparency.
Без разрешения сервер получает 00000000-0000-0000-0000-000000000000 вместо реального идентификатора.
ATT и инициализация SDK — независимые процессы. Вы можете инициализировать SDK в любое время.
Шаг 1. Добавление описания в Info.plist
В Build Settings проекта (или напрямую в Info.plist) добавьте ключ NSUserTrackingUsageDescription:
<key>NSUserTrackingUsageDescription</key>
<string>This identifier is used to deliver personalized ads.</string>
Или через Xcode Build Settings:
INFOPLIST_KEY_NSUserTrackingUsageDescription = "This identifier is used to deliver personalized ads."
Шаг 2. Запрос разрешения в коде
ATT-запрос должен вызываться после появления UI — иначе системный диалог не отобразится.
Вариант A — scenePhase (рекомендуется для SwiftUI)
Запрос при переходе сцены в .active. Гарантирует, что окно приложения уже отображено:
import AppTrackingTransparency
import AdSupport
@main
struct MyApp: App {
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { phase in
if phase == .active {
if ATTrackingManager.trackingAuthorizationStatus == .notDetermined {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
ATTrackingManager.requestTrackingAuthorization { _ in }
}
}
}
}
}
}
Важно: applicationDidBecomeActive не вызывается в SwiftUI-приложениях на основе сцен. Используйте scenePhase.
Вариант B — UIKit AppDelegate
Для UIKit-приложений (без scene lifecycle):
class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidBecomeActive(_ application: UIApplication) {
if ATTrackingManager.trackingAuthorizationStatus == .notDetermined {
ATTrackingManager.requestTrackingAuthorization { _ in }
}
}
}
Поведение статуса ATT
| Статус ATT | IDFA | Результат |
|---|---|---|
.authorized | Реальный UUID | Персонализированная реклама |
.denied | 00000000-... | Контекстная реклама |
.restricted | 00000000-... | Контекстная реклама |
.notDetermined | 00000000-... | Нужно запросить разрешение |
Тестирование на симуляторе: чтобы снова показать ATT-диалог, удалите приложение с симулятора и запустите заново.
4. Сборка и запуск
Соберите и запустите проект: Cmd+R
Структура проекта
ios-sdk-test/
├── ios-sdk-test/
│ ├── ios_sdk_testApp.swift # Entry point (SwiftUI App)
│ ├── ContentView.swift # UI with banner load buttons
│ ├── IABoxBannerView.swift # Prebid + WKWebView wrapper
│ └── Assets.xcassets/ # Resources
├── ios-sdk-test.xcodeproj/ # Xcode project
└── README.md
Как работает интеграция
1. Инициализация SDK
Prebid.shared.prebidServerAccountId = accountID
Prebid.shared.timeoutMillis = 10000
Prebid.shared.shareGeoLocation = true
try? Prebid.initializeSDK(serverURL: serverURL) { status, error in
// Handle initialization result
}
initializeSDK проверяет {serverURL}/status. Если этот endpoint не настроен (404) — это не критично, bid-запросы всё равно будут отправляться на /openrtb2/auction.
2. Bid-запрос
let bannerView = BannerView(
frame: CGRect(origin: .zero, size: adSize),
configID: configID,
adSize: adSize
)
bannerView.delegate = self
bannerView.loadAd() // → POST {serverURL}/openrtb2/auction
3. Рендеринг креатива в WKWebView
Когда bid-ответ получен, adm (HTML-креатив) извлекается и загружается в WKWebView:
func bannerView(_ bannerView: BannerView, didReceiveAdWithAdSize adSize: CGSize) {
if let adm = bannerView.lastBidResponse?.winningBid?.adm {
renderAdm(adm) // Loads HTML into WKWebView
}
}
4. SwiftUI-обёртка
struct IABoxBanner: UIViewRepresentable {
@ObservedObject var viewModel: BannerViewModel
let configID: String
let adSize: CGSize
var onStatusUpdate: ((String) -> Void)?
func makeUIView(context: Context) -> IABoxBannerContainerView { ... }
func updateUIView(_ uiView: IABoxBannerContainerView, context: Context) { ... }
}
5. Обработка кликов
Клики по рекламе обрабатываются через WKNavigationDelegate и WKUIDelegate. Ссылки <a href> перехватываются в decidePolicyFor и открываются в Safari. window.open() из JS перехватывается в createWebViewWith и открывается в Safari.
Формат ответа сервера (adm)
Сервер OMNIBOX возвращает HTML/JS креатив. Трекинг событий (impression, click, viewability) выполняется внутри adboxsdk.new.js на стороне WebView.
<script src="https://dcdn.adbox.ru/adboxsdk.new.js" async></script>
<script>
window.AdBox.push({
"format": "simple",
"srcType": "url",
"src": "https://ia.box/ads/markup?bid=...&imp=...",
"target": "#slot-...",
"size": {"w": 300, "h": 250},
"track": {
"impression": ["https://ia.box/track/impression?..."],
"click": ["https://ia.box/track/click?..."],
"view": ["https://ia.box/track/viewable?..."],
"load": ["https://ia.box/track/loaded?..."],
"error": ["https://ia.box/track/error?..."]
}
})
</script>
<div id="slot-..."></div>
Устранение неполадок
No such module 'PrebidMobile'
Проверьте, что Swift Package добавлен: Project → Package Dependencies. Убедитесь, что добавлен только PrebidMobile, без дополнительных адаптеров.
SDK status check failed
Endpoint /status не настроен на сервере — это допустимо. Bid-запросы будут работать через /openrtb2/auction.
Creative model must be provided with event tracker
Лог от встроенного рендерера PrebidMobile — можно игнорировать. Креатив рендерится через WKWebView, минуя встроенный рендерер.
Google Mobile Ads SDK initialized without an application ID
Были добавлены лишние пакеты (PrebidMobileAdMobAdapters и т.д.). Удалите их, оставьте только PrebidMobile.
deviceId = 00000000-0000-0000-0000-000000000000
Пользователь не дал разрешение на трекинг (ATT). Проверьте NSUserTrackingUsageDescription в Info.plist. Убедитесь, что ATTrackingManager.requestTrackingAuthorization вызывается после появления UI. На симуляторе: удалите приложение и запустите заново.
Banner not displayed
Проверьте логи Xcode Console на сообщения loadBanner(), Bid received, Rendering adm. Убедитесь, что configID существует на сервере. Проверьте доступность сервера: curl https://ia.box/ads/prebid/openrtb2/auction