BiddingStack iOS SDK

BiddingStack iOS SDK Integration Guide

iOS header bidding SDK built on OpenRTB 2.5. Supports banner, interstitial, rewarded, native, and outstream video ad formats with Google Ad Manager (GAM) integration. View Changelog.

Requirements

ComponentMinimum Version
Xcode16.0+
iOS Deployment Target15.6+
Swift5.0+

Integration

1. Add Frameworks

Drag the frameworks into your Xcode project:

  • BiddingStackSDK.xcframework: set to Embed & Sign
  • OMSDK_Transfon.xcframework: set to Embed & Sign
  • BiddingStackGAMAdapter.xcframework: set to Do Not Embed (static framework)

2. Add Google Mobile Ads SDK

Add the Google Mobile Ads SDK via Swift Package Manager or CocoaPods:

// SPM: https://github.com/googleads/swift-package-manager-google-mobile-ads
// CocoaPods: pod 'Google-Mobile-Ads-SDK', '>= 13.0'

For video ads, also add the Google IMA SDK:

// SPM: https://github.com/googleads/swift-package-manager-google-interactive-media-ads-ios
// CocoaPods: pod 'GoogleAds-IMA-iOS-SDK'

3. Info.plist Configuration

Add the following keys to your Info.plist:

Google Ad Application Identifier:

<key>GADApplicationIdentifier</key>
<string>ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXX</string>

App Transport Security:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

Tracking Usage Description (for iOS 14+ ATT):

<key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to deliver personalized ads to you.</string>

SKAdNetwork Identifiers:

SKAdNetwork ID reference table
SKAdNetwork IDCompany
cstr6suwn9Google
4fzdc2evr5Aarki
2fnua5tdw4AppLovin
ydx93a7assAdikteev
p78axxw29gByteDance (Pangle)
v72qych5uuAppier
c6k4g5qg8mBeeswax (Freewheel)
hs6bdukanmCriteo
mlmmfzh3r3Appreciate
wzmmz9fp6wSnap Inc.
yclnxrl5pmJampp
t38b2kh725Lifestreet
7ug5zh24huLiftoff (Vungle)
9t245vhmplMoloco
44jx6755aqPersona.ly
4468km3ulzKayzen
tl55sbb4fmPubNative (Verve Group)
2u9pt9hc89Remerge
8s468mfl3yRTB House
klf5c3l5u5Sift
ppxm28t8apSmadex
3rd42ekr43YouAppi
OthersGoogle (Demand Partners)
<key>SKAdNetworkItems</key>
<array>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>22mmun2rn5.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>2fnua5tdw4.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>2u9pt9hc89.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>3qcr597p9d.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>3qy4746246.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>3rd42ekr43.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>3sh42y64q3.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>4468km3ulz.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>44jx6755aq.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>47vhws6wlr.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>4dzt52r2t5.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>4fzdc2evr5.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>578prtvx9j.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>7ug5zh24hu.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>8c4e2ghe7u.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>8s468mfl3y.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>97r2b46745.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>9t245vhmpl.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>a2p9lx4jpn.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>c3frkrj4fj.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>c6k4g5qg8m.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>cp8zw746q7.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>cstr6suwn9.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>e5fvkxwrpn.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>f38h382jlk.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>gta9lk7p23.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>hs6bdukanm.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>k674qkevps.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>kbd757ywx3.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>kbmxgpxpgc.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>klf5c3l5u5.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>ludvb6z3bs.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>mlmmfzh3r3.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>n38lu8286q.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>p78axxw29g.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>ppxm28t8ap.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>s39g8k73mm.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>su67r6k2v3.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>t38b2kh725.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>tl55sbb4fm.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>uw77j35x4d.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>v4nxqhlyqp.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>v72qych5uu.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>v9wttpbfk9.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>vutu7akeur.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>wg4vff78zm.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>wzmmz9fp6w.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>y5ghdn5j9k.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>yclnxrl5pm.skadnetwork</string>
    </dict>
    <dict>
        <key>SKAdNetworkIdentifier</key>
        <string>ydx93a7ass.skadnetwork</string>
    </dict>
</array>

SDK Initialization

Initialize the SDK in AppDelegate.application(_:didFinishLaunchingWithOptions:):

import BiddingStackSDK

BiddingStack.shared.initialize(
    accountId: "your_account_id",
    enableTestMode: false
) {
    print("SDK initialized, config loaded")
}

Initialize Parameters

ParameterTypeDefaultDescription
accountIdStringrequiredYour BiddingStack Account ID
appBundleString?nilApp bundle ID (auto-detected from Info.plist)
appNameString?nilApp name (resolved from config if nil)
appStoreIdString?nilApp Store ID (resolved from config if nil)
timeoutInt2000Bid request timeout in ms
enableTestModeBoolfalseEnable debug logging and test mode
currencyString"USD"Currency code for bid requests
enableCMPBooltrueAuto-read IAB CMP strings from UserDefaults
requestATTPermissionBooltruePrompt for App Tracking Transparency permission during initialization
completion(() -> Void)?nilCalled after remote config is loaded

