prebid / prebid-mobile-ios

Prebid Mobile SDK for iOS applications
Apache License 2.0
47 stars 88 forks source link

Rendering Module - Milestone 1 #363

Closed YuriyVelichkoOpenX closed 2 years ago

YuriyVelichkoOpenX commented 3 years ago

Summary

This proposal describes the steps of the initial migration of Apollo SDK into Prebid SDK. In the scope of this doc, the term Apollo SDK is still used for distinguishing the features of Prebid SDK and Apollo. After this migration, everything will be Prebid SDK.

Migration steps:

Won't be done

Several things will be purposely omitted in the scope of this milestone.

Acceptance Criteria

Here is the list of items that should be achieved at the end of the milestone:

For the Discussion

  1. Objective-C vs Swift API. The current Apollo codebase is written with Objective-C. We strongly believe that Swift is a more safe and efficient language so its usage is preferable. We propose to rewrite the API classes to Swift or make Swift wrappers for them, depending on estimated efforts for rewriting. In this case, we will provide Swift API for all Prebid modules.
  2. Test ads. Apollo SDK uses Apollo Server, MoPub, and GAM instances to run integration tests. We think that it makes sense not to migrate them in the initial milestone but eventually, we will definitely do it. So we need to develop a migration policy to the Prebid instances.
  3. Demo ads. All demo cases available for publishers should use the current PBS instance. We would need access to them.
  4. Tech docs and specs. Does Prebid SDK have internal documentation? How is public documentation maintained?

Integration: Dependency Managers

Cocoapods

To download and integrate the Rendering Module into the project using CocoaPods, publishers will have to add the following line to the project’s podfile:

pod 'PrebidMobile/Rendering'

To integrate the Prebid SDK with GAM or MoPub

# + Google Ad Manager (optional)
pod 'PrebidMobile/GAMEventHandlers'
# + MoPub (optional)
pod 'PrebidMobile/MoPubAdapters'

Gradle

To add the Rendering Module dependency, need to update the app module’s build.gradle to have the following dependencies:

implementation 'org.prebid:prebid-mobile-sdk-rendering:1.30'

To integrate the Prebid SDK with GAM or MoPub

# + Google Ad Manager (optional)
implementation 'org.prebid:prebid-mobile-sdk-gam-event-handler:1.30'
# + MoPub (optional)
implementation 'org.prebid:prebid-mobile-sdk-mopub-adapters:1.30'

Usage of Rendering API

To be on the same page, by the API we mean the classes that publishers should integrate into their codebase in order to make bid requests and render ads. There are three kinds of API classes in the SDK:

  1. Views. They should be built into the application UI and they will display the winning bid.
  2. AdUnits. They make bid requests and set the targeting keywords in the ad requests for Primary Ad Server. They do not render anything.
  3. Controllers. They manage Interstitial ads. Firstly they make bid requests and then display interstitial views.

In this proposal, the iOS API is described. The Android API is pretty much similar.

Integration Scenarios

Depending on the integration kind, with Primary Ad Server or without it, the API will be different.

Ad Format Pure In-App Bidding Google Ad Manager MoPub
Display Banner Banner View Banner View Banner Ad Unit + Banner Adapter
Display Interstitial Interstitial Controller Interstitial Controller Interstitial Ad Unit + Interstitial Adapter
Video Banner Banner View Banner View Banner Ad Unit + Banner Adapter
Video Interstitial Interstitial Controller Interstitial Controller Interstitial Ad Unit + Interstitial Adapter
Rewarded Video Rewarded Controller Rewarded Controller Rewarded Ad Unit + Rewarded Adapter
Native Native Ad Unit + Native Ad + Media View Native Ad Unit + Native Ad + Media View Native Ad Unit + Native Ad Adapter

For In-App and GAM integrations publishers will integrate Prebid Ad Views into their UI layout.

For MoPub integration publishers will integrate MoPub Ad View but the rendering of the prebid ad will be performed in Prebid Ad Views via mediation functionality.

Integration Example: Banner

Below you can see the snippets of banner ads integrations.

In-App Bidding (No Primary Ad Server)

// Create an Ad View
let banner = OXABannerView(frame: CGRect(origin: .zero, size: adSize),
                           configId: CONFIG_ID,
                           adSize: adSize)

banner.delegate = self

// Load an Ad
banner.loadAd()

self.view.addSubview(banner)

This code will work as follows:

  1. BannerView makes a bid request
  2. BannerView renders a winning bid

GAM

// Create an Event Handler
let eventHandler = OXAGAMBannerEventHandler(adUnitID: GAM_AD_UNIT_ID,
                                            validGADAdSizes: [NSValueFromGADAdSize(adSize)])

