Unified Bidding | iOS Integration
Unified Bidding Integration Guide for Publishers with Smaato or GAM as a Primary Ad Server
(iOS NextGen SDK version 21.7.0)
Contact Sales Engineering
Introduction
Smaato’s in-app header bidding solution, Unified Bidding, is a holistic bidding model that uses real-time pricing competition to provide publishers with maximum competition and control over their inventory.
Take advantage of the our in-app header bidding solution by integrating with the Smaato NextGen SDK today.
Your Advantage
We are observing more and more demand partners moving away from multiple integrations. Following the TradeDesk’s public announcement that they will only allow one integration per supply source, many other demand partners are now following suit.
Currently, we have seen up to 230% higher revenue when comparing Smaato Unified Bidding versus TAM integrations. We have also observed around a 300% increase in revenue via the direct connection due to the additional revenue accumulated.
GAM Setup Process
For each price point (of each line item you set up in SPX), you will need to create one line item in GAM.
You will also want to add a new Dynamic Key (with no value defined) called “smaub”, on Inventory >> Key-Values. For more information, see here
GAM Dashboard Setup
1. Go to the GAM Dashboard
2. Click on “Create Mediation Group” button
3. Select Ad format as per your needs. Possible options are Banner, Interstitial, Native, and Rewarded.
4. Set the desired parameters in the next tab.
5. On “Add Ad Unit” option under “Ad Unit“, select the ad unit that needs to be targeted with mediation.
6. Under “Ad sources“, click on “ADD CUSTOM EVENT“
-
- Provide Label and your desired eCPM
- Under “Configure ad units“, provide the class name details:
-
-
- For Banner Ads:
SMAAdMobSmaatoBannerAdapter
- For Interstitial Ads:
SMAAdMobSmaatoInterstitialAdapter
- For Rewarded Video Ads:
SMAAdMobSmaatoRewardedVideoAdapter
- For Native Ads:
SMAAdMobSmaatoNativeAdapter
- For Banner Ads:
-
-
- Pass parameter for the SPX Adspace ID:
adSpaceId=<YOUR_SPX_ADSPACE_ID>
- Pass parameter for the SPX Adspace ID:
6. Next, set the eCPM of the price that you want to target, for example, $0.10.
7. Don’t add day/time or day/part targeting.
8. Don’t add frequency capping.
In the Custom Targeting >> Key-Values targeting section you need to select “smaub” as your targeted key, and enter a value, for each line item that you have created. Please make sure that the value is “smaato_cpm:” and the price rate of the Line Item and repeat this process for each Line Item.
For example:
- For $0.10 enter the value smaato_cpm:0.10
- For $1.30 enter the value smaato_cpm:1.30
- For $20.00 enter the value smaato_cpm:20.00
Integrate the NextGen SDK
First, configure your SPX account: https://spx.smaato.com/
With the modular structure of Smaato’s NextGen SDK, you can select different integration options. The following are configuration examples for your project Podfile:
Banner Ads (with Rich Media support)
pod 'smaato-ios-sdk/Banner'
pod 'smaato-ios-sdk/UnifiedBidding'
pod 'smaato-ios-sdk-mediation-admob/Banner'
Interstitial Ads (with Rich Media & Video support)
pod 'smaato-ios-sdk/Interstitial'
pod 'smaato-ios-sdk/UnifiedBidding'
pod 'smaato-ios-sdk-mediation-admob/Interstitial'
Rewarded Video Ads
pod 'smaato-ios-sdk/RewardedAds'
pod 'smaato-ios-sdk/UnifiedBidding'
pod 'smaato-ios-sdk-mediation-admob/RewardedAds'
Interstitial Ads (Rich Media only)
pod 'smaato-ios-sdk/Modules/Interstitial'
pod 'smaato-ios-sdk/Modules/RichMedia'
pod 'smaato-ios-sdk/UnifiedBidding'
pod 'smaato-ios-sdk-mediation-admob/Interstitial'
Native Ads
pod 'smaato-ios-sdk/Native'
pod 'smaato-ios-sdk/UnifiedBidding'
pod 'smaato-ios-sdk-mediation-admob/Native'
OR
You can integrate manually with these source code files.
Open Terminal then go to the project directory.
cd {PATH_TO_PROJECT_DIR}
Install the pods.
pod install
Initialize the NextGen SDK
In the “AppDelegate.m” import SmaatoSDKCore module and configure SDK in “application:didFinishLaunchingWithOptions:“
// AppDelegate.swift
import UIKit
import SmaatoSDKCore
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// initialize SDK!
guard let config = SMAConfiguration(publisherId: "SMAATO_PUBLISHER_ID") else {
fatalError("SDK config is nil!")
}
// allow HTTPS traffic only
config.httpsOnly = true
// log errors only
config.logLevel = .error
// ads content restriction based on age
config.maxAdContentRating = .undefined
SmaatoSDK.initSDK(withConfig:config)
// allow the Smaato SDK to automatically get the user's location and put it inside the ad request
SmaatoSDK.gpsEnabled = true
// optional, specific configuration
SmaatoSDK.userGender = .male // usually set after user logs in
SmaatoSDK.userAge = 40 // usually set after user logs in
SmaatoSDK.userSearchQuery = "bitcoin, lamborghini, san+francisco"
return true
}
}
// AppDelegate.m
#import "AppDelegate.h"
@import SmaatoSDKCore;
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// initialize SDK first!
SMAConfiguration *config = [[SMAConfiguration alloc] initWithPublisherId:@"SMAATO_PUBLISHER_ID"];
// allow HTTPS traffic only
config.httpsOnly = YES;
// log errors only
config.logLevel = kSMALogLevelError;
// ads content restriction based on age
config.maxAdContentRating = kSMAMaxAdContentRatingUndefined;
[SmaatoSDK initSDKWithConfig:config];
// allow the Smaato SDK to automatically get the user's location and put it inside the ad request
SmaatoSDK.gpsEnabled = YES;
// optional configuration
SmaatoSDK.userGender = kSMAGenderFemale; // usually set after user logs in
SmaatoSDK.userAge = @30; // usually set after user logs in
SmaatoSDK.userSearchQuery = @"yoga, vegan, san+francisco";
return YES;
}
Important Note: Starting from NextGen SDK version 21.2.1, the new GPSEnabled flag allows the SDK to automatically get the user’s location when permission is given by the user. By default, this flag is set to off and the user location will not be provided in the ad request. To enable this the GPSEnabled flag must be set to yes (for objective-c) or true (for swift).
Important Details About GDPR
The General Data Protection Regulation (GDPR) was created to provide European users with greater transparency and control over their personal information. As a publisher, you should integrate a Consent Management Platform (CMP) and request for user, vendor, and purpose consents as outlined in IAB Europe’s IAB Tech Lab – CMP API v2. You can find an example implementation of a web-based CMP and the corresponding native wrappers here in the IAB’s GDPR-Transparency-and-Consent-Framework.
You can also make your own custom CMP. The collected end-user consent information needs to be stored in NSUserDefaults using the following keys:
Transparency and Consent Framework v2:
Key | Type | Description |
---|---|---|
IABTCF_gdprApplies | NSNumber |
1 = Subject to GDPR |
IABTCF_TCString | NSString |
Base64-encoded consent string as defined in IAB Tech Lab – Consent string and vendor list formats v2 |
Sample of GDPR Saving in NSUserDefaults
// User is not subject to GDPR
UserDefaults.standard.set(0, forKey: "IABTCF_gdprApplies")
// Use this example above to set values for the rest of the GDPR keys
// User is not subject to GDPR
[[NSUserDefaults standardUserDefaults] setObject:@0 forKey:@"IABTCF_gdprApplies"];
// Use this example above to set values for the rest of the GDPR keys
Important Details About CCPA
The California Consumer Privacy Act (CCPA) was created to provide California consumers with greater transparency and control over their personal information.
For more information about the CCPA regulation, please check out the Smaato FAQ. You can also review the IAB’s U.S. Privacy String documentation.
For Publishers with California-Based Users
As a publisher, you need to make sure to request consent from California-based users (to give or refuse consent / to opt-out or opt-in ) about private data transfer. This answer should be saved in NSUserDefaults with key “IABUSPrivacy_String” in the US Privacy String format (CCPA Opt-Out Storage Format).
Sample of CCPA Saving in NSUserDefaults
// save the user's consent
private func saveCCPA(usPrivacy string: String?) {
guard let usString = string else {
return
}
UserDefaults.standard.set(usString, forKey: "IABUSPrivacy_String"
}
// call this function like this:
// saveCCPA(string: "1YNN")
// save the user's consent
- (void)saveCCPAUsPrivacyString:(NSString *)usString
{
if (usString.length > 0) {
[NSUserDefaults.standardUserDefaults setObject:usString
forKey:@"IABUSPrivacy_String"];
}
}
// call this function like this:
// [saveCCPAUsPrivacyString usString:@"1YNN"];
NextGen SDK reads that value by key “IABUSPrivacy_String” if it exists and uses it as an optional parameter for all ad requests.
Further Examples of the U.S. Privacy String 1YNN: User has not made a choice to opt-out 1NYY: User has made a choice to opt-out 1—: A Digital Property has determined to use a U.S. Privacy string version 1 and that CCPA does not apply to the transaction.
Ads for Testing Purposes
Adspace ID | Type | Description |
---|---|---|
130626424 | Rich Media | Banner / Med-rect / Leaderboard / Skyscraper |
130635694 | Static Image | Banner / Med-rect / Leaderboard / Skyscraper |
130635706 | MRAID | Banner / Med-rect / Leaderboard / Skyscraper |
133150211 | Rich Media / Video | Interstitial (Video with an end-card + Rich Media Interstitial for 320×480, 480×320, 1024×768 & 768×1024) |
133149967 | Video | Skippable Video |
133149968 | Rewarded | Rewarded Video without an end-card |
130783664 | Native | Native with static image main creative |
Prebid a Banner Request
var bannerView = GAMBannerView(adSize: kGADAdSizeBanner)
bannerView.adUnitID = "YOUR_GAM_AD_UNIT_ID"
bannerView.rootViewController = self
bannerView.delegate = self
SmaatoSDK.prebidBanner(forAdSpaceId: "SMAATO_ADSPACE_ID",
bannerSize: .xxLarge_320x50) {(bid: SMAUbBid?, error: Error?) in
if let smaatoBid = bid {
// Let's assume this is the max price of your line items (you will want to change this float to yours)
let maxPrice : CGFloat = 0.1
let bidKeyword : String
// Helper to format bidKeyword if the received bid price is greater than max price. In this case, just add smaato_cpm prefix to the maxPrice (formatted value to the required fraction digits).
if smaatoBid.bidPrice > maxPrice {
bidKeyword = String(format: "smaato_cpm:%.2f", maxPrice)
} else {
bidKeyword = smaatoBid.mopubPrebidKeyword
}
let kvpRequest = GAMRequest()
let ubKVP = [
"smaub": bidKeyword // make sure you add "smaub" as a Dynamic Key under Inventory >> Key-Values inside of GAM (no value as you will pass that here)
]
kvpRequest.customTargeting = ubKVP
}
bannerView.load(kvpRequest)
}
GAMBannerView *bannerView = [[GAMBannerView alloc] initWithAdSize:kGADAdSizeBanner];
bannerView.adUnitID = @"YOUR_GAM_AD_UNIT_ID";
bannerView.rootViewController = self;
bannerView.delegate = self;
[SmaatoSDK prebidBannerForAdSpaceId:@"SMAATO_ADSPACE_ID"
bannerSize:kSMAUbBannerSizeXXLarge_320x50
completion:^(SMAUbBid * _Nullable bid, NSError * _Nullable error) {
if (bid) {
// Let's assume this is the max price of your line items (you will want to change this float to yours)
CGFloat maxPrice = 0.1;
NSString *bidKeyword;
if (bid.bidPrice > maxPrice) {
bidKeyword = [NSString stringWithFormat:@"smaato_cpm:%.2f", maxPrice];
} else {
bidKeyword = bid.mopubPrebidKeyword;
}
GAMRequest kvpRequest = [GAMRequest request];
NSDictionary *ubKVP = @{@"smaub":bidKeyword}; // make sure you add "smaub" as a Dynamic Key under Inventory >> Key-Values inside of GAM (no value as you will pass that here)
kvpRequest.customTargeting = ubKVP;
}
[self.bannerView loadRequest: kvpRequest];
}];
adViewDidLoadAd
or adViewDidFailToLoadAd
events, clear KVPs in the GAM request and make another request to get new bids.Prebid an Interstitial Request
private var interstitial: GAMInterstitialAd?
SmaatoSDK.prebidInterstitial(forAdSpaceId: "SMAATO_ADSPACE_ID") { (bid: SMAUbBid?, error: Error?) in
if let smaatoBid = bid {
// Let's assume this is the max price of your line items (you will want to change this float to yours)
let maxPrice : CGFloat = 0.1
let bidKeyword : String
if smaatoBid.bidPrice > maxPrice {
bidKeyword = String(format: "smaato_cpm:%.2f", maxPrice)
} else {
bidKeyword = smaatoBid.mopubPrebidKeyword
}
let kvpRequest = GAMRequest()
let ubKVP = [
"smaub": bidKeyword // make sure you add "smaub" as a Dynamic Key under Inventory >> Key-Values inside of GAM (no value as you will pass that here)
]
kvpRequest.customTargeting = ubKVP
}
GAMInterstitialAd.load(withAdUnitID:"YOUR_GAM_AD_UNIT_ID",
request: kvpRequest,
completionHandler: { [self] ad, error in
if let error = error {
print("Failed to load interstitial ad with error: \(error.localizedDescription)")
return
}
interstitial = ad
interstitial?.fullScreenContentDelegate = self
}
)
}
@property(nonatomic, strong) GAMInterstitialAd *interstitial;
[SmaatoSDK prebidInterstitialForAdSpaceId:@"SMAATO_ADSPACE_ID"
completion:^(SMAUbBid * _Nullable bid, NSError * _Nullable error) {
if (bid) {
// Let's assume this is the max price of your line items (you will want to change this float to yours)
CGFloat maxPrice = 0.1;
NSString *bidKeyword;
if (bid.bidPrice > maxPrice) {
bidKeyword = [NSString stringWithFormat:@"smaato_cpm:%.2f", maxPrice];
} else {
bidKeyword = bid.mopubPrebidKeyword;
}
GAMRequest kvpRequest = [GAMRequest request];
NSDictionary *ubKVP = @{@"smaub":bidKeyword}; // make sure you add "smaub" as a Dynamic Key under Inventory >> Key-Values inside of GAM (no value as you will pass that here)
kvpRequest.customTargeting = ubKVP;
}
[GAMInterstitialAd loadWithAdUnitID:@"YOUR_GAM_AD_UNIT_ID"
request:kvpRequest
completionHandler:^(GAMInterstitialAd *ad, NSError *error) {
if (error) {
NSLog(@"Failed to load interstitial ad with error: %@", [error localizedDescription]);
return;
}
self.interstitial = ad;
self.interstitial.fullScreenContentDelegate = self;
}];
Prebid a Rewarded Video Request
var rewardedAd: GADRewardedAd?
SmaatoSDK.prebidRewardedInterstitial(forAdSpaceId: "SMAATO_ADSPACE_ID") { (bid: SMAUbBid?, error: Error?) in
if let smaatoBid = bid {
// Let's assume this is the max price of your line items (you will want to change this float to yours)
let maxPrice : CGFloat = 0.1
let bidKeyword : String
if smaatoBid.bidPrice > maxPrice {
bidKeyword = String(format: "smaato_cpm:%.2f", maxPrice)
} else {
bidKeyword = smaatoBid.mopubPrebidKeyword
}
let kvpRequest = GAMRequest()
let ubKVP = [
"smaub": bidKeyword // make sure you add "smaub" as a Dynamic Key under Inventory >> Key-Values inside of GAM (no value as you will pass that here)
]
kvpRequest.customTargeting = ubKVP
}
GADRewardedAd.load(withAdUnitID:"YOUR_GAM_AD_UNIT_ID",
request: kvpRequest,
completionHandler: { [self] ad, error in
if let error = error {
print("Failed to load rewarded ad with error: \(error.localizedDescription)")
return
}
rewardedAd = ad
rewardedAd?.fullScreenContentDelegate = self
}
)
}
@property(nonatomic, strong) GADRewardedAd *rewardedAd;
[SmaatoSDK prebidRewardedInterstitialForAdSpaceId:@"SMAATO_ADSPACE_ID"
completion:^(SMAUbBid * _Nullable bid, NSError * _Nullable error) {
if (bid) {
// Let's assume this is the max price of your line items (you will want to change this float to yours)
CGFloat maxPrice = 0.1;
NSString *bidKeyword;
if (bid.bidPrice > maxPrice) {
bidKeyword = [NSString stringWithFormat:@"smaato_cpm:%.2f", maxPrice];
} else {
bidKeyword = bid.mopubPrebidKeyword;
}
GAMRequest kvpRequest = [GAMRequest request];
NSDictionary *ubKVP = @{@"smaub":bidKeyword}; // make sure you add "smaub" as a Dynamic Key under Inventory >> Key-Values inside of GAM (no value as you will pass that here)
kvpRequest.customTargeting = ubKVP;
}
[GADRewardedAd
loadWithAdUnitID:@"YOUR_GAM_AD_UNIT_ID"
request:kvpRequest
completionHandler:^(GADRewardedAd *ad, NSError *error) {
if (error) {
NSLog(@"Rewarded ad failed to load with error: %@", [error localizedDescription]);
return;
}
self.rewardedAd = ad;
NSLog(@"Rewarded ad loaded.");
}];
];
Last Modified: March 15, 2022 at 3:28 pm