Note: App bundle, name, store ID, and GAM ad unit paths are resolved automatically from the remote config. You only need to pass the account ID.

Optional Configuration

// User data
BiddingStack.shared.setUserId("user_123")
BiddingStack.shared.setUserGender("M")
BiddingStack.shared.setUserYob(1990)

// Privacy
BiddingStack.shared.setCoppa(false)
BiddingStack.shared.setGDPR("TCF_consent_string", gdprApplies: true)
BiddingStack.shared.setUSPrivacy("1YNN")
BiddingStack.shared.setGPP("gpp_string", sid: [2])

// App metadata (resolved from config if not set)
BiddingStack.shared.setAppStoreUrl("https://apps.apple.com/app/id123456")
BiddingStack.shared.setAppCategories(["IAB1", "IAB2"])

// Supply chain
BiddingStack.shared.setSchainObject("{...}")
BiddingStack.shared.setExternalUserIds(["partner": "uid_abc"])
BiddingStack.shared.setBuyerUid("buyer_123")
BiddingStack.shared.setUserLatLng(lat: 37.7749, lon: -122.4194)

Content URL

Pass the web URL of the content the user is currently viewing so it is included in every bid request as app.content.url. This is important for brand safety and yield: it gives bidders page-level context to verify where their ads run, which increases bid rates and CPMs. Set it before loading ads and update it on each screen change; pass nil to clear.

BiddingStack.shared.setContentUrl("https://www.example.com/article/12345")

Available since SDK 26.7.1.

Ad Formats

// 1. Create ad unit (gamAdUnitId resolved from config; hardcoded value serves as fallback)
let bannerAdUnit = BiddingStackBannerAdUnit(
    configId: "your_config_id",
    gamAdUnitId: "your_gam_ad_unit_id", // optional fallback if config API is unavailable
    adSizes: [BiddingStackAdSize(width: 300, height: 250)]
)

// 2. Create banner view
let bannerView = BiddingStackBannerView(adUnit: bannerAdUnit)

// 3. Set GAM provider
let provider = GAMBannerProvider(
    adUnitID: bannerAdUnit.resolvedGamAdUnitId,
    rootViewController: self,
    adSizes: bannerAdUnit.adSizes
)
bannerView.setAdServerProvider(provider)

// 4. Set callbacks
bannerView.onAdLoaded = { print("Banner loaded") }
bannerView.onAdFailedToLoad = { error in print("Failed: \(error)") }

// 5. Add to view and load
view.addSubview(bannerView)
bannerView.loadAd()

BiddingStackBannerAdUnit Parameters

ParameterTypeDefaultDescription
configIdStringrequiredPlacement config ID
gamAdUnitIdString""GAM ad unit path (resolved from config if empty)
adSizes[BiddingStackAdSize]requiredAd sizes
autoRefreshEnabledBoolfalseAuto-refresh ads
lazyLoadEnabledBoolfalseLazy load when visible
isAutoLoadAdBooltrueAuto-load on creation
formats[BiddingStackFormat][.banner].banner, .video
refreshIntervalTimeInterval30Refresh interval in seconds

Outstream Video

let videoAdUnit = BiddingStackBannerAdUnit(
    configId: "your_video_config_id",
    adSizes: [BiddingStackAdSize(width: 300, height: 250)],
    formats: [.video]
)

let videoView = BiddingStackBannerView(adUnit: videoAdUnit)
videoView.videoRenderer = BiddingStackIMAVideoRenderer()
videoView.onAdLoaded = { print("Video loaded") }
videoView.onAdPlaybackCompleted = { print("Video complete") }
videoView.loadAd()

Interstitial Ad

let interstitialAdUnit = BiddingStackInterstitialAdUnit(
    configId: "your_config_id",
    size: BiddingStackAdSize(width: 320, height: 480)
)

BiddingStack.shared.loadInterstitialAd(adUnit: interstitialAdUnit) { ad, error in
    if let ad = ad {
        ad.fullScreenContentDelegate = self
        ad.present(from: self)
    }
}

App Open Ad

App open ads show at natural app-entry points, such as launch or returning from the background. Preload the ad with BiddingStackInterstitialBanner, then show it when the app becomes active.

class AppOpenAdManager: NSObject, BiddingStackInterstitialDelegate {

    static let shared = AppOpenAdManager()

    private var interstitial: BiddingStackInterstitialBanner?
    private var isLoading = false

    func loadAd() {
        if isLoading || interstitial?.isAdReady() == true { return }
        isLoading = true

        // Google interstitial size: 320x480 phone, 768x1024 tablet
        let interstitialSize = UIDevice.current.userInterfaceIdiom == .pad
            ? BiddingStackAdSize(width: 768, height: 1024)
            : BiddingStackAdSize(width: 320, height: 480)
        let adUnit = BiddingStackBannerAdUnit(
            configId: "your_config_id",
            gamAdUnitId: "your_gam_ad_unit_id",
            adSizes: [interstitialSize, BiddingStackAdSize(width: 300, height: 250)],
            autoRefreshEnabled: false
        )

        let banner = BiddingStackInterstitialBanner(adUnit: adUnit)
        banner.delegate = self
        if let root = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.rootViewController {
            banner.setAdServerProvider(GAMBannerProvider(
                adUnitID: adUnit.resolvedGamAdUnitId,
                rootViewController: root,
                adSizes: adUnit.adSizes
            ))
        }
        interstitial = banner
        banner.loadAd()
    }

