Unified Bidding | iOS Integration

Unified Bidding Integration Guide for Publishers with Smaato or MoPub as a Primary Ad Server

(iOS NextGen SDK version 21.6.17)

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.

 

MoPub Setup Process

For each price point, you will need to create one line item in MoPub.

 

MoPub Dashboard Setup

2. Click on “New network” button

3. Choose “Custom SDK network” from the bottom of the list

4. Set the desired parameters in the next two tabs.

5. On “App & ad unit setup” tab, set the following: 

    • Custom event classes:
      • For Banner Ads: SMAMoPubSmaatoBannerAdapter
      • For Interstitial Ads: SMAMoPubSmaatoInterstitialAdapter
      • For Rewarded Video Ads: SMAMoPubSmaatoRewardedVideoAdapter
    • Custom event class data:
      • {"adspaceId":"<YOUR_SPX_ADSPACE_ID>"}

6. Next, set the Rate of the price that you want to target, for example, $0.10.

7. For Daypart targeting please select the option Serve ads all the time.

8. For Frequency caps please select No caps for impressions per user.

In the Audience Keyword targeting section you need to enter a keyword for each line item that you have created. Please make sure that the value matches the price rate of the Line Item and repeat this process for each Line Item.

Example Keyword targeting:

  • For $0.10 enter the keyword smaato_cpm:0.10
  • For $1.30 enter the keyword smaato_cpm:1.30
  • For $20.00 enter the keyword 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/Modules/UnifiedBidding'
pod 'smaato-ios-sdk-mediation-mopub/Banner'

Interstitial Ads (with Rich Media & Video support)

pod 'smaato-ios-sdk/Interstitial'
pod 'smaato-ios-sdk/Modules/UnifiedBidding'
pod 'smaato-ios-sdk-mediation-mopub/Interstitial'

Rewarded Video Ads

pod 'smaato-ios-sdk/RewardedAds'
pod 'smaato-ios-sdk/Modules/UnifiedBidding'
pod 'smaato-ios-sdk-mediation-mopub/RewardedAds'

Interstitial Ads (Rich Media only)

pod 'smaato-ios-sdk/Modules/Interstitial'
pod 'smaato-ios-sdk/Modules/RichMedia'
pod 'smaato-ios-sdk/Modules/UnifiedBidding'
pod 'smaato-ios-sdk-mediation-mopub/Interstitial'

OR
You can integrate manually with the following source code files for MoPub 5.12 or MoPub 5.13+.

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.mimport SmaatoSDKCore module and configure SDK in “application:didFinishLaunchingWithOptions:

Swift
Objective-C
Swift
// 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
    }
}

Objective-C
// 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).

 

SDK Initialization Using MoPub SDK Mediation Configuration Class

Swift
Objective-C
Swift
// AppDelegate.swift
import UIKit
import SmaatoSDKCore
  
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {  
    var window: UIWindow?
      
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        let sdkConfig = MPMoPubConfiguration(adUnitIdForAppInitialization: "MOPUB_AD_UNIT_ID")
        sdkConfig.globalMediationSettings = []
        sdkConfig.loggingLevel = .info
        sdkConfig.allowLegitimateInterest = true
        sdkConfig.additionalNetworks = SMAMoPubBaseAdapterConfiguration()
        
        var smaatoConfig: [AnyHashable : Any] = [:]
        smaatoConfig.setObject("SMAATO_PUBLISHER_ID", forKey: "publisherId")
        smaatoConfig.setObject(true, forKey: "httpsOnly")
        smaatoConfig.setObject(false, forKey: "loggingDisabled")
        smaatoConfig.setObject(kSMALogLevelError, forKey: "logLevel")
        smaatoConfig.setObject(kSMAMaxAdContentRatingUndefined, forKey: "maxAdContentRating")
        
        sdkConfig.mediatedNetworkConfigurations = [
        "SMAMoPubBaseAdapterConfiguration": smaatoConfig
        ]
        
        MoPub.sharedInstance().initializeSdk(with: sdkConfig) {
            print("SDK initialization complete.")
            SmaatoSDK.gpsEnabled = true
            SmaatoSDK.userGender = kSMAGenderFemale
            SmaatoSDK.userAge = 30
            SmaatoSDK.userSearchQuery = "yoga, vegan, san+francisco"
        }
         
        return true
    }
}