// Create a Banner View
let banner = OXABannerView(configId: CONFIG_ID,
                           eventHandler: eventHandler)
banner.delegate = self

addBannerToUI(banner: banner)

// Load an Ad
banner.loadAd()

self.view.addSubview(banner)

This code will work as follows:

  1. GAMBannerEventHandler creates internal GAM classes f.e., OXADFPBanner, DFPRequest.
  2. BannerView makes a bid request
  3. GAMBannerEventHandler sets targeting keywords into DFPRequest
  4. GAMBannerEventHandler makes an ad request
  5. GAMBannerEventHandler detects who won on the primary ad server:
    1. App Event for Banner and Interstitial Ads
    2. Meta Data Chanages for Rewarded ads
  6. If Prebid wins GAMBannerEventHandler will return control flow to the PrebidSDK in order to render an internal WebView with the winning bid
  7. If GAM Ad wins GAMBannerEventHandler will return control flow to the PrebidSDK with particular OXADFPBanner in order to display this view

MoPub

// Create an AdView
banner = MPAdView(adUnitId: MOPUB_AD_UNIT_ID)
banner.delegate = self

self.view.addSubview(banner)

// Create an In-App Bidding Ad Unit
adUnit = OXAMoPubBannerAdUnit(configId: CONFIG_ID, size: adSize)

// Run an Header Bidding auction on Apollo
adUnit.fetchDemand(with: banner!) { [weak self] result in

     // Load an Ad
    self?.banner.loadAd()
}

This code will work as follows:

  1. BannerAdUnit makes a bid request
  2. BannerAdUnit sets targeting keywords into MPAdView
  3. MPAdView makes an ad request
  4. If Prebid Line Item wins the MoPub SDK will instantiate the adapter class OXAMoPubBannerAdapter that will render the winning bid. All MoPub adapters are shipped with PrebidSDKMoPubAdapters framework.
  5. If MoPub ad wins the MoPub SDK will render it in the same MPAdView

Other API Classes

Other ad formats are supported in a similar way as Banner ads with some differences related to the integration peculiarities. The samples of their integration are skipped in this proposal in order to keep it minimal.

You can find integration examples in the Demo Application described below.

Event Handlers

In order to avoid dependencies to the GAM or MoPub SDKs and also reduce the usage of reflective programming in the core SDK, Apollo uses additional frameworks:

Demo Application

The current Demo Application provides examples of integration for all basic ad formats for In-App, GAM, and MoPub integrations.

Download iOS Demo App

Download Android Demo App

Note: The current public Apollo SDK version is 1.1.0 and it does not include support if Native Ads and a bunch of other improvements. Now we are working on the finalization of version 1.2 which will be migrated into Prebid.

Internal Test Applications

Apollo SDK has a powerful testing tool called Internal Test Application. It contains dozens of test cases and provides the ability of runtime configuration.

UI tests are based on this application. Its codebase wasn't intended to be public. Os it is not aimed to demonstrate the integration approaches but to test as many aspects as possible.

This Application is required in order to keep SDK reliable. However, there are several concerns related to its migration.

Ad Unit Configs

Currently, all test cases for the SDK are hosted on the Apollo server. Do you ok with keeping them there or we should migrate cases to the Prebid instances.

Mock Server

A lot of test cases for rendering and network requests depend on our Internal Mock Server. The Mock server is an app that acts like a real prebid server. It does not run auctions, of course, but it is able to respond with a particular mock to /auction request. This app helps to avoid network issues and allows to host dozens of mocked responses so there is no need to manage PBS configs to test rendering peculiarities.

YuriyVelichkoOpenX commented 3 years ago

@bszekely1 @yoalex5

Stability verification

In order to be sure that the rendering module will work properly we can rely on three points:

Here is how our integration tests work:

Mock Server Integration (Formatted Version)-Prebid Proposal

The test flow looks like this: MockServerDiagram

Step 1: The testing application starts a particular test.

Step 2: Before starting the test case, the test application can upload the mock for the particular ad unit to the Mock Server. If the mock already exists on the MockServer this step can be skipped.

Step 3: The SDK makes a regular bid request.

Step 4: The Mock Server returns the mock for the requested ad unit. If there is no mock the response will be empty (no bids).

Step 5: The ad requests additional resources like images or video files. All of them should be present on the Mock Server.

Step 6: The Mock Server returns generated or stored resources.

Step 7: The SDK tracks events to the Mock Server. The OM SDK events are tracked to the Mock Server as well.

Step 8: In any time the Testing Application could request the logs of all network requests to the Mock Server.

Step 9: The Mock Server returns all logs, and the Testing Application could analyze them.

Here is the Mock Server repository.

In brief, it gives us two major advantages over other testing techniques:

