Appearance
Opera Ads iOS SDK - Server Bidding (S2S)
Overview
Server-to-Server (S2S) Bidding, also known as Server-Side Bidding or Header Bidding, allows you to run ad auctions on your own mediation server or integrate with third-party mediation platforms that support server-side auctions.
Benefits of Server Bidding
- Centralized Auction Logic: Manage bidding on your server
- Reduced Client Complexity: Less code in your app
- Flexibility: Easy to add/remove ad networks
- Cross-Platform Consistency: Same auction logic for iOS and Android
- Better Analytics: Centralized bid tracking and reporting
How It Works
┌─────────────────────────────────────────────────────────┐
│ 1. App requests bid token from Opera Ads SDK │
│ ┌──────────────┐ │
│ │ getBidToken │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ Bid Token (compressed signals) │
│ │ │
│ ▼ │
│ 2. Send bid token to your auction server │
│ ┌──────────────┐ │
│ │ POST /auction│ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ 3. Server runs auction with Opera and other networks │
│ ┌──────────────┐ │
│ │Opera: $1.50 │ │
│ │Net A: $1.20 │ │
│ │Net B: $1.80 │ ← Winner │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ 4. Server returns bid response (if Opera wins) │
│ ┌──────────────┐ │
│ │Bid Response │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ 5. App loads ad with bid response │
│ ┌──────────────┐ │
│ │ loadRtbAd │ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────────┘Prerequisites
Before implementing server bidding, ensure you have:
Opera Ads SDK Integration
- SDK version 2.1.0 or higher
- Basic integration completed
Server Infrastructure
- Auction server or third-party mediation platform
- Ability to process bid tokens
- API endpoints for auction requests/responses
Understanding of S2S Bidding
- Bid token format and usage
- Auction server communication
- Bid response handling
Implementation Steps
Step 1: Request Bid Token
Request a bid token from the Opera Ads SDK. This token contains encrypted bidding signals.
Swift Example:
swift
import OpAdxSdk
class ServerBiddingManager {
func requestBidToken(for adFormat: AdFormat, completion: @escaping (String?) -> Void) {
// Create bid token request
let request = BidTokenRequest.Builder(adMediation: .custom) // Or .admob, .applovin, .topon
.placementId("s14198264979520")
.adFormat(adFormat)
.adSize(.BANNER_MREC) // Optional: For banner ads
.build()
// Request bid token
OpAdxSDK.getBidToken(request) { callback in
callback.onSuccess { token in
print("✅ Bid token received: \(token.prefix(50))...")
completion(token)
}
callback.onError { error in
print("❌ Bid token request failed: \(error.message)")
completion(nil)
}
}
}
}Objective-C Example:
objective-c
#import <OpAdxSdk/OpAdxSdk.h>
@implementation ServerBiddingManager
- (void)requestBidTokenForAdFormat:(AdFormat)adFormat
completion:(void (^)(NSString * _Nullable))completion {
// Create bid token request
OpAdxBidTokenRequestBuilder *builder = [[OpAdxBidTokenRequestBuilder alloc]
initWithAdMediation:@"custom"];
[builder placementId:@"s14198264979520"];
[builder adFormat:[self adFormatString:adFormat]];
if (adFormat == AdFormatBanner) {
[builder adSize:@"300x250"];
}
OpAdxBidTokenRequest *request = [builder build];
// Request bid token
[OpAdxSDK getBidToken:request callback:^(NSString * _Nullable token, OpAdxAdError * _Nullable error) {
if (token) {
NSLog(@"✅ Bid token received: %@...", [token substringToIndex:MIN(50, token.length)]);
completion(token);
} else {
NSLog(@"❌ Bid token request failed: %@", error.message);
completion(nil);
}
}];
}
@endStep 2: Send Bid Token to Server
Send the bid token to your auction server along with other auction parameters.
Swift:
swift
import Foundation
class AuctionClient {
func sendAuctionRequest(bidToken: String,
adFormat: String,
placementId: String,
completion: @escaping (AuctionResponse?) -> Void) {
// Prepare auction request
let requestBody: [String: Any] = [
"bid_token": bidToken,
"ad_format": adFormat,
"placement_id": placementId,
"device_id": UIDevice.current.identifierForVendor?.uuidString ?? "",
"app_version": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
]
guard let url = URL(string: "https://your-server.com/api/auction"),
let jsonData = try? JSONSerialization.data(withJSONObject: requestBody) else {
completion(nil)
return
}
// Send request
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data,
error == nil,
let auctionResponse = try? JSONDecoder().decode(AuctionResponse.self, from: data) else {
completion(nil)
return
}
completion(auctionResponse)
}.resume()
}
}
struct AuctionResponse: Codable {
let winner: String // "opera" or other network name
let bidResponse: String? // Opera bid response (if Opera won)
let ecpm: Double
let placementId: String
}Objective-C:
objective-c
@implementation AuctionClient
- (void)sendAuctionRequestWithBidToken:(NSString *)bidToken
adFormat:(NSString *)adFormat
placementId:(NSString *)placementId
completion:(void (^)(NSDictionary * _Nullable))completion {
// Prepare auction request
NSDictionary *requestBody = @{
@"bid_token": bidToken,
@"ad_format": adFormat,
@"placement_id": placementId,
@"device_id": [UIDevice currentDevice].identifierForVendor.UUIDString ?: @"",
@"app_version": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] ?: @""
};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:requestBody
options:0
error:&error];
if (error || !jsonData) {
completion(nil);
return;
}
// Send request
NSURL *url = [NSURL URLWithString:@"https://your-server.com/api/auction"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = jsonData;
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error || !data) {
completion(nil);
return;
}
NSDictionary *auctionResponse = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
completion(auctionResponse);
}] resume];
}
@endStep 3: Load Ad with Bid Response
If Opera Ads wins the server auction, load the ad using the bid response.
Swift:
swift
class ServerBiddingManager {
func handleAuctionResponse(_ response: AuctionResponse) {
guard response.winner == "opera",
let bidResponse = response.bidResponse else {
print("Opera Ads did not win - show other network's ad")
showOtherNetworkAd()
return
}
print("✅ Opera Ads won with eCPM: $\(response.ecpm)")
loadOperaAd(with: bidResponse, placementId: response.placementId)
}
func loadOperaAd(with bidResponse: String, placementId: String) {
let interstitialAd = OpAdxInterstitialAd(
placementId: placementId,
auctionType: .serverBidding // ← Important: Use serverBidding
)
let loadListener = OpAdxInterstitialAdLoadListenerImp(
onAdLoaded: { ad in
print("✅ Opera ad loaded from bid response")
self.showOperaAd(interstitialAd)
},
onAdFailedToLoad: { error in
print("❌ Opera ad failed to load: \(error.message)")
}
)
// Load ad with bid response
interstitialAd.load(
bidResponse: bidResponse,
listener: loadListener
)
}
func showOperaAd(_ ad: OpAdxInterstitialAd) {
let interactionListener = OpAdxInterstitialAdInteractionListenerImp(
onAdClicked: {
print("Ad clicked")
},
onAdDisplayed: {
print("Ad displayed")
},
onAdDismissed: {
print("Ad dismissed")
},
onAdFailedToShow: { error in
print("Ad failed to show: \(error.message)")
}
)
ad.show(on: self.viewController, listener: interactionListener)
}
}Objective-C:
objective-c
@implementation ServerBiddingManager
- (void)handleAuctionResponse:(NSDictionary *)response {
NSString *winner = response[@"winner"];
NSString *bidResponse = response[@"bid_response"];
if (![winner isEqualToString:@"opera"] || !bidResponse) {
NSLog(@"Opera Ads did not win - show other network's ad");
[self showOtherNetworkAd];
return;
}
double ecpm = [response[@"ecpm"] doubleValue];
NSLog(@"✅ Opera Ads won with eCPM: $%.4f", ecpm);
[self loadOperaAdWithBidResponse:bidResponse placementId:response[@"placement_id"]];
}
- (void)loadOperaAdWithBidResponse:(NSString *)bidResponse placementId:(NSString *)placementId {
OpAdxInterstitialAdBridge *interstitialAd = [[OpAdxInterstitialAdBridge alloc]
initWithPlacementId:placementId
auctionType:AdAuctionTypeServerBidding]; // ← Important
interstitialAd.delegate = self;
// Load ad with bid response
[interstitialAd loadWithBidResponse:bidResponse];
}
#pragma mark - OpAdxInterstitialAdDelegate
- (void)interstitialAdDidLoad:(OpAdxInterstitialAdBridge *)interstitialAd {
NSLog(@"✅ Opera ad loaded from bid response");
[self showOperaAd:interstitialAd];
}
- (void)interstitialAd:(OpAdxInterstitialAdBridge *)interstitialAd
didFailWithError:(OpAdxAdError *)error {
NSLog(@"❌ Opera ad failed to load: %@", error.message);
}
@endAd Format Implementations
Banner Ads
Swift:
swift
func loadBannerWithServerBidding(bidResponse: String) {
let bannerAd = OpAdxBannerAdView()
bannerAd.setPlacementId("s14170965187264")
bannerAd.setAdSize(.BANNER_MREC)
let listener = OpAdxBannerAdListenerImp(
onAdLoaded: { [weak self] bannerInfo in
print("Banner loaded from bid response")
self?.showBanner(bannerAd)
},
onAdFailedToLoad: { error in
print("Banner failed: \(error.message)")
},
onAdImpression: {
print("Banner impression")
},
onAdClicked: {
print("Banner clicked")
}
)
// Load banner with bid response
bannerAd.loadRtbAd(bidResponse: bidResponse, listener: listener)
}
func showBanner(_ banner: OpAdxBannerAdView) {
view.addSubview(banner)
banner.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
banner.centerXAnchor.constraint(equalTo: view.centerXAnchor),
banner.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
banner.widthAnchor.constraint(equalToConstant: 300),
banner.heightAnchor.constraint(equalToConstant: 250)
])
}Objective-C:
objective-c
- (void)loadBannerWithServerBidding:(NSString *)bidResponse {
OpAdxBannerAdBridge *bannerAd = [[OpAdxBannerAdBridge alloc]
initWithPlacementId:@"s14170965187264"
adSize:OpAdxAdSize.BANNER_MREC];
bannerAd.delegate = self;
// Load banner with bid response
[bannerAd loadRtbAdWithBidResponse:bidResponse];
}
- (void)bannerAdDidLoad:(OpAdxBannerAdBridge *)bannerAd {
NSLog(@"Banner loaded from bid response");
[self showBanner:bannerAd];
}Native Ads
Swift:
swift
func loadNativeWithServerBidding(bidResponse: String) {
let nativeAd = OpAdxNativeAd(
placementId: "s14198263063424",
auctionType: .serverBidding
)
let listener = OpAdxNativeAdListenerImp(
onAdLoaded: { [weak self] ad in
guard let self = self, let nativeAd = ad as? OpAdxNativeAd else { return }
print("Native ad loaded from bid response")
self.renderNativeAd(nativeAd)
},
onAdFailedToLoad: { error in
print("Native ad failed: \(error.message)")
},
onAdImpression: {
print("Native ad impression")
},
onAdClicked: {
print("Native ad clicked")
}
)
// Load native ad with bid response
nativeAd.loadRtb(bidResponse: bidResponse, listener: listener)
}
func renderNativeAd(_ ad: OpAdxNativeAd) {
let nativeAdView = OpAdxNativeAdView(frame: CGRect(x: 0, y: 0, width: 300, height: 400))
nativeAdView.configure(with: ad)
let rootView = OpAdxNativeAdRootView(root: nativeAdView)
ad.registerInteractionViews(
container: rootView,
interactionViews: nativeAdView.interactionViews,
adChoicePosition: .topRight
)
adContainer.addSubview(nativeAdView)
}Rewarded Ads
Swift:
swift
func loadRewardedWithServerBidding(bidResponse: String) {
let rewardedAd = OpAdxRewardedAd(
placementId: "s14198592226752",
auctionType: .serverBidding
)
let loadListener = OpAdxRewardedAdLoadListenerImp(
onAdLoaded: { [weak self] ad in
print("Rewarded ad loaded from bid response")
self?.showRewardedAd(rewardedAd)
},
onAdFailedToLoad: { error in
print("Rewarded ad failed: \(error.message)")
}
)
// Load rewarded ad with bid response
rewardedAd.loadRtb(bidResponse: bidResponse, listener: loadListener)
}App Open Ads
Swift:
swift
func loadAppOpenWithServerBidding(bidResponse: String) {
let appOpenAd = OpAdxAppOpenAd(
placementId: "s14496438551808",
auctionType: .serverBidding
)
let loadListener = OpAdxAppOpenAdLoadListenerImp(
onAdLoaded: { [weak self] ad in
print("App open ad loaded from bid response")
self?.showAppOpenAd(appOpenAd)
},
onAdFailedToLoad: { error in
print("App open ad failed: \(error.message)")
}
)
// Load app open ad with bid response
appOpenAd.loadRtb(bidResponse: bidResponse, listener: loadListener)
}Bid Token Request Configuration
Ad Mediation Types
Specify your mediation platform when requesting bid tokens:
Swift:
swift
// For custom mediation server
let request = BidTokenRequest.Builder(adMediation: .custom)
.placementId("s14198264979520")
.adFormat(.interstitial)
.build()
// For AdMob mediation
let request = BidTokenRequest.Builder(adMediation: .admob)
.placementId("s14198264979520")
.adFormat(.interstitial)
.build()
// For AppLovin MAX mediation
let request = BidTokenRequest.Builder(adMediation: .applovin)
.placementId("s14198264979520")
.adFormat(.interstitial)
.build()
// For TopOn mediation
let request = BidTokenRequest.Builder(adMediation: .topon)
.placementId("s14198264979520")
.adFormat(.interstitial)
.build()Objective-C:
objective-c
// For custom mediation server
OpAdxBidTokenRequestBuilder *builder = [[OpAdxBidTokenRequestBuilder alloc]
initWithAdMediation:@"custom"];
// For AdMob mediation
OpAdxBidTokenRequestBuilder *builder = [[OpAdxBidTokenRequestBuilder alloc]
initWithAdMediation:@"admob"];
// For AppLovin MAX mediation
OpAdxBidTokenRequestBuilder *builder = [[OpAdxBidTokenRequestBuilder alloc]
initWithAdMediation:@"applovin"];
// For TopOn mediation
OpAdxBidTokenRequestBuilder *builder = [[OpAdxBidTokenRequestBuilder alloc]
initWithAdMediation:@"topon"];Ad Formats
Specify the ad format in your bid token request:
Swift:
swift
// Banner
let request = BidTokenRequest.Builder(adMediation: .custom)
.placementId("s14170965187264")
.adFormat(.banner)
.adSize(.BANNER_MREC) // Optional: Specify banner size
.build()
// Interstitial
let request = BidTokenRequest.Builder(adMediation: .custom)
.placementId("s14198264979520")
.adFormat(.interstitial)
.build()
// Rewarded
let request = BidTokenRequest.Builder(adMediation: .custom)
.placementId("s14198592226752")
.adFormat(.rewarded)
.build()
// Native
let request = BidTokenRequest.Builder(adMediation: .custom)
.placementId("s14198263063424")
.adFormat(.native)
.build()
// App Open
let request = BidTokenRequest.Builder(adMediation: .custom)
.placementId("s14496438551808")
.adFormat(.appOpen)
.build()Banner Sizes
For banner ads, specify the ad size:
Swift:
swift
// Medium Rectangle (300x250)
.adSize(.BANNER_MREC)
// Standard Banner (320x50)
.adSize(.BANNER)
// Large Banner (320x100)
.adSize(.BANNER_LARGE)
// Leaderboard (728x90)
.adSize(.BANNER_LEADERBOARD)Complete Workflow Example
Here's a complete implementation of server bidding:
Swift:
swift
import OpAdxSdk
class CompleteServerBiddingExample {
// MARK: - Step 1: Request Bid Token
func startServerBidding(for adFormat: AdFormat, placementId: String) {
print("Step 1: Requesting bid token...")
let request = BidTokenRequest.Builder(adMediation: .custom)
.placementId(placementId)
.adFormat(adFormat)
.build()
OpAdxSDK.getBidToken(request) { callback in
callback.onSuccess { [weak self] token in
print("✅ Bid token received")
self?.sendToAuctionServer(
bidToken: token,
adFormat: adFormat,
placementId: placementId
)
}
callback.onError { error in
print("❌ Bid token failed: \(error.message)")
}
}
}
// MARK: - Step 2: Send to Auction Server
func sendToAuctionServer(bidToken: String, adFormat: AdFormat, placementId: String) {
print("Step 2: Sending to auction server...")
let requestBody: [String: Any] = [
"bid_token": bidToken,
"ad_format": adFormat.rawValue,
"placement_id": placementId
]
guard let url = URL(string: "https://your-server.com/api/auction"),
let jsonData = try? JSONSerialization.data(withJSONObject: requestBody) else {
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
guard let data = data,
error == nil,
let auctionResponse = try? JSONDecoder().decode(AuctionResponse.self, from: data) else {
print("❌ Auction request failed")
return
}
DispatchQueue.main.async {
self?.handleAuctionResponse(auctionResponse, adFormat: adFormat)
}
}.resume()
}
// MARK: - Step 3: Handle Auction Response
func handleAuctionResponse(_ response: AuctionResponse, adFormat: AdFormat) {
print("Step 3: Handling auction response...")
print("Winner: \(response.winner) with eCPM: $\(response.ecpm)")
guard response.winner == "opera",
let bidResponse = response.bidResponse else {
print("Opera Ads did not win - showing other network")
return
}
loadOperaAd(
bidResponse: bidResponse,
adFormat: adFormat,
placementId: response.placementId
)
}
// MARK: - Step 4: Load Ad with Bid Response
func loadOperaAd(bidResponse: String, adFormat: AdFormat, placementId: String) {
print("Step 4: Loading Opera ad with bid response...")
switch adFormat {
case .banner:
loadBanner(bidResponse: bidResponse, placementId: placementId)
case .interstitial:
loadInterstitial(bidResponse: bidResponse, placementId: placementId)
case .rewarded:
loadRewarded(bidResponse: bidResponse, placementId: placementId)
case .native:
loadNative(bidResponse: bidResponse, placementId: placementId)
case .appOpen:
loadAppOpen(bidResponse: bidResponse, placementId: placementId)
}
}
func loadInterstitial(bidResponse: String, placementId: String) {
let ad = OpAdxInterstitialAd(
placementId: placementId,
auctionType: .serverBidding
)
let listener = OpAdxInterstitialAdLoadListenerImp(
onAdLoaded: { [weak self] _ in
print("✅ Interstitial loaded - ready to show")
self?.showInterstitial(ad)
},
onAdFailedToLoad: { error in
print("❌ Interstitial failed: \(error.message)")
}
)
ad.load(bidResponse: bidResponse, listener: listener)
}
func showInterstitial(_ ad: OpAdxInterstitialAd) {
let listener = OpAdxInterstitialAdInteractionListenerImp(
onAdClicked: {
print("Interstitial clicked")
},
onAdDisplayed: {
print("Interstitial displayed")
},
onAdDismissed: {
print("Interstitial dismissed")
},
onAdFailedToShow: { error in
print("Failed to show: \(error.message)")
}
)
guard let viewController = UIApplication.shared.windows.first?.rootViewController else {
return
}
ad.show(on: viewController, listener: listener)
}
}
// Response model
struct AuctionResponse: Codable {
let winner: String
let bidResponse: String?
let ecpm: Double
let placementId: String
}Server-Side Implementation Guide
Auction Server Requirements
Your auction server should:
Receive Bid Token
jsonPOST /api/auction { "bid_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "ad_format": "interstitial", "placement_id": "s14198264979520", "device_id": "ABC123...", "app_version": "1.0.0" }Forward to Opera Ads Auction Endpoint
POST https://auction.opera.com/v1/bid Headers: Authorization: Bearer YOUR_API_KEY Content-Type: application/json Body: { "bid_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "floor_price": 0.50 }Receive Opera Bid Response
json{ "status": "success", "ecpm": 1.50, "bid_response": "base64_encoded_bid_response_data...", "currency": "USD" }Run Auction with Other Networks
javascriptconst bids = [ { network: "opera", ecpm: 1.50, bidResponse: "..." }, { network: "network_a", ecpm: 1.20, bidResponse: "..." }, { network: "network_b", ecpm: 1.80, bidResponse: "..." } ]; const winner = bids.sort((a, b) => b.ecpm - a.ecpm)[0];Return Winner to App
json{ "winner": "opera", "bid_response": "base64_encoded_bid_response_data...", "ecpm": 1.50, "placement_id": "s14198264979520" }
Sample Server Code (Node.js)
javascript
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
app.post('/api/auction', async (req, res) => {
const { bid_token, ad_format, placement_id } = req.body;
try {
// Step 1: Get Opera Ads bid
const operaBid = await axios.post('https://auction.opera.com/v1/bid', {
bid_token: bid_token,
floor_price: 0.50
}, {
headers: {
'Authorization': `Bearer ${process.env.OPERA_API_KEY}`,
'Content-Type': 'application/json'
}
});
// Step 2: Get bids from other networks (parallel)
const [networkA, networkB] = await Promise.all([
getNetworkABid(ad_format, placement_id),
getNetworkBBid(ad_format, placement_id)
]);
// Step 3: Run auction
const bids = [
{ network: 'opera', ecpm: operaBid.data.ecpm, bidResponse: operaBid.data.bid_response },
{ network: 'network_a', ecpm: networkA.ecpm, bidResponse: networkA.bidResponse },
{ network: 'network_b', ecpm: networkB.ecpm, bidResponse: networkB.bidResponse }
];
const winner = bids.sort((a, b) => b.ecpm - a.ecpm)[0];
// Step 4: Return winner
res.json({
winner: winner.network,
bid_response: winner.bidResponse,
ecpm: winner.ecpm,
placement_id: placement_id
});
} catch (error) {
console.error('Auction error:', error);
res.status(500).json({ error: 'Auction failed' });
}
});
app.listen(3000, () => console.log('Auction server running on port 3000'));Best Practices
1. Caching Bid Tokens
Don't cache bid tokens - request fresh tokens for each auction:
swift
// ✅ Good: Fresh token for each auction
func loadAd() {
requestBidToken { token in
self.sendToServer(token)
}
}
// ❌ Bad: Reusing old tokens
var cachedToken: String?
func loadAd() {
if let token = cachedToken {
sendToServer(token) // May be expired!
}
}2. Timeout Handling
Set reasonable timeouts for auction requests:
swift
var request = URLRequest(url: url)
request.timeoutInterval = 5.0 // 5 seconds
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error as NSError?, error.code == NSURLErrorTimedOut {
print("Auction timeout - using fallback")
self.showFallbackAd()
}
}.resume()3. Error Handling
Always handle auction failures gracefully:
swift
func handleAuctionResponse(_ response: AuctionResponse?) {
guard let response = response else {
// Auction failed - show fallback
showFallbackAd()
return
}
if response.winner == "opera" {
loadOperaAd(bidResponse: response.bidResponse)
} else {
showOtherNetworkAd()
}
}4. Thread Safety
Always update UI on main thread:
swift
URLSession.shared.dataTask(with: request) { data, response, error in
// Parse response on background thread
let auctionResponse = parseResponse(data)
// Update UI on main thread
DispatchQueue.main.async {
self.handleAuctionResponse(auctionResponse)
}
}.resume()Troubleshooting
Issue: Bid Token Request Fails
Cause: SDK not initialized or invalid parameters
Solution:
swift
// Ensure SDK is initialized first
OpAdxSDK.initialize(withConfig: config) {
// Now request bid token
self.requestBidToken()
}
// Verify request parameters
let request = BidTokenRequest.Builder(adMediation: .custom)
.placementId("s14198264979520") // Valid placement ID
.adFormat(.interstitial) // Valid ad format
.build()Issue: Bid Response Loading Fails
Cause: Invalid bid response or expired token
Solution:
- Don't cache bid responses
- Request fresh bid token for each auction
- Check bid response format from server
- Verify placement ID matches
Issue: Low Fill Rate
Cause: Floor price too high or limited demand
Solution:
- Lower floor price on server
- Check bid token includes correct signals
- Verify placement ID is active
- Test with different geos
Support
Document Version: 2.9.0 Last Updated: 2026-03-24 SDK Version: 2.9.0