Objective-C
// AppDelegate.m 
#import "AppDelegate.h"
  
@import SmaatoSDKCore; // Import SmaatoSDKCore 
@import MoPub; // Import MoPub
@implementation AppDelegate
  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    MPMoPubConfiguration *sdkConfig = [[MPMoPubConfiguration alloc] initWithAdUnitIdForAppInitialization:@ "MOPUB_AD_UNIT_ID"];
 
    sdkConfig.globalMediationSettings = @[];
    sdkConfig.loggingLevel = MPBLogLevelInfo;
    sdkConfig.allowLegitimateInterest = YES;
    sdkConfig.additionalNetworks = @[SMAMoPubBaseAdapterConfiguration.class];
     
    NSMutableDictionary *smaatoConfig = [NSMutableDictionary new];
    [smaatoConfig setObject:@"SMAATO_PUBLISHER_ID" forKey:@"publisherId"];
    [smaatoConfig setObject:@(YES) forKey:@"httpsOnly"];
    [smaatoConfig setObject:@(NO) forKey:@"loggingDisabled"];
    [smaatoConfig setObject:@(kSMALogLevelError) forKey:@"logLevel"];
    [smaatoConfig setObject:@(kSMAMaxAdContentRatingUndefined) forKey:@"maxAdContentRating"];
 
    sdkConfig.mediatedNetworkConfigurations = [@{@"SMAMoPubBaseAdapterConfiguration":smaatoConfig} mutableCopy];
 
    [[MoPub sharedInstance] initializeSdkWithConfiguration:sdkConfig completion:^{
        NSLog(@"SDK initialization complete");
        // SDK initialization complete. Ready to make ad requests.
        SmaatoSDK.gpsEnabled = YES;
        SmaatoSDK.userGender = kSMAGenderFemale;
        SmaatoSDK.userAge = @30;
        SmaatoSDK.userSearchQuery = @"yoga, vegan, san+francisco";
    }];
     
    return YES;
}
  
@end

 

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
0 = Not subject to GDPR
-1 or unset = Undetermined (default before initialization)

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

Swift
Objective-C
Swift
// 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

Objective-C
// 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

Swift
Objective-C
Swift
// 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")

Objective-C
// 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.

 

 

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
130626426 Rich Media / Video Interstitial (Video with an end-card + Rich Media Interstitial for 320×480, 480×320, 1024×768 & 768×1024)
130626427 Video Skippable Video
130626428 Rewarded Rewarded Video
130635048 Rewarded Rewarded Video without an end-card

 

 

Prebid a Banner Request

Swift
Objective-C
Swift
guard let adView = MPAdView(adUnitId: "a27e026a62d54d5ba59486ef6d63406c", size: MOPUB_BANNER_SIZE) else {
    return
}
adView.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
        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
        }
        // or manual something like `let bidKeyword = "smaato" + "-" + "\(smaatoBid.bidPrice * 100)"`
        if let adViewKeywords = adView.keywords {                                       
            adView.keywords = adViewKeywords + ",\(bidKeyword)"
        } else {
            adView.keywords = bidKeyword
        }
        // `smaatoBid.metaData` contains unique bid identifier, which is used for creative fetching from cache, if Smaato's bid wins
        if let adLocalExtras = adView.localExtras {
            adView.localExtras = adLocalExtras.merging(smaatoBid.metaData) { $1 }
        } else {
            adView.localExtras = smaatoBid.metaData
        }
    }
    adView.loadAd()
}