The biggest advantage of integration tests is traffic testing. They check almost all kinds of network requests including VAST and Open Measurement events. Thanks to them we are sure that all network requests are stable despite any changes in the SDK.

And of course, our tests pointed not only to the Mock Server, we have tests that run over the live prebid server as well.

YuriyVelichkoOpenX commented 3 years ago

@bszekely1 @yoalex5

Modularization Roadmap

We’ve prepared some diagrams to visualize how we see the modularization future. Of course, they are for discussion not for obligatory implementation. But we think they will help to keep everyone on the same page.

Current Components

Origin

Rendering: Open Beta

Open Beta

Rendering: Reuse of core components (Open Alpha)

Open Alpha

API Changes: 2.0 with prod rendering functionality and new API

New API with Complete Modularization

YuriyVelichkoOpenX commented 3 years ago

@bszekely1, @yoalex5

Event handlers and waterfall

On the last call dedicated to the rendering module, we touched the topic of integration into MoPub and GAM. We proposed to perform it using standalone libraries - GAM Event Handlers and MoPub Adapters.

Here is the source code of these libs: Android: MoPubAdapters.zip, OpenXApolloGAMEventHandlers.zip

iOS: EventHandlers.zip

Returning to the question of the waterfall for MoPub integration. I reviewed all available docs and according to them, the Line Item type does not play any role in the creation of the ad sources chain in the MoPub’s ad response. Line items are ranged by priority in the first place and then by CPM. So the Non-Guaranteed line items are processed in the same way as Network Line items. To check it I’ve created the demo app with six ad units that are targeted by different line items.

TestMoPubWaterfall.zip

Screenshot 2021-03-17 at 15 36 46

NG - Non-Guaranteed Line Item with CPM 0.10 (like for PUC) Network - Network Line Item with CPM 0.10 (like for Custom Adapters)

NG 0.15 - Non-Guaranteed Line Item with CPM 0.15 (like for PUC) N 0.15 - Network Line Item with CPM 0.15 (like for Custom Adapters)

You can build the app and inspect the traffic. I’ll just leave here the ad response for the case “2 NG + 2 Network LI”.

