iaBox iOS SDK

Swift

Banner ad integration in iOS apps using Prebid Mobile SDK 3.3.1 and SwiftUI.

Architecture

┌─────────────────────────────────────────────────────┐
│  ContentView (SwiftUI)                              │
│    └─ IABoxBanner (UIViewRepresentable)             │
│         └─ IABoxBannerContainerView (UIView)        │
│              ├─ BannerView (Prebid) — bid request   │
│              └─ WKWebView — rendering HTML creative │
└─────────────────────────────────────────────────────┘
  1. PrebidMobile.BannerView sends an OpenRTB bid request to Prebid Server
  2. Server returns bid response with HTML/JS creative (adm)
  3. WKWebView renders the received adm
Why WKWebView instead of PrebidMobile built-in renderer?
The iaBox server returns HTML/JS creative with its own SDK (adboxsdk.new.js) containing event tracking in JavaScript, not at the OpenRTB protocol level. The built-in PrebidMobile renderer expects event trackers in OpenRTB format (burl, eventtrackers), so it cannot render such creative. WKWebView renders HTML as-is.

Requirements

  • iOS 14.0+
  • Xcode 15.0+
  • Swift 5.9+

Installation

1. Add Prebid Mobile SDK

PrebidMobile is a third-party open-source package from Prebid.org, hosted on GitHub.

In Xcode: File → Add Package Dependencies, enter URL:

https://github.com/prebid/prebid-mobile-ios

Select only:

  • PrebidMobile
Do not add PrebidMobileAdMobAdapters, PrebidMobileGAMEventHandlers, PrebidMobileMAXAdapters — they are not needed for standalone mode and pull in Google Mobile Ads SDK.

2. Configure Parameters

In IABoxBannerView.swift set your account parameters:

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)

To pass the real deviceId (IDFA) to the ad server, you must obtain user permission via App Tracking Transparency.

Without permission the server receives 00000000-0000-0000-0000-000000000000 instead of the real identifier.

ATT and SDK initialization are independent processes. You can initialize the SDK at any time.

Step 1. Add description to Info.plist

In Build Settings of the project (or directly in Info.plist) add key NSUserTrackingUsageDescription:

<key>NSUserTrackingUsageDescription</key>
<string>This identifier is used to deliver personalized ads.</string>

Or via Xcode Build Settings:

INFOPLIST_KEY_NSUserTrackingUsageDescription = "This identifier is used to deliver personalized ads."

Step 2. Request permission in code

ATT request must be called after UI appears — otherwise the system dialog will not display.

Option A — scenePhase (recommended for SwiftUI)

Request when scene transitions to .active. Guarantees the app window is already displayed:

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 }
                    }
                }
            }
        }
    }
}
Important: applicationDidBecomeActive is not called in SwiftUI scene-based apps. Use scenePhase.
Option B — UIKit AppDelegate

For UIKit apps (without scene lifecycle):

class AppDelegate: UIResponder, UIApplicationDelegate {
    func applicationDidBecomeActive(_ application: UIApplication) {
        if ATTrackingManager.trackingAuthorizationStatus == .notDetermined {
            ATTrackingManager.requestTrackingAuthorization { _ in }
        }
    }
}

ATT status behavior

ATT statusIDFAResult
.authorizedReal UUIDPersonalized ads
.denied00000000-...Contextual ads
.restricted00000000-...Contextual ads
.notDetermined00000000-...Need to request permission
Testing on simulator: to re-show ATT dialog, delete the app from simulator and run again.

4. Build & Run

Build and run the project: Cmd+R

Project Structure

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

How Integration Works

1. SDK Initialization

Prebid.shared.prebidServerAccountId = accountID
Prebid.shared.timeoutMillis = 10000
Prebid.shared.shareGeoLocation = true

try? Prebid.initializeSDK(serverURL: serverURL) { status, error in
    // Handle initialization result
}
initializeSDK checks {serverURL}/status. If this endpoint is not configured (404) — this is not critical, bid requests will still be sent to /openrtb2/auction.

2. Bid Request

let bannerView = BannerView(
    frame: CGRect(origin: .zero, size: adSize),
    configID: configID,
    adSize: adSize
)
bannerView.delegate = self
bannerView.loadAd()  // → POST {serverURL}/openrtb2/auction

3. Rendering Creative in WKWebView

When bid response is received, adm (HTML creative) is extracted and loaded into WKWebView:

func bannerView(_ bannerView: BannerView, didReceiveAdWithAdSize adSize: CGSize) {
    if let adm = bannerView.lastBidResponse?.winningBid?.adm {
        renderAdm(adm)  // Loads HTML into WKWebView
    }
}

4. SwiftUI Wrapper

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. Click Handling

Ad clicks are handled via WKNavigationDelegate and WKUIDelegate. <a href> links are intercepted in decidePolicyFor and opened in Safari. window.open() from JS is intercepted in createWebViewWith and opened in Safari.

Server Response Format (adm)

The iaBox server returns an HTML/JS creative. Event tracking (impression, click, viewability) is performed inside adboxsdk.new.js on the WebView side.

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

Troubleshooting

No such module 'PrebidMobile'

Check that Swift Package is added: Project → Package Dependencies. Make sure only PrebidMobile is added, without additional adapters.

SDK status check failed

The /status endpoint is not configured on the server — this is acceptable. Bid requests will work via /openrtb2/auction.

Creative model must be provided with event tracker

Log from built-in PrebidMobile renderer — can be ignored. Creative is rendered via WKWebView, bypassing the built-in renderer.

Google Mobile Ads SDK initialized without an application ID

Extra packages were added (PrebidMobileAdMobAdapters, etc.). Remove them, keep only PrebidMobile.

deviceId = 00000000-0000-0000-0000-000000000000

User did not grant tracking permission (ATT). Check NSUserTrackingUsageDescription in Info.plist. Ensure ATTrackingManager.requestTrackingAuthorization is called after UI appears. On simulator: delete app and rerun.

Banner not displayed

Check Xcode Console logs for loadBanner(), Bid received, Rendering adm messages. Make sure configID exists on the server. Check server availability: curl https://ia.box/ads/prebid/openrtb2/auction