Objective-C
MPAdView *adView = [[MPAdView alloc] initWithAdUnitId:@"MOPUB_ADUNIT_ID" size:MOPUB_BANNER_SIZE];
adView.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
        CGFloat maxPrice = 0.1;
        NSString *bidKeyword;
                                      
        if (bid.bidPrice > maxPrice) {
            bidKeyword = [NSString stringWithFormat:@"smaato_cpm:%.2f", maxPrice];
        } else {
            bidKeyword = bid.mopubPrebidKeyword;
        }
                
        NSString *adViewKeywords = adView.keywords;
        adViewKeywords = adViewKeywords ? [NSString stringWithFormat:@"%@,%@", adViewKeywords, bidKeyword] : bidKeyword;
        adView.keywords = adViewKeywords;
        if (self.adView.localExtras) {
            NSMutableDictionary *extras = [NSMutableDictionary dictionaryWithDictionary:self.adView.localExtras];
            [extras addEntriesFromDictionary:bid.metaData];
            self.adView.localExtras = extras;
        } else {
            self.adView.localExtras = bid.metaData;
        }
    }
    [adView loadAd];  
}];

 

Prebid an Interstitial Request

Swift
Objective-C
Swift
guard let adInterstitial = MPInterstitialAdController.init(forAdUnitId: "MOPUB_ADUNIT") else {
    return
}
adInterstitial.delegate = self
SmaatoSDK.prebidInterstitial(forAdSpaceId: "SMAATO_ADSPACE_ID") { (bid: SMAUbBid?, error: Error?) in
   if let smaatoBid = bid {
       // Let's assume this is the max price
       let maxPrice : CGFloat = 0.1
       let bidKeyword : String
                 
       if smaatoBid.bidPrice > maxPrice {
            bidKeyword = String(format: "smaato_cpm:%.2f", maxPrice)
       } else {
            bidKeyword = smaatoBid.mopubPrebidKeyword
       }
       // or manual something like `let bidKeyword = "smaato" + "-" + "\(smaatoBid.bidPrice * 100)"`
       if let adViewKeywords = adInterstitial.keywords {
            var filteredKeywords = adViewKeywords.split(separator: ",").filter { !$0.starts(with: "smaato_cpm") }.map { String($0) }
            filteredKeywords.append(bidKeyword)
            adInterstitial.keywords = filteredKeywords.joined(separator: ",")
       } else {
            adInterstitial.keywords = bidKeyword
       }
 
       // `smaatoBid.metaData` contains unique bid identifier, which is used for creative fetching from cache, if Smaato's bid wins
       if let adLocalExtras = adInterstitial.localExtras {
            adInterstitial.localExtras = adLocalExtras.merging(smaatoBid.metaData) { $1 }
       } else {
            adInterstitial.localExtras = smaatoBid.metaData
       }
   }
   adInterstitial.loadAd()
}

Objective-C
MPInterstitialAdController *adInterstitial = [MPInterstitialAdController interstitialAdControllerForAdUnitId:@"MOPUB_ADUNIT_ID"];
adInterstitial.delegate = self;
[SmaatoSDK prebidInterstitialForAdSpaceId:@"SMAATO_ADSPACE_ID"
                               completion:^(SMAUbBid * _Nullable bid, NSError * _Nullable error) {
    if (bid) {
        // Let's assume this is the max price
        CGFloat maxPrice = 0.1;
        NSString *bidKeyword;
                                            
        if (bid.bidPrice > maxPrice) {
            bidKeyword = [NSString stringWithFormat:@"smaato_cpm:%.2f", maxPrice];
        } else {
            bidKeyword = bid.mopubPrebidKeyword;
        }
        NSString *adViewKeywords = adInterstitial.keywords;
        if (adViewKeywords.length > 0) {
            NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF beginswith[c] 'smaato_cpm')"];
            NSMutableArray *filteredKeywords = [[[adViewKeywords componentsSeparatedByString:@","] filteredArrayUsingPredicate:predicate] mutableCopy];
            [filteredKeywords addObject:bidKeyword];
            adInterstitial.keywords = [filteredKeywords componentsJoinedByString:@","];
        } else {
            adInterstitial.keywords = bidKeyword;
        }
 
        if (adInterstitial.localExtras) {
            NSMutableDictionary *extras = [NSMutableDictionary dictionaryWithDictionary:adInterstitial.localExtras];
            [extras addEntriesFromDictionary:bid.metaData];
            adInterstitial.localExtras = extras;
        } else {
            adInterstitial.localExtras = bid.metaData;
        }
    }
    [adInterstitial loadAd];
}];

 