    func showAdIfAvailable(from viewController: UIViewController) {
        guard let banner = interstitial, banner.isAdReady() else {
            loadAd()
            return
        }
        banner.show(from: viewController)
    }

    func interstitialDidReceiveAd() { isLoading = false }
    func interstitialDidFailToReceiveAd(error: Error) {
        isLoading = false
        interstitial = nil
    }
    func interstitialWillPresent() {}
    func interstitialDidDismiss() {
        interstitial = nil
        loadAd() // Preload the next one
    }
}

Show it when the app comes to the foreground, e.g. in your scene delegate:

func sceneDidBecomeActive(_ scene: UIScene) {
    if let root = (scene as? UIWindowScene)?.keyWindow?.rootViewController {
        AppOpenAdManager.shared.showAdIfAvailable(from: root)
    }
}

Rewarded Ad

let rewardedAdUnit = BiddingStackRewardedAdUnit(
    configId: "your_config_id",
    size: BiddingStackAdSize(width: 320, height: 480)
)

BiddingStack.shared.loadRewardedAd(adUnit: rewardedAdUnit) { ad, error in
    if let ad = ad {
        ad.present(from: self) {
            print("User earned reward!")
        }
    }
}

Native Ad

let nativeAdUnit = BiddingStackNativeAdUnit(
    configId: "your_native_config_id"
)

BiddingStack.shared.loadNativeAd(adUnit: nativeAdUnit) { ad, error in
    guard let ad = ad else { return }
    print(ad.title)
    print(ad.body)

    // Register view for impression/click tracking
    ad.registerView(
        forInteraction: adView,
        withViewController: self,
        clickableViews: [adView.ctaButton]
    )
}

BiddingStackNativeAd Properties

PropertyTypeDescription
titleString?Ad headline
bodyString?Ad body text
callToActionString?CTA button text
sponsoredByString?Sponsor/advertiser name
iconURLURL?Icon image URL
mainImageURLURL?Main image URL
clickURLURL?Click-through URL

Advanced Features

Bid Preloading

Preload a bid before displaying. Cached bid is valid for 30 seconds.

BiddingStack.shared.preloadBid(for: bannerAdUnit)
// Later, loadAd() uses the cached bid automatically

Custom Targeting

BiddingStack.shared.addTargetingKV(key: "interest", value: "tech")
BiddingStack.shared.addTargetingKV(key: "interest", values: ["tech", "gaming"])
BiddingStack.shared.removeTargetingKey("interest")
BiddingStack.shared.clearTargetingKV()

// Ad unit-level targeting
bannerAdUnit.addContextKeyword(key: "section", value: "homepage")

PMP Deals

let deal = BiddingStackDeal(id: "deal_123", bidfloor: 5.0, bidfloorcur: "USD")
bannerAdUnit.setDeals([deal])

Fetch Demand Directly

For custom ad server integrations:

BiddingStack.shared.fetchDemand(for: adUnit) { result in
    switch result {
    case .success(let targeting):
        print(targeting) // [String: String] bid keywords
    case .failure(let error):
        print(error)
    }
}

GAM Adapter

ClassDescription
GAMBannerProviderAd server provider for GAM banners
BiddingStackGAMUtilsApply targeting keys to AdManagerRequest
BiddingStackGAMLoaderLoad GAM ads with BiddingStack demand
BiddingStackIMAVideoRendererVideo renderer using Google IMA SDK
// Banner
BiddingStackGAMLoader.loadBanner(
    biddingStackAdUnit: bannerAdUnit,
    rootViewController: self
) { result in ... }

// Interstitial
BiddingStackGAMLoader.loadInterstitial(
    biddingStackAdUnit: interstitialAdUnit
) { result in ... }

Privacy & Compliance

The SDK automatically reads IAB consent strings from UserDefaults:

  • GDPR: IABTCF_TCString and IABTCF_gdprApplies
  • CCPA: IABUSPrivacy_String
  • GPP: IABGPP_HDR_GppString and IABGPP_SID
  • ATT: Automatically requests IDFA via AppTrackingTransparency

Debug Logging

BiddingStack.shared.initialize(..., enableTestMode: true)

Log prefixes: BS-Info (lifecycle), BS-Debug (bid data), BS-Error (issues).

Inspect logs on any build

All SDK logs are also emitted via Apple's unified logging under the subsystem com.biddingstack.sdk, so they can be inspected on any device — including production/TestFlight builds — without code changes:

  1. Connect the device to a Mac and open Console.app
  2. Filter by subsystem:com.biddingstack.sdk
  3. Enable Action → Include Debug Messages to see auction details; the verbose category carries full bid request/response JSON

Or from the terminal:

log stream --level debug --predicate 'subsystem == "com.biddingstack.sdk"'

When running from Xcode, add the launch argument -BSDebugLogging YES to also mirror all logs (including verbose) to the console.