{
    "ad-responses": [{
        "metadata": {
            "x-refreshtime": 999999,
            "x-adtype": "html",
            "x-width": 320,
            "x-height": 50,
            "x-networktype": "custom",
            "x-creativeid": "c955a83cb7344faa88ae912e80d79f77",
            "x-adgroupid": "14131c6800644268adba6677bc1943e1",
            "vast-player-version": 1,
            "content-type": "text/html; charset=UTF-8",
            "x-browser-agent": 0,
            "x-banner-impression-min-ms": "0",
            "x-banner-impression-min-pixels": "1",
            "imptrackers": ["https://ads.mopub.com/m/imp?cid=c955a83cb7344faa88ae912e80d79f77&city=Kherson&ckv=2&country_code=UA&cppck=0FBE9&current_consent_status=dnt&dev=x86_64&exclude_adgroups=14131c6800644268adba6677bc1943e1%2C8ea2fab7329743478559433103850765%2Cba139a81429e44a99c13257cc150da00%2Cc8de848595f444cdbf900c6e02ca3b7e&gdpr_applies=0&id=1439b3b5a9e8459796e82ec2d872a0f9&is_mraid=0&os=iOS&osv=14.4.0&priority=11&req=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&reqt=1615978215.0&rev=0.000100&udid=ifv%3A284487A5-0426-47D5-8F9C-0F257E0B712B&video_type="],
            "x-after-load-url": "https://cb.mopub.com/load?account_id=ec3ab052690d42da8d0f7c390bb9fbb2&adgroup_id=14131c6800644268adba6677bc1943e1&adgroup_priority=11&adgroup_type=network&adunit_format=Banner&adunit_id=1439b3b5a9e8459796e82ec2d872a0f9&app_id=5142930d5bc7410894a4666721b11ebb&campaign_id=423148dcbd5a4c1fb546666fa2519571&cid=c955a83cb7344faa88ae912e80d79f77&cluster=atla&connection_type=2&country=UA&hostname=atla-ihp-24-sr1.prod.twttr.net&is_mraid=0&log_attempt=1&logged_server_side=0&os=iOS&request_id=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&request_ts_ms=1615978215&rev=0.000100&round_trip_count=0&sdk_version=5.16.1&load_result=%%LOAD_RESULT%%&load_duration_ms=%%LOAD_DURATION_MS%%",
            "clicktrackers": ["https://ads.mopub.com/m/aclk?cid=c955a83cb7344faa88ae912e80d79f77&city=Kherson&ckv=2&country_code=UA&cppck=6AF3E&current_consent_status=dnt&dev=x86_64&exclude_adgroups=14131c6800644268adba6677bc1943e1%2C8ea2fab7329743478559433103850765%2Cba139a81429e44a99c13257cc150da00%2Cc8de848595f444cdbf900c6e02ca3b7e&gdpr_applies=0&id=1439b3b5a9e8459796e82ec2d872a0f9&is_mraid=0&os=iOS&osv=14.4.0&priority=11&req=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&reqt=1615978215.0&rev=0&udid=ifv%3A284487A5-0426-47D5-8F9C-0F257E0B712B&video_type="],
            "x-before-load-url": "",
            "x-ad-timeout-ms": 10000
        },
        "content": "<!DOCTYPE html> <html> <head>  <!-- Adgroup is 14131c6800644268adba6677bc1943e1 -->  <meta http-equiv=\"Content-Security-Policy\" content=\"upgrade-insecure-requests\">  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">  <style type='text/css'> .mp_center { position: fixed; top: 50%; left: 50%; margin-left: -160px !important; margin-top: -25px !important; } </style>  <script type=\"text/javascript\"> function mopubFinishLoad(){ setTimeout(function() { window.location = 'mopub://finishLoad'; }, 0); } </script>  <script type=\"text/javascript\"> function webviewDidClose(){ if (typeof webviewDidCloseHelper == 'function') { webviewDidCloseHelper(); } } function webviewDidAppear(){  if(typeof trackImpressionHelper == 'function') { trackImpressionHelper(); }  if(typeof webviewDidAppearHelper == 'function') { webviewDidAppearHelper(); } } window.addEventListener(\"load\", function() { var links = document.getElementsByTagName('a'); for(var i=0; i < links.length; i++) { links[i].setAttribute('target','_blank'); } }, false); </script>  </head> <body style=\"margin:0;padding:0;\"> Test: Network Line Item #2 <script type=\"text/javascript\"> if (typeof htmlWillCallFinishLoad == \"undefined\" || !htmlWillCallFinishLoad) { if(typeof mopubFinishLoad == 'function') { window.onload = mopubFinishLoad; } }  </script>  </body> </html>"
    }, {
        "metadata": {
            "x-refreshtime": 999999,
            "x-adtype": "html",
            "x-width": 320,
            "x-height": 50,
            "x-creativeid": "ce94441291d24e68a8636d67cac53e7f",
            "x-adgroupid": "8ea2fab7329743478559433103850765",
            "vast-player-version": 1,
            "content-type": "text/html; charset=UTF-8",
            "x-browser-agent": 0,
            "x-banner-impression-min-ms": "0",
            "x-banner-impression-min-pixels": "1",
            "imptrackers": ["https://ads.mopub.com/m/imp?cid=ce94441291d24e68a8636d67cac53e7f&city=Kherson&ckv=2&country_code=UA&cppck=BC07B&current_consent_status=dnt&dev=x86_64&exclude_adgroups=14131c6800644268adba6677bc1943e1%2C8ea2fab7329743478559433103850765%2Cba139a81429e44a99c13257cc150da00%2Cc8de848595f444cdbf900c6e02ca3b7e&gdpr_applies=0&id=1439b3b5a9e8459796e82ec2d872a0f9&is_mraid=0&os=iOS&osv=14.4.0&priority=11&req=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&reqt=1615978215.0&rev=0.000100&udid=ifv%3A284487A5-0426-47D5-8F9C-0F257E0B712B&video_type="],
            "x-after-load-url": "https://cb.mopub.com/load?account_id=ec3ab052690d42da8d0f7c390bb9fbb2&adgroup_id=8ea2fab7329743478559433103850765&adgroup_priority=11&adgroup_type=non_gtee&adunit_format=Banner&adunit_id=1439b3b5a9e8459796e82ec2d872a0f9&app_id=5142930d5bc7410894a4666721b11ebb&campaign_id=423148dcbd5a4c1fb546666fa2519571&cid=ce94441291d24e68a8636d67cac53e7f&cluster=atla&connection_type=2&country=UA&hostname=atla-ihp-24-sr1.prod.twttr.net&is_mraid=0&log_attempt=1&logged_server_side=0&os=iOS&request_id=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&request_ts_ms=1615978215&rev=0.000100&round_trip_count=0&sdk_version=5.16.1&load_result=%%LOAD_RESULT%%&load_duration_ms=%%LOAD_DURATION_MS%%",
            "clicktrackers": ["https://ads.mopub.com/m/aclk?cid=ce94441291d24e68a8636d67cac53e7f&city=Kherson&ckv=2&country_code=UA&cppck=B810E&current_consent_status=dnt&dev=x86_64&exclude_adgroups=14131c6800644268adba6677bc1943e1%2C8ea2fab7329743478559433103850765%2Cba139a81429e44a99c13257cc150da00%2Cc8de848595f444cdbf900c6e02ca3b7e&gdpr_applies=0&id=1439b3b5a9e8459796e82ec2d872a0f9&is_mraid=0&os=iOS&osv=14.4.0&priority=11&req=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&reqt=1615978215.0&rev=0&udid=ifv%3A284487A5-0426-47D5-8F9C-0F257E0B712B&video_type="],
            "x-before-load-url": "",
            "x-ad-timeout-ms": 10000
        },
        "content": "<!DOCTYPE html> <html> <head>  <!-- Adgroup is 8ea2fab7329743478559433103850765 -->  <meta http-equiv=\"Content-Security-Policy\" content=\"upgrade-insecure-requests\">  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">  <style type='text/css'> .mp_center { position: fixed; top: 50%; left: 50%; margin-left: -160px !important; margin-top: -25px !important; } </style>  <script type=\"text/javascript\"> function mopubFinishLoad(){ setTimeout(function() { window.location = 'mopub://finishLoad'; }, 0); } </script>  <script type=\"text/javascript\"> function webviewDidClose(){ if (typeof webviewDidCloseHelper == 'function') { webviewDidCloseHelper(); } } function webviewDidAppear(){  if(typeof trackImpressionHelper == 'function') { trackImpressionHelper(); }  if(typeof webviewDidAppearHelper == 'function') { webviewDidAppearHelper(); } } window.addEventListener(\"load\", function() { var links = document.getElementsByTagName('a'); for(var i=0; i < links.length; i++) { links[i].setAttribute('target','_blank'); } }, false); </script>  </head> <body style=\"margin:0;padding:0;\"> Test: Non-guaranteed Line Item <script type=\"text/javascript\"> if (typeof htmlWillCallFinishLoad == \"undefined\" || !htmlWillCallFinishLoad) { if(typeof mopubFinishLoad == 'function') { window.onload = mopubFinishLoad; } }  </script>  </body> </html>"
    }, {
        "metadata": {
            "x-refreshtime": 999999,
            "x-adtype": "html",
            "x-width": 320,
            "x-height": 50,
            "x-networktype": "custom",
            "x-creativeid": "4e92278ca8064787bc7b60b191d9cb67",
            "x-adgroupid": "ba139a81429e44a99c13257cc150da00",
            "vast-player-version": 1,
            "content-type": "text/html; charset=UTF-8",
            "x-browser-agent": 0,
            "x-banner-impression-min-ms": "0",
            "x-banner-impression-min-pixels": "1",
            "imptrackers": ["https://ads.mopub.com/m/imp?cid=4e92278ca8064787bc7b60b191d9cb67&city=Kherson&ckv=2&country_code=UA&cppck=2F72C&current_consent_status=dnt&dev=x86_64&exclude_adgroups=14131c6800644268adba6677bc1943e1%2C8ea2fab7329743478559433103850765%2Cba139a81429e44a99c13257cc150da00%2Cc8de848595f444cdbf900c6e02ca3b7e&gdpr_applies=0&id=1439b3b5a9e8459796e82ec2d872a0f9&is_mraid=0&os=iOS&osv=14.4.0&priority=11&req=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&reqt=1615978215.0&rev=0.000100&udid=ifv%3A284487A5-0426-47D5-8F9C-0F257E0B712B&video_type="],
            "x-after-load-url": "https://cb.mopub.com/load?account_id=ec3ab052690d42da8d0f7c390bb9fbb2&adgroup_id=ba139a81429e44a99c13257cc150da00&adgroup_priority=11&adgroup_type=network&adunit_format=Banner&adunit_id=1439b3b5a9e8459796e82ec2d872a0f9&app_id=5142930d5bc7410894a4666721b11ebb&campaign_id=423148dcbd5a4c1fb546666fa2519571&cid=4e92278ca8064787bc7b60b191d9cb67&cluster=atla&connection_type=2&country=UA&hostname=atla-ihp-24-sr1.prod.twttr.net&is_mraid=0&log_attempt=1&logged_server_side=0&os=iOS&request_id=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&request_ts_ms=1615978215&rev=0.000100&round_trip_count=0&sdk_version=5.16.1&load_result=%%LOAD_RESULT%%&load_duration_ms=%%LOAD_DURATION_MS%%",
            "clicktrackers": ["https://ads.mopub.com/m/aclk?cid=4e92278ca8064787bc7b60b191d9cb67&city=Kherson&ckv=2&country_code=UA&cppck=FEBEF&current_consent_status=dnt&dev=x86_64&exclude_adgroups=14131c6800644268adba6677bc1943e1%2C8ea2fab7329743478559433103850765%2Cba139a81429e44a99c13257cc150da00%2Cc8de848595f444cdbf900c6e02ca3b7e&gdpr_applies=0&id=1439b3b5a9e8459796e82ec2d872a0f9&is_mraid=0&os=iOS&osv=14.4.0&priority=11&req=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&reqt=1615978215.0&rev=0&udid=ifv%3A284487A5-0426-47D5-8F9C-0F257E0B712B&video_type="],
            "x-before-load-url": "",
            "x-ad-timeout-ms": 10000
        },
        "content": "<!DOCTYPE html> <html> <head>  <!-- Adgroup is ba139a81429e44a99c13257cc150da00 -->  <meta http-equiv=\"Content-Security-Policy\" content=\"upgrade-insecure-requests\">  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">  <style type='text/css'> .mp_center { position: fixed; top: 50%; left: 50%; margin-left: -160px !important; margin-top: -25px !important; } </style>  <script type=\"text/javascript\"> function mopubFinishLoad(){ setTimeout(function() { window.location = 'mopub://finishLoad'; }, 0); } </script>  <script type=\"text/javascript\"> function webviewDidClose(){ if (typeof webviewDidCloseHelper == 'function') { webviewDidCloseHelper(); } } function webviewDidAppear(){  if(typeof trackImpressionHelper == 'function') { trackImpressionHelper(); }  if(typeof webviewDidAppearHelper == 'function') { webviewDidAppearHelper(); } } window.addEventListener(\"load\", function() { var links = document.getElementsByTagName('a'); for(var i=0; i < links.length; i++) { links[i].setAttribute('target','_blank'); } }, false); </script>  </head> <body style=\"margin:0;padding:0;\"> Test: Network Line Item #1 <script type=\"text/javascript\"> if (typeof htmlWillCallFinishLoad == \"undefined\" || !htmlWillCallFinishLoad) { if(typeof mopubFinishLoad == 'function') { window.onload = mopubFinishLoad; } }  </script>  </body> </html>"
    }, {
        "metadata": {
            "x-refreshtime": 999999,
            "x-adtype": "html",
            "x-width": 320,
            "x-height": 50,
            "x-creativeid": "3dfb7759de954184946955092ff87a46",
            "x-adgroupid": "c8de848595f444cdbf900c6e02ca3b7e",
            "vast-player-version": 1,
            "content-type": "text/html; charset=UTF-8",
            "x-browser-agent": 0,
            "x-banner-impression-min-ms": "0",
            "x-banner-impression-min-pixels": "1",
            "imptrackers": ["https://ads.mopub.com/m/imp?cid=3dfb7759de954184946955092ff87a46&city=Kherson&ckv=2&country_code=UA&cppck=57D16&current_consent_status=dnt&dev=x86_64&exclude_adgroups=14131c6800644268adba6677bc1943e1%2C8ea2fab7329743478559433103850765%2Cba139a81429e44a99c13257cc150da00%2Cc8de848595f444cdbf900c6e02ca3b7e&gdpr_applies=0&id=1439b3b5a9e8459796e82ec2d872a0f9&is_mraid=0&os=iOS&osv=14.4.0&priority=11&req=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&reqt=1615978215.0&rev=0.000100&udid=ifv%3A284487A5-0426-47D5-8F9C-0F257E0B712B&video_type="],
            "x-after-load-url": "https://cb.mopub.com/load?account_id=ec3ab052690d42da8d0f7c390bb9fbb2&adgroup_id=c8de848595f444cdbf900c6e02ca3b7e&adgroup_priority=11&adgroup_type=non_gtee&adunit_format=Banner&adunit_id=1439b3b5a9e8459796e82ec2d872a0f9&app_id=5142930d5bc7410894a4666721b11ebb&campaign_id=423148dcbd5a4c1fb546666fa2519571&cid=3dfb7759de954184946955092ff87a46&cluster=atla&connection_type=2&country=UA&hostname=atla-ihp-24-sr1.prod.twttr.net&is_mraid=0&log_attempt=1&logged_server_side=0&os=iOS&request_id=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&request_ts_ms=1615978215&rev=0.000100&round_trip_count=0&sdk_version=5.16.1&load_result=%%LOAD_RESULT%%&load_duration_ms=%%LOAD_DURATION_MS%%",
            "clicktrackers": ["https://ads.mopub.com/m/aclk?cid=3dfb7759de954184946955092ff87a46&city=Kherson&ckv=2&country_code=UA&cppck=78024&current_consent_status=dnt&dev=x86_64&exclude_adgroups=14131c6800644268adba6677bc1943e1%2C8ea2fab7329743478559433103850765%2Cba139a81429e44a99c13257cc150da00%2Cc8de848595f444cdbf900c6e02ca3b7e&gdpr_applies=0&id=1439b3b5a9e8459796e82ec2d872a0f9&is_mraid=0&os=iOS&osv=14.4.0&priority=11&req=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&reqt=1615978215.0&rev=0&udid=ifv%3A284487A5-0426-47D5-8F9C-0F257E0B712B&video_type="],
            "x-before-load-url": "",
            "x-ad-timeout-ms": 10000
        },
        "content": "<!DOCTYPE html> <html> <head>  <!-- Adgroup is c8de848595f444cdbf900c6e02ca3b7e -->  <meta http-equiv=\"Content-Security-Policy\" content=\"upgrade-insecure-requests\">  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">  <style type='text/css'> .mp_center { position: fixed; top: 50%; left: 50%; margin-left: -160px !important; margin-top: -25px !important; } </style>  <script type=\"text/javascript\"> function mopubFinishLoad(){ setTimeout(function() { window.location = 'mopub://finishLoad'; }, 0); } </script>  <script type=\"text/javascript\"> function webviewDidClose(){ if (typeof webviewDidCloseHelper == 'function') { webviewDidCloseHelper(); } } function webviewDidAppear(){  if(typeof trackImpressionHelper == 'function') { trackImpressionHelper(); }  if(typeof webviewDidAppearHelper == 'function') { webviewDidAppearHelper(); } } window.addEventListener(\"load\", function() { var links = document.getElementsByTagName('a'); for(var i=0; i < links.length; i++) { links[i].setAttribute('target','_blank'); } }, false); </script>  </head> <body style=\"margin:0;padding:0;\"> Test: Non-guaranteed Line Item #2 <script type=\"text/javascript\"> if (typeof htmlWillCallFinishLoad == \"undefined\" || !htmlWillCallFinishLoad) { if(typeof mopubFinishLoad == 'function') { window.onload = mopubFinishLoad; } }  </script>  </body> </html>"
    }],
    "x-request-id": "e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075",
    "x-next-url": "https://ads.mopub.com/m/ad?ac=0&adunit=03bf5f24c513443b8416d9a11069e310&ats=0&av=1.0&backoff_reason=filled&bundle=vdev.TestMoPubWaterfall&ch=100&cppck=C2667&ct=2&current_consent_status=dnt&cw=640&dn=x86_64&dnt=1&exclude=14131c6800644268adba6677bc1943e1%2C8ea2fab7329743478559433103850765%2Cba139a81429e44a99c13257cc150da00%2Cc8de848595f444cdbf900c6e02ca3b7e&force_gdpr_applies=0&gdpr_applies=0&h=1792&id=1439b3b5a9e8459796e82ec2d872a0f9&ifv=284487A5-0426-47D5-8F9C-0F257E0B712B&las=unknown&mid=CD702A97-9487-457F-9DF2-D17ABD3D2257&mr=1&nv=5.16.1&o=p&os=ios&request_id=e88dd8d700d544ec9c367a9e77f7f39d_0063fb6f00720075&rtc=1&sc=2.0&tas=not_determined&tqr=YFHe57dtnp5oKx_-jsRzGSa1d5FIB8W1Y-Qp0w&v=8&vv=4&vver=1.3.4-Mopub&w=828&z=%2B0200",
    "adunit-format": "banner",
    "backoff_ms": "0",
    "backoff_reason": "filled"
}
Screenshot 2021-03-17 at 13 42 18

In this response, you can see that Non-Guaranteed line items are shipped alongside Network Line items. It means that we do not break the current prebid behavior. Line items for Prebid Adapters will be processed in the same way as Line Items for PUC. But yes, they will take part in the Primary Ad Server Waterfall in any case. Publishers could set the priority of prebid Line items higher than 12, however, this is not obligatory. And MoPub doesn’t recommend it. So all other publisher’s ad networks including MoPub Marketplace and Advanced bidding will compete with prebid line items respectively to their CPM. The same behavior makes sense for GAM as well.

Please, let me know if I missed something important in this configuration.

YuriyVelichkoOpenX commented 3 years ago

@bszekely1, et al

What do you think about the necessity of an Ad Indicator?

So far I see two strict requirements from Apple and IAB about the necessity of Ad Indicator for:

How do you think, does it makes sense to add the Ad Indicator to all Ad Views? Apollo SDK used an ad indicator which is similar to AdChoices, however, it wasn't AdChoices.

As I already told on the call I would like to keep this functionality but redesign it. For instance to show the ⓘ button like a lot of SDKs do and show some modal dialog with the message about this ad.

Such disclosure looks reasonable. How do you think in general? The concrete can be discussed later.

YuriyVelichkoOpenX commented 3 years ago

It looks strange but I checked latest MoPub, Google, ironSource, inMobi, Smaato SDKs and did not find any Ad Indicator icons. I had seen it earlier but didn't save screenshots. All I have now is an old screenshot of Unity SDK:

image_ image

So maybe it doesn't make sense to add it.

bszekely1 commented 3 years ago

After doing some more research, it appears several of the SDKs out there require it for Native ads only since native ads blend in with the natural elements of the app / page. Here is a link describing this requirement for Google Ad Manager Native only: https://support.google.com/admanager/answer/6075370?hl=en https://developers.mopub.com/publishers/mediation/networks/google/#adchoices-options https://support.inmobi.com/monetize/android-guidelines/native-ads-for-android

They all reference Native only.

Lets hold off completely on adding a notification, even on Native for now. We can rethink it later when we have what we want to put there, if anything at all.

YuriyVelichkoOpenX commented 3 years ago

Dependency Hell with new and old versions of GAM and MoPub SDKs

Background

Recently Google and MoPub introduced new versions of their SDKs, which have critical changes in API.

The Rendering Module, in particular GAM Event Handlers and MoPub Adapters, has binary dependencies to these SDKs.

Note: later in this comment GAM Event Handlers and MoPub Adapters will be called just Dependency Handlers.

It means that we can't support both versions of third-party SDK in the single release of the rendering module package. However, we might face with situation when some publishers have the new version of GAM or MoPub SDK and some publishers have a previous version. So we should develop our strategy for this situation.

Below in this note, you will find the possible options, their pros, and cons and the final proposed approach.