Prebid a Rewarded Video Request

Swift
Objective-C
Swift
MPRewardedVideo.setDelegate(self, forAdUnitId: "MOPUB_ADUNIT_ID")
SmaatoSDK.prebidRewardedInterstitial(forAdSpaceId: "SMAATO_ADSPACE_ID") { (bid: SMAUbBid?, error: Error?) in
    var keyword: String? = "SOME-OTHER-BID-KEYWORDS"
    var bidExtra: [AnyHashable:Any]? = ["SOME-OTHER-KEY" : "SOME-OTHER-VALUE"]
    if let smaatoBid = bid {
        // Let's assume this is the max price
        let maxPrice : CGFloat = 0.1
        let bidKeyword : String            
        if smaatoBid.bidPrice > maxPrice {
            bidKeyword = String(format: "smaato_cpm:%.2f", maxPrice)
        } else {
            bidKeyword = smaatoBid.mopubPrebidKeyword
        }
        // or manual something like `let bidKeyword = "smaato" + "-" + "\(smaatoBid.bidPrice * 100)"`
        keywords = (keywords != nil) ? keywords + ",\(bidKeyword)" : bidKeyword
        bidExtra = (bidExtra != nil) ? bidExtra.merging(smaatoBid.metaData) { $1 } : smaatoBid.metaData
   }
   MPRewardedVideo.loadAd(withAdUnitID: "MOPUB_ADUNIT_ID", keywords: keywords, userDataKeywords: nil, location: nil, customerId: nil, mediationSettings: [], localExtras: bidExtra)
}

Objective-C
[MPRewardedVideo setDelegate:self forAdUnitId:@"MOPUB_ADUNIT_ID"];
[SmaatoSDK prebidRewardedInterstitialForAdSpaceId:@"SMAATO_ADSPACE_ID"
                                       completion:^(SMAUbBid * _Nullable bid, NSError * _Nullable error) {
    NSString *keywords = @"SOME-OTHER-BID-KEYWORDS"; // could be nil
    NSDictionary *bidExtra = @{@"SOME-OTHER-KEY" : @"SOME-OTHER-VALUE"}; // could be nil
    if (bid) {
        // Let's assume this is the max price
        CGFloat maxPrice = 0.1;
        NSString *bidKeyword;                                          
        if (bid.bidPrice > maxPrice) {
            bidKeyword = [NSString stringWithFormat:@"smaato_cpm:%.2f", maxPrice];
        } else {
            bidKeyword = bid.mopubPrebidKeyword;
        }
 
        keywords = keywords ?  [NSString stringWithFormat:@"%@,%@", keywords, bidKeyword] : bidKeyword;
 
        if (bidExtra) {
            NSMutableDictionary *extras = [NSMutableDictionary dictionaryWithDictionary:bidExtra];
            [extras addEntriesFromDictionary:bid.metaData];
            bidExtra = extras;
        } else {
            bidExtra = bid.metaData;
        }
    }
    [MPRewardedVideo loadRewardedVideoAdWithAdUnitID:@"MOPUB_ADUNIT_ID"
                                            keywords:keywords
                                    userDataKeywords:nil
                                            location:nil
                                          customerId:nil
                                   mediationSettings:@[]
                                         localExtras:bidExtra];
   } 
];

 

Last Modified: November 19, 2021 at 12:41 pm


© 2005-2020 Smaato, Inc. All Rights Reserved. Smaato® is a registered trademark of Smaato, Inc.

Privacy Policy | Cookie Policy | Support