Spoiler: for the open beta release we are going to support only the latest versions of GAM and MoPub SDKs in Dependency Handlers.

Options

We believe that we should think about the long-term strategy from the very beginning. That is why we considered several approaches in order to develop the best one for our project.

Unified Handler + Versioned Dependency Handlers

The codebase of Dependency Handlers could be split into two parts:

It means that we can split it into two libraries

It will give us the ability to support different versions of primary ad server SDKs by supporting several Wrapper frameworks, respectively to the SDK version.

Technically, it is the most correct way. However, it is pretty complex in terms of supporting and deploying. Because it adds new dependencies, which should be added into the dependency managers ecosystem and which should be supported by the community. One more issue with this approach is the maintaining of the protocol between Handler and Wrapper parts, which will be actual for all Wrapper's versions.

Versioned Dependency Handlers

Using different branches we can provide and support different versions of handlers:

This approach looks simpler from the support perspective, however, it also has problems:

Dependency Handlers which support only the latest SDK version

This is the most easiest and convenient way from the support perspective. However, we will have to push all publishers to use the particular SDK version. And publishers often do not like this.

The Proposed Approach

As we discussed at the working sessions, the best strategy, in this case, could look like this:

So the proposed approach is a mix of second and third options from this comment. It looks like the most maintainable one and should cover the future issues related to the releasing of new versions of primary ad server SDKs. But still, we will have to maintain the protocol between the rendering module and previous Dependency Handlers until it makes sense.

TBD

If anyone has objections, corrections, or better ideas - let us know.

YuriyVelichkoOpenX commented 3 years ago

Here is an updated demo app for inspecting the ad responses from GAM, AdMob, MoPub

TestMoPubWaterfall.zip

YuriyVelichkoOpenX commented 3 years ago

@bszekely1 @yoalex5

I've checked how GAM supports viewability vendors and created test line item with custom vendor. Here is a screenshot of the Charles logs

Screenshot 2021-05-07 at 17 02 54

We can support the same functionality as GAM. Publishers will have to provide OM Tag and other params and we will apply them. The stored request will be the best place for these params.

bszekely1 commented 3 years ago

We should put a little thought into the workflow then. For rendering, we would need a Prebid Stored Request echoback feature like we did for video but specific for OMID signaling. Lets make a list of all the partners we wish to support and their tag configurations, then decide how to implement in PBS.

YuriyVelichkoOpenX commented 3 years ago

Unfortunately, I haven't found a good way how to get the OM data from the ad response. If to talk about video ads, Google places OM tag not into the VAST but into the own wrapper. Here is an example of the ad response with video creative:

Screenshot 2021-05-25 at 18 14 39

the ad response is a huge html file. The VAST tag of the actual creative, as well as OM tag, is placed in it.

Until we can't get the OM tag from PBS we can provide the respective API for the SDK. The OM tag is a pretty constant thing and publishers shouldn't need to change it frequently.

In any case, I suggest considering the support of the publisher's OM tags for the upcoming releases

YuriyVelichkoOpenX commented 3 years ago

Here is a list of imported specs about how rendering module works:

Reading the docs keep in mind two things. Some of them are outdated and the details of the current behaviour can be different. Some of them were written as “intend to implement specs” so you can see transition info which describes the motivation and objectives of particular features.