From 9fbb34d5e42c86f8cb6955b815a86f73e74fa4a0 Mon Sep 17 00:00:00 2001 From: Jono Sligh Date: Thu, 5 Sep 2024 10:53:09 -0500 Subject: [PATCH 01/23] Updated for version 2.3.0 --- .../EventHandlers.xcodeproj/project.pbxproj | 24 +++++++++---------- PrebidMobile.podspec | 2 +- PrebidMobile.xcodeproj/project.pbxproj | 8 +++---- PrebidMobile/Constants.swift | 2 +- PrebidMobileAdMobAdapters.podspec | 4 ++-- PrebidMobileGAMEventHandlers.podspec | 4 ++-- PrebidMobileMAXAdapters.podspec | 4 ++-- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/EventHandlers/EventHandlers.xcodeproj/project.pbxproj b/EventHandlers/EventHandlers.xcodeproj/project.pbxproj index 46171f223..06e83745a 100644 --- a/EventHandlers/EventHandlers.xcodeproj/project.pbxproj +++ b/EventHandlers/EventHandlers.xcodeproj/project.pbxproj @@ -1218,7 +1218,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2.2.3; + CURRENT_PROJECT_VERSION = 2.3.0; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1232,7 +1232,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.3.0; MODULEMAP_FILE = ""; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileGAMEventHandlers; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -1253,7 +1253,7 @@ CLANG_ENABLE_MODULES = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2.2.3; + CURRENT_PROJECT_VERSION = 2.3.0; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1267,7 +1267,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.3.0; MODULEMAP_FILE = ""; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileGAMEventHandlers; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -1287,7 +1287,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2.2.3; + CURRENT_PROJECT_VERSION = 2.3.0; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1302,7 +1302,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.3.0; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileMAXAdapters; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -1322,7 +1322,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2.2.3; + CURRENT_PROJECT_VERSION = 2.3.0; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1337,7 +1337,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.3.0; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileMAXAdapters; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -1399,7 +1399,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2.2.3; + CURRENT_PROJECT_VERSION = 2.3.0; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -1413,7 +1413,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.3.0; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileAdMobAdapters; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = None; @@ -1434,7 +1434,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2.2.3; + CURRENT_PROJECT_VERSION = 2.3.0; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -1448,7 +1448,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.3.0; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileAdMobAdapters; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = None; diff --git a/PrebidMobile.podspec b/PrebidMobile.podspec index 53f82a671..d3df2e1c0 100644 --- a/PrebidMobile.podspec +++ b/PrebidMobile.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "PrebidMobile" - s.version = "2.2.3" + s.version = "2.3.0" s.summary = "PrebidMobile is a lightweight framework that integrates directly with Prebid Server." s.description = <<-DESC diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index ce63f7648..bde3a5074 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -4505,7 +4505,7 @@ CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; - CURRENT_PROJECT_VERSION = 2.2.3; + CURRENT_PROJECT_VERSION = 2.3.0; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -4524,7 +4524,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.3.0; MODULEMAP_FILE = PrebidMobile/BuildFiles/PrebidMobile.modulemap; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -4541,7 +4541,7 @@ CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; - CURRENT_PROJECT_VERSION = 2.2.3; + CURRENT_PROJECT_VERSION = 2.3.0; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -4560,7 +4560,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.3.0; MODULEMAP_FILE = PrebidMobile/BuildFiles/PrebidMobile.modulemap; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; diff --git a/PrebidMobile/Constants.swift b/PrebidMobile/Constants.swift index 4f492115f..e0b3d4471 100644 --- a/PrebidMobile/Constants.swift +++ b/PrebidMobile/Constants.swift @@ -18,7 +18,7 @@ import UIKit public let PrebidLocalCacheIdKey = "hb_cache_id_local" @objc public class Constants: NSObject { - @objc public static let PREBID_VERSION = "2.2.3" + @objc public static let PREBID_VERSION = "2.3.0" } extension String { diff --git a/PrebidMobileAdMobAdapters.podspec b/PrebidMobileAdMobAdapters.podspec index 01f1d3a54..3e39d782b 100644 --- a/PrebidMobileAdMobAdapters.podspec +++ b/PrebidMobileAdMobAdapters.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "PrebidMobileAdMobAdapters" - s.version = "2.2.3" + s.version = "2.3.0" s.summary = "The bridge between PrebidMobile SDK and GMA SDK." s.description = "AdMob Adatpers manages rendering of Prebid ads in the case of integration with AdMob as a Primary Ad Server." @@ -40,7 +40,7 @@ Pod::Spec.new do |s| s.static_framework = true - s.dependency 'PrebidMobile', '2.2.3' + s.dependency 'PrebidMobile', '2.3.0' s.dependency 'Google-Mobile-Ads-SDK' end diff --git a/PrebidMobileGAMEventHandlers.podspec b/PrebidMobileGAMEventHandlers.podspec index ce79c2b05..9b664ddc2 100644 --- a/PrebidMobileGAMEventHandlers.podspec +++ b/PrebidMobileGAMEventHandlers.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "PrebidMobileGAMEventHandlers" - s.version = "2.2.3" + s.version = "2.3.0" s.summary = "The bridge between PrebidMobile SDK and GMA SDK." s.description = "GAM Event Handlers manages rendering of Prebid or GAM ads respectively to the winning bid." @@ -39,7 +39,7 @@ Pod::Spec.new do |s| s.source_files = 'EventHandlers/PrebidMobileGAMEventHandlers/**/*.{h,m,swift}' s.static_framework = true - s.dependency 'PrebidMobile', '2.2.3' + s.dependency 'PrebidMobile', '2.3.0' s.dependency 'Google-Mobile-Ads-SDK' end diff --git a/PrebidMobileMAXAdapters.podspec b/PrebidMobileMAXAdapters.podspec index a81ef68f3..d7a09173c 100644 --- a/PrebidMobileMAXAdapters.podspec +++ b/PrebidMobileMAXAdapters.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "PrebidMobileMAXAdapters" - s.version = "2.2.3" + s.version = "2.3.0" s.summary = "The bridge between PrebidMobile SDK and Applovin MAX SDK." s.description = "MAX Adapters manages rendering of Prebid or MAX ads respectively to the winning bid." @@ -39,7 +39,7 @@ Pod::Spec.new do |s| s.source_files = 'EventHandlers/PrebidMobileMAXAdapters/**/*.{h,m,swift}' s.static_framework = true - s.dependency 'PrebidMobile', '2.2.3' + s.dependency 'PrebidMobile', '2.3.0' s.dependency 'AppLovinSDK' end From a0dcb0f8aadfbb0596ee58078b24ba45eb3672d5 Mon Sep 17 00:00:00 2001 From: Jono Sligh <139150153+jsligh@users.noreply.github.com> Date: Wed, 25 Sep 2024 05:08:31 -0500 Subject: [PATCH 02/23] Fixed broken build (#1043) --- .../InternalTestApp.xcodeproj/project.pbxproj | 6 -- .../Utilities/SampleCustomRenderer.swift | 83 ------------------- PrebidMobile.xcodeproj/project.pbxproj | 4 + .../SampleCustomRenderer.swift | 2 +- .../TestUtils.xcodeproj/project.pbxproj | 12 --- 5 files changed, 5 insertions(+), 102 deletions(-) delete mode 100644 InternalTestApp/PrebidMobileDemoRendering/Utilities/SampleCustomRenderer.swift rename {tools/TestUtils/TestUtils/SamplePluginRegister => PrebidMobile/PrebidMobileRendering/PluginRenderer}/SampleCustomRenderer.swift (99%) diff --git a/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj b/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj index f923c4532..d2cf1fe9b 100644 --- a/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj +++ b/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj @@ -27,8 +27,6 @@ 34FC0CF624ADD5640045553E /* PrebidGAMRewardedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34FC0CF524ADD5640045553E /* PrebidGAMRewardedController.swift */; }; 3C28C9D82C35713B00D0A7DB /* CustomRendererInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C28C9D72C35713B00D0A7DB /* CustomRendererInterstitialController.swift */; }; 3C28C9D92C35713B00D0A7DB /* CustomRendererInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C28C9D72C35713B00D0A7DB /* CustomRendererInterstitialController.swift */; }; - 3C28C9DB2C357E3500D0A7DB /* SampleCustomRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C28C9DA2C357E3500D0A7DB /* SampleCustomRenderer.swift */; }; - 3C28C9DC2C357E3500D0A7DB /* SampleCustomRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C28C9DA2C357E3500D0A7DB /* SampleCustomRenderer.swift */; }; 3CC4A3E92C11F96800B97128 /* CustomRendererBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CC4A3E82C11F96800B97128 /* CustomRendererBannerController.swift */; }; 3CC4A3EA2C11F96800B97128 /* CustomRendererBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CC4A3E82C11F96800B97128 /* CustomRendererBannerController.swift */; }; 457FD34BA45C3840CC31A8F5 /* Pods_InternalTestApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C11D97034E668BBFDD0DDCA /* Pods_InternalTestApp.framework */; }; @@ -381,7 +379,6 @@ 35F94D131F93F85D00CF46DB /* InternalTestAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InternalTestAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 35F94D171F93F85D00CF46DB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3C28C9D72C35713B00D0A7DB /* CustomRendererInterstitialController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRendererInterstitialController.swift; sourceTree = ""; }; - 3C28C9DA2C357E3500D0A7DB /* SampleCustomRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleCustomRenderer.swift; sourceTree = ""; }; 3CC4A3E82C11F96800B97128 /* CustomRendererBannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRendererBannerController.swift; sourceTree = ""; }; 530E722B292FACB20025B44D /* UIImageView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Extensions.swift"; sourceTree = ""; }; 5397BD132936185400ABDA22 /* PrebidOriginalAPIDisplayBannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidOriginalAPIDisplayBannerController.swift; sourceTree = ""; }; @@ -847,7 +844,6 @@ 34B10A2F255017DE00B5FE09 /* ThreadCheckingButton.swift */, 34C25816255D69E1000A4A73 /* EventReportContainer.swift */, 530E722B292FACB20025B44D /* UIImageView+Extensions.swift */, - 3C28C9DA2C357E3500D0A7DB /* SampleCustomRenderer.swift */, ); path = Utilities; sourceTree = ""; @@ -1577,7 +1573,6 @@ 53ED2FE829798104007D13EE /* PrebidBannerController.swift in Sources */, 53ED2FE929798104007D13EE /* PrebidBannerConfigurationController.swift in Sources */, 53ED2FEA29798104007D13EE /* TestCase.swift in Sources */, - 3C28C9DC2C357E3500D0A7DB /* SampleCustomRenderer.swift in Sources */, 53ED2FEB29798104007D13EE /* PrebidAdMobNativeViewController.swift in Sources */, 53ED2FEC29798104007D13EE /* PrebidInterstitialController.swift in Sources */, 53ED2FED29798104007D13EE /* PrebidOriginalAPIDisplayInterstitialController.swift in Sources */, @@ -1674,7 +1669,6 @@ 3493022224740BEE004A6086 /* PrebidBannerController.swift in Sources */, 5BDB85DA2739794900A529F6 /* PrebidBannerConfigurationController.swift in Sources */, 5B3EEDE52101FB8800BAA0C4 /* TestCase.swift in Sources */, - 3C28C9DB2C357E3500D0A7DB /* SampleCustomRenderer.swift in Sources */, 92C475122796FF8400C26E27 /* PrebidAdMobNativeViewController.swift in Sources */, 34FC0CF424ADD3A80045553E /* PrebidInterstitialController.swift in Sources */, 5397BD26293760F500ABDA22 /* PrebidOriginalAPIDisplayInterstitialController.swift in Sources */, diff --git a/InternalTestApp/PrebidMobileDemoRendering/Utilities/SampleCustomRenderer.swift b/InternalTestApp/PrebidMobileDemoRendering/Utilities/SampleCustomRenderer.swift deleted file mode 100644 index 5fd7d86b3..000000000 --- a/InternalTestApp/PrebidMobileDemoRendering/Utilities/SampleCustomRenderer.swift +++ /dev/null @@ -1,83 +0,0 @@ -/*   Copyright 2018-2021 Prebid.org, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ -import Foundation -import PrebidMobile -import UIKit - -public class SampleCustomRenderer: NSObject, PrebidMobilePluginRenderer { - - public let name = "SampleCustomRenderer" - - public let version = "1.0.0" - - public var data: [AnyHashable: Any]? = nil - - private var adViewManager: PBMAdViewManager? - - public func isSupportRendering(for format: AdFormat?) -> Bool { - AdFormat.allCases.contains(where: { $0 == format }) - } - - public func setupBid(_ bid: Bid, adConfiguration: AdUnitConfig, connection: PrebidServerConnectionProtocol) { - - } - - public func createBannerAdView(with frame: CGRect, bid: Bid, adConfiguration: AdUnitConfig, - connection: PrebidServerConnectionProtocol, adViewDelegate: (any PBMAdViewDelegate)?) { - DispatchQueue.main.async { - if let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }), - let rootView = window.rootViewController?.view { - if let prebidBannerView = self.findPrebidBannerView(in: rootView) { - print("Found PrebidBannerView: \(prebidBannerView)") - - let label = UILabel() - label.text = "Prebid SDK - Custom Renderer" - label.textAlignment = .center - label.textColor = .black - label.backgroundColor = .yellow - label.translatesAutoresizingMaskIntoConstraints = false - - prebidBannerView.addSubview(label) - - NSLayoutConstraint.activate([ - label.centerXAnchor.constraint(equalTo: prebidBannerView.centerXAnchor), - label.centerYAnchor.constraint(equalTo: prebidBannerView.centerYAnchor), - label.widthAnchor.constraint(equalTo: prebidBannerView.widthAnchor), - label.heightAnchor.constraint(equalTo: prebidBannerView.heightAnchor) - ]) - - } else { - print("PrebidBannerView not found.") - } - } - } - } - - private func findPrebidBannerView(in view: UIView) -> UIView? { - if view.accessibilityIdentifier == "PrebidBannerView" { - return view - } - for subview in view.subviews { - if let foundView = findPrebidBannerView(in: subview) { - return foundView - } - } - return nil - } - - public func createInterstitialController(bid: Bid, adConfiguration: AdUnitConfig, connection: PrebidServerConnectionProtocol, - adViewManagerDelegate adViewDelegate: InterstitialController?, videoControlsConfig: VideoControlsConfiguration?) { - } -} diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index bde3a5074..fa064f43b 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -780,6 +780,7 @@ A908694629E05F7900B37479 /* PrebidRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A908694529E05F7900B37479 /* PrebidRenderer.swift */; }; A9750D7A2ABB9A300066E4E6 /* PluginEventListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9750D792ABB9A300066E4E6 /* PluginEventListener.swift */; }; A9A3396B2AC57193006AD0E7 /* PBMAdViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A3396A2AC57193006AD0E7 /* PBMAdViewDelegate.swift */; }; + F95DA2072C87567D0068A2BB /* SampleCustomRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F95DA2062C87567D0068A2BB /* SampleCustomRenderer.swift */; }; FA5AD5E42271FA4100C8F274 /* ConstantsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5AD5E32271FA4100C8F274 /* ConstantsTest.swift */; }; FA9D7F2722E8A83D006FCBEF /* AdViewUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA9D7F2622E8A83D006FCBEF /* AdViewUtilsTests.swift */; }; FAA29904242D1C27002ACBF2 /* TargetingObjCTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FAA29903242D1C27002ACBF2 /* TargetingObjCTests.m */; }; @@ -1672,6 +1673,7 @@ A908694529E05F7900B37479 /* PrebidRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidRenderer.swift; sourceTree = ""; }; A9750D792ABB9A300066E4E6 /* PluginEventListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginEventListener.swift; sourceTree = ""; }; A9A3396A2AC57193006AD0E7 /* PBMAdViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBMAdViewDelegate.swift; sourceTree = ""; }; + F95DA2062C87567D0068A2BB /* SampleCustomRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleCustomRenderer.swift; sourceTree = ""; }; FA4A88432497A99D00FDCBB6 /* Swizzling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Swizzling.swift; sourceTree = ""; }; FA5AD5E32271FA4100C8F274 /* ConstantsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsTest.swift; sourceTree = ""; }; FA85F9B4264946FC00B8BE72 /* TestUtils.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TestUtils.xcodeproj; path = ../tools/TestUtils/TestUtils.xcodeproj; sourceTree = ""; }; @@ -3184,6 +3186,7 @@ A908694029E05E8D00B37479 /* PluginRenderer */ = { isa = PBXGroup; children = ( + F95DA2062C87567D0068A2BB /* SampleCustomRenderer.swift */, A908694129E05EAF00B37479 /* PrebidMobilePluginRegister.swift */, A908694329E05ED500B37479 /* PrebidMobilePluginRenderer.swift */, A9750D792ABB9A300066E4E6 /* PluginEventListener.swift */, @@ -4154,6 +4157,7 @@ 537B6518283372FD008AE9D1 /* PathBuilder.swift in Sources */, FAEE4D1A262DC2B200AD9966 /* NativeAd.swift in Sources */, 5BC37A1B271F1D0000444D5E /* Prebid.swift in Sources */, + F95DA2072C87567D0068A2BB /* SampleCustomRenderer.swift in Sources */, 5BC379A8271F1D0000444D5E /* PBMModalViewController.m in Sources */, 5BC37AC3271F1D0100444D5E /* PBMURLComponents.m in Sources */, 5BC378D3271F1CFF00444D5E /* PBMORTBAbstract.m in Sources */, diff --git a/tools/TestUtils/TestUtils/SamplePluginRegister/SampleCustomRenderer.swift b/PrebidMobile/PrebidMobileRendering/PluginRenderer/SampleCustomRenderer.swift similarity index 99% rename from tools/TestUtils/TestUtils/SamplePluginRegister/SampleCustomRenderer.swift rename to PrebidMobile/PrebidMobileRendering/PluginRenderer/SampleCustomRenderer.swift index f27d68dbd..8ff5a684e 100644 --- a/tools/TestUtils/TestUtils/SamplePluginRegister/SampleCustomRenderer.swift +++ b/PrebidMobile/PrebidMobileRendering/PluginRenderer/SampleCustomRenderer.swift @@ -12,8 +12,8 @@  See the License for the specific language governing permissions and  limitations under the License.  */ + import Foundation -import PrebidMobile import UIKit public class SampleCustomRenderer: NSObject, PrebidMobilePluginRenderer { diff --git a/tools/TestUtils/TestUtils.xcodeproj/project.pbxproj b/tools/TestUtils/TestUtils.xcodeproj/project.pbxproj index 2663a5f43..a9dcc57c2 100644 --- a/tools/TestUtils/TestUtils.xcodeproj/project.pbxproj +++ b/tools/TestUtils/TestUtils.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 3CD0ADF12C51274A006CDA6B /* SampleCustomRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CD0ADF02C51274A006CDA6B /* SampleCustomRenderer.swift */; }; 53AB8AB22851D9EF00837C70 /* OMSDK-Static_Prebidorg.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53AB8AB12851D9EF00837C70 /* OMSDK-Static_Prebidorg.xcframework */; }; 53AB8AB32851D9EF00837C70 /* OMSDK-Static_Prebidorg.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 53AB8AB12851D9EF00837C70 /* OMSDK-Static_Prebidorg.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 92C0CEA027BD411E00C480EB /* emptyResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = 92C0CE9F27BD411E00C480EB /* emptyResponse.json */; }; @@ -61,7 +60,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 3CD0ADF02C51274A006CDA6B /* SampleCustomRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleCustomRenderer.swift; sourceTree = ""; }; 537B669C283DFCCD008AE9D1 /* OMSDK_Prebidorg.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = OMSDK_Prebidorg.xcframework; path = ../../Frameworks/OMSDK_Prebidorg.xcframework; sourceTree = ""; }; 53AB8AB12851D9EF00837C70 /* OMSDK-Static_Prebidorg.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = "OMSDK-Static_Prebidorg.xcframework"; path = "../../Frameworks/OMSDK-Static_Prebidorg.xcframework"; sourceTree = ""; }; 92C0CE9F27BD411E00C480EB /* emptyResponse.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = emptyResponse.json; sourceTree = ""; }; @@ -114,14 +112,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 3CD0ADEF2C5126FD006CDA6B /* SamplePluginRegister */ = { - isa = PBXGroup; - children = ( - 3CD0ADF02C51274A006CDA6B /* SampleCustomRenderer.swift */, - ); - path = SamplePluginRegister; - sourceTree = ""; - }; 537B669B283DFCCD008AE9D1 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -151,7 +141,6 @@ FA9E5E0F2649420C0049B214 /* TestUtils */ = { isa = PBXGroup; children = ( - 3CD0ADEF2C5126FD006CDA6B /* SamplePluginRegister */, FAC853582649462E009DC0F3 /* Category */, FAC8536726494646009DC0F3 /* Responses */, FAC8537B26494647009DC0F3 /* Stubbing */, @@ -322,7 +311,6 @@ buildActionMask = 2147483647; files = ( FAC8535B2649462E009DC0F3 /* NSURLRequest+HTTPBodyTesting.m in Sources */, - 3CD0ADF12C51274A006CDA6B /* SampleCustomRenderer.swift in Sources */, FAC8539926494647009DC0F3 /* PBHTTPStubbingManager.m in Sources */, FAC8539A26494647009DC0F3 /* PBURLConnectionStub+NSURLSessionConfiguration.m in Sources */, FAC853652649463A009DC0F3 /* PBTestGlobal.m in Sources */, From a20b62eb661fbc2f8ff5ee1e29966b7fda2f456e Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Thu, 3 Oct 2024 16:52:03 +0300 Subject: [PATCH 03/23] Generate PrebidMobile Documentation (#1034) * feat: add jazzy configuration * feat: update settings * feat: update settings * feat: update .circleci/config.yml * feat: update .circleci/config.yml * feat: update .circleci/config.yml * feat: update .circleci/config.yml * feat: update * feat: add doc in comments * feat: update doc * feat: update README.md * feat: add upload-docs.yml * feat: update config * feat: update config * feat: update config * feat: update config * feat: update config * feat: update upload-docs-ios.yml * feat: update upload-docs-ios.yml * feat: update upload-docs-ios.yml * feat: update upload-docs-ios.yml * doc: use specific README * doc: update the Overview page * doc: small change to fix build * doc: small change to fix the build * Revert "doc: small change to fix build" This reverts commit 66065845c6eda03ef3af74b51dbf16e9eb7ca87e. * Revert "doc: small change to fix the build" This reverts commit 4b5ced487c04bcee37b404c9dee872b1550158b1. * feat: update upload-docs-ios.yml * feat: update upload-docs-ios.yml * doc: update README * doc: update README * fix build * fix: update supported GMA SDK version * fix test * fix tests * tests: clean build before running unit tests * feat: add check for operation with docs_new directory * feat: update workflow to abort if conditions are not met --------- Co-authored-by: Yuriy Velichko --- .circleci/config.yml | 1 - .github/workflows/upload-docs-ios.yml | 58 ++++ .jazzy.yaml | 63 +++++ .../PrebidAdMobMediationBaseAdapter.swift | 2 +- .../Sources/GAMUtils.swift | 2 +- .../Sources/PrebidMAXMediationAdapter.swift | 1 - .../PrebidAdMobBannerViewController.swift | 2 +- ...rebidAdMobInterstitialViewController.swift | 2 +- .../PrebidAdMobNativeViewController.swift | 2 +- .../PrebidAdMobRewardedViewController.swift | 2 +- .../MAX/PrebidMAXBannerController.swift | 2 +- .../MAX/PrebidMAXInterstitialController.swift | 2 +- .../MAX/PrebidMAXNativeController.swift | 2 +- .../MAX/PrebidMAXRewardedController.swift | 2 +- Podfile.lock | 12 +- PrebidMobile.xcodeproj/project.pbxproj | 8 + PrebidMobile/AdUnits/AdUnit.swift | 163 ++++++----- PrebidMobile/AdUnits/BannerAdUnit.swift | 11 + .../AdUnits/InstreamVideoAdUnit.swift | 7 +- PrebidMobile/AdUnits/InterstitialAdUnit.swift | 11 + .../AdUnits/MultiformatAdUnit/BidInfo.swift | 20 +- .../MultiformatAdUnit/PrebidAdUnit.swift | 27 +- .../MultiformatAdUnit/PrebidRequest.swift | 65 +++-- .../AdUnits/Native/ContextSubType.swift | 39 ++- PrebidMobile/AdUnits/Native/ContextType.swift | 9 +- PrebidMobile/AdUnits/Native/NativeAd.swift | 25 +- .../Native/NativeAdConfiguration.swift | 5 + .../AdUnits/Native/NativeAdDelegate.swift | 26 +- .../Native/NativeAdEventDelegate.swift | 20 +- .../AdUnits/Native/NativeAdMarkup.swift | 6 + .../AdUnits/Native/NativeAdMarkupAsset.swift | 4 + PrebidMobile/AdUnits/Native/NativeAsset.swift | 98 +++++-- PrebidMobile/AdUnits/Native/NativeData.swift | 4 + .../AdUnits/Native/NativeDataAssetType.swift | 44 ++- .../AdUnits/Native/NativeEventTracker.swift | 32 ++- .../Native/NativeEventTrackerResponse.swift | 5 + PrebidMobile/AdUnits/Native/NativeImage.swift | 6 + .../AdUnits/Native/NativeImageAssetType.swift | 9 + PrebidMobile/AdUnits/Native/NativeLink.swift | 5 + .../Native/NativeMarkupRequestObject.swift | 13 + .../AdUnits/Native/NativeRequest.swift | 35 ++- PrebidMobile/AdUnits/Native/NativeTitle.swift | 5 + .../AdUnits/Native/PlacementType.swift | 16 +- .../AdUnits/Parameters/BannerParameters.swift | 5 + .../AdUnits/Parameters/NativeParameters.swift | 24 ++ PrebidMobile/AdUnits/Parameters/Signals.swift | 168 +++++------ .../Parameters/SingleContainerInt.swift | 51 ++++ .../AdUnits/Parameters/VideoParameters.swift | 19 +- .../AdUnits/RewardedVideoAdUnit.swift | 13 + PrebidMobile/AdUnits/Utils.swift | 22 +- PrebidMobile/AdUnits/VideoAdUnit.swift | 10 + .../AdUnits/VideoInterstitialAdUnit.swift | 13 + .../ConfigurationAndTargeting/Gender.swift | 15 + .../ConfigurationAndTargeting/Prebid.swift | 80 ++++-- .../PrebidGAMVersionChecker.swift | 2 +- .../PrebidInitializationStatus.swift | 3 + .../ConfigurationAndTargeting/Targeting.swift | 261 +++++++++++------- PrebidMobile/ExternalUserId.swift | 29 +- PrebidMobile/Host.swift | 41 +-- .../PrebidMobileRendering/AdFormat.swift | 20 +- .../PrebidMobileRendering/AdPosition.swift | 48 ++-- .../AdTypes/AdView/Position.swift | 7 + .../AdView/VideoControlsConfiguration.swift | 53 ++-- .../Integrations/GAM/BannerEventHandler.swift | 3 + .../Prebid/Integrations/GAM/BannerView.swift | 85 +++++- .../Integrations/GAM/BannerViewDelegate.swift | 50 ++-- .../GAM/BaseInterstitialAdUnit.swift | 121 +++++++- .../GAM/InterstitialAdUnitDelegate.swift | 9 +- .../GAM/InterstitialRenderingAdUnit.swift | 33 ++- .../Integrations/GAM/RewardedAdUnit.swift | 44 +++ .../GAM/RewardedAdUnitDelegate.swift | 7 +- .../GAM/RewardedEventHandlerProtocol.swift | 4 +- .../MediationAPI/MediationBannerAdUnit.swift | 102 +++++-- .../MediationBaseInterstitialAdUnit.swift | 100 +++++-- .../MediationInterstitialAdUnit.swift | 19 +- .../MediationAPI/MediationNativeAdUnit.swift | 95 ++++++- .../MediationRewardedAdUnit.swift | 6 + .../PrebidMediationDelegate.swift | 24 +- PrebidMobile/ResultCode.swift | 39 ++- README.md | 2 - README_API_DOC.md | 9 + scripts/testPrebidMobile.sh | 6 + 82 files changed, 1857 insertions(+), 658 deletions(-) create mode 100644 .github/workflows/upload-docs-ios.yml create mode 100644 .jazzy.yaml create mode 100644 PrebidMobile/AdUnits/Parameters/SingleContainerInt.swift create mode 100644 README_API_DOC.md diff --git a/.circleci/config.yml b/.circleci/config.yml index 4f7a19ed3..e45bb6321 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -41,7 +41,6 @@ jobs: name: Run Smoke UI Tests command: scripts/testPrebidDemo.sh -ui -l - workflows: pr-check: jobs: diff --git a/.github/workflows/upload-docs-ios.yml b/.github/workflows/upload-docs-ios.yml new file mode 100644 index 000000000..8afe7b82d --- /dev/null +++ b/.github/workflows/upload-docs-ios.yml @@ -0,0 +1,58 @@ +name: Upload Docs iOS + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+' + - 'test-doc-*' + +jobs: + upload-docs: + + runs-on: macos-latest + + steps: + - name: Checkout current branch + uses: actions/checkout@v4 + - name: Install Jazzy + run: | + gem install jazzy + - name: Generate docs + run: | + jazzy + cp -r docs docs_new + - name: Checkout docs branch + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: docs + clean: false + - name: Commit documentation changes + run: | + MIN_SIZE=5120 + if [ -d docs_new ] && [ "$(du -s docs_new | cut -f1)" -ge "$MIN_SIZE" ]; then + rm -rf docs + cp -r docs_new docs + rm -rf docs_new + else + echo "docs_new directory either does not exist or is too small." + exit 1 + fi + + if [ "$(git status --porcelain | wc -l)" -gt 0 ]; then + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git status + TAG_NAME=${{ github.ref }} + TAG_NAME=${TAG_NAME#refs/tags/} + echo "Current tag: $TAG_NAME" + git add docs + git commit -m "Generate docs - $TAG_NAME" + else + echo "No changes to commit." + fi + - name: Push changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: docs diff --git a/.jazzy.yaml b/.jazzy.yaml new file mode 100644 index 000000000..4f3d949f5 --- /dev/null +++ b/.jazzy.yaml @@ -0,0 +1,63 @@ +# Ordered by output of `jazzy — help config` +output: docs +clean: true +xcodebuild_arguments: +- -workspace +- PrebidMobile.xcworkspace +- -scheme +- PrebidMobile +- CODE_SIGNING_ALLOWED = NO +author: Prebid +readme: README_API_DOC.md +exclude: + - PrebidMobile/Addendum/* + - PrebidMobile/AdUnits/BannerBasedAdUnitProtocol.swift + - PrebidMobile/AdUnits/VideoBasedAdUnitProtocol.swift + - PrebidMobile/CacheManagement/* + - PrebidMobile/ConfigurationAndTargeting/AgeUtils.swift + - PrebidMobile/ConfigurationAndTargeting/ClickbrowserType.swift + - PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift + - PrebidMobile/ConfigurationAndTargeting/PrebidSDKInitializer.swift + - PrebidMobile/ConfigurationAndTargeting/PrebidServerStatusRequester.swift + - PrebidMobile/ConfigurationAndTargeting/UserConsentDataManager.swift + - PrebidMobile/Constants.swift + - PrebidMobile/Dispatcher.swift + - PrebidMobile/ImageHelper.swift + - PrebidMobile/JSONConvertible.swift + - PrebidMobile/JsonDecodable.swift + - PrebidMobile/Logging/* + - PrebidMobile/StorageUtils.swift + - PrebidMobile/Tracker* + - PrebidMobile/UIViewExtension.swift + - PrebidMobile/Utils/* + - PrebidMobile/PrebidMobileRendering/AdTypes/AdView/AdConfiguration.swift + - PrebidMobile/PrebidMobileRendering/AdTypes/AdView/AdViewButtonDecorator.swift + - PrebidMobile/PrebidMobileRendering/AdTypes/AdView/HiddenWebViewManager.swift + - PrebidMobile/PrebidMobileRendering/Assets/PrebidImagesRepository.swift + - PrebidMobile/PrebidMobileRendering/AutoRefreshCountConfig.swift + - PrebidMobile/PrebidMobileRendering/Impression* + - PrebidMobile/PrebidMobileRendering/Networking* + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/AdLoadFlowControllerDelegate.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/BannerAdLoaderDelegate.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerEventInteractionDelegate.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerEventLoadingDelegate.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialEventInteractionDelegate.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialEventLoadingDelegate.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventInteractionDelegate.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventLoadingDelegate.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnitProtocol.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialEventHandlerProtocol.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationUtils.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationNativeUtils.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationConstants.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBidInfoWrapper.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/PBMStandaloneSDK/DemandResponseInfo.swift + - PrebidMobile/PrebidMobileRendering/Prebid/Integrations/PBMStandaloneSDK/EventHandlers/* + - PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/* + - PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/* + - PrebidMobile/PrebidMobileRendering/PrebidServerEventTracker.swift + - PrebidMobile/PrebidMobileRendering/ServerEvent.swift + - PrebidMobile/PrebidMobileRendering/ServerSideConfiguration/* + - PrebidMobile/PrebidMobileRendering/Skadn* + - PrebidMobile/PrebidMobileRendering/Utilities/* +theme: apple diff --git a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift index 96e3868a8..f58f7f9de 100644 --- a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift +++ b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift @@ -41,7 +41,7 @@ public class PrebidAdMobMediationBaseAdapter: NSObject, GADMediationAdapter { // Added for tests static func latestTestedGMAVersion() -> GADVersionNumber { - return GADVersionNumber(majorVersion: 11, minorVersion: 8, patchVersion: 0) + return GADVersionNumber(majorVersion: 11, minorVersion: 10, patchVersion: 0) } required public override init() { diff --git a/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift index ee2fc7e2a..1b73d07e8 100644 --- a/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift +++ b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift @@ -59,7 +59,7 @@ public class GAMUtils: NSObject { // Added for tests static func latestTestedGMAVersion() -> GADVersionNumber { - return GADVersionNumber(majorVersion: 11, minorVersion: 8, patchVersion: 0) + return GADVersionNumber(majorVersion: 11, minorVersion: 10, patchVersion: 0) } // MARK: Private Methods diff --git a/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter.swift b/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter.swift index 46114a86f..f98d35f78 100644 --- a/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter.swift +++ b/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter.swift @@ -45,7 +45,6 @@ public class PrebidMAXMediationAdapter: ALMediationAdapter { // TODO: Add Prebid SDK initialization logic completionHandler(.initializedUnknown, nil) - Targeting.shared.subjectToCOPPA = ALPrivacySettings.isAgeRestrictedUser() } public override var sdkVersion: String { diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobBannerViewController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobBannerViewController.swift index 400035961..a578149ff 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobBannerViewController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobBannerViewController.swift @@ -113,7 +113,7 @@ class PrebidAdMobBannerViewController: // imp[].ext.data if let adUnitContext = AppConfiguration.shared.adUnitContext { for dataPair in adUnitContext { - adUnit?.addContextData(dataPair.value, forKey: dataPair.key) + adUnit?.addContextData(key: dataPair.value, value: dataPair.key) } } diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobInterstitialViewController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobInterstitialViewController.swift index 150e08d0b..b89f154b2 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobInterstitialViewController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobInterstitialViewController.swift @@ -109,7 +109,7 @@ class PrebidAdMobInterstitialViewController: NSObject, AdaptedController, Prebid // imp[].ext.data if let adUnitContext = AppConfiguration.shared.adUnitContext { for dataPair in adUnitContext { - adUnit?.addContextData(dataPair.value, forKey: dataPair.key) + adUnit?.addContextData(key: dataPair.value, value: dataPair.key) } } diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobNativeViewController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobNativeViewController.swift index 28a513e4b..86246f1b7 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobNativeViewController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobNativeViewController.swift @@ -62,7 +62,7 @@ class PrebidAdMobNativeViewController: NSObject, AdaptedController, GADNativeAdL // imp[].ext.data if let adUnitContext = AppConfiguration.shared.adUnitContext { for dataPair in adUnitContext { - nativeAdUnit?.addContextData(dataPair.value, forKey: dataPair.key) + nativeAdUnit?.addContextData(key: dataPair.value, value: dataPair.key) } } diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobRewardedViewController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobRewardedViewController.swift index 0b8014911..df21373db 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobRewardedViewController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/AdMob/PrebidAdMobRewardedViewController.swift @@ -62,7 +62,7 @@ class PrebidAdMobRewardedViewController: NSObject, AdaptedController, PrebidConf // imp[].ext.data if let adUnitContext = AppConfiguration.shared.adUnitContext { for dataPair in adUnitContext { - adUnit?.addContextData(dataPair.value, forKey: dataPair.key) + adUnit?.addContextData(key: dataPair.value, value: dataPair.key) } } diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXBannerController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXBannerController.swift index c6769ef48..fdaf5c757 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXBannerController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXBannerController.swift @@ -98,7 +98,7 @@ class PrebidMAXBannerController: NSObject, AdaptedController, PrebidConfigurable // imp[].ext.data if let adUnitContext = AppConfiguration.shared.adUnitContext { for dataPair in adUnitContext { - adUnit?.addContextData(dataPair.value, forKey: dataPair.key) + adUnit?.addContextData(key: dataPair.value, value: dataPair.key) } } diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXInterstitialController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXInterstitialController.swift index 218c44c2f..36da03bce 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXInterstitialController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXInterstitialController.swift @@ -114,7 +114,7 @@ class PrebidMAXInterstitialController: NSObject, AdaptedController, PrebidConfig // imp[].ext.data if let adUnitContext = AppConfiguration.shared.adUnitContext { for dataPair in adUnitContext { - adUnit?.addContextData(dataPair.value, forKey: dataPair.key) + adUnit?.addContextData(key: dataPair.value, value: dataPair.key) } } diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXNativeController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXNativeController.swift index c312e26bf..9e44c4de5 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXNativeController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXNativeController.swift @@ -68,7 +68,7 @@ class PrebidMAXNativeController: NSObject, AdaptedController { // imp[].ext.data if let adUnitContext = AppConfiguration.shared.adUnitContext { for dataPair in adUnitContext { - nativeAdUnit?.addContextData(dataPair.value, forKey: dataPair.key) + nativeAdUnit?.addContextData(key: dataPair.value, value: dataPair.key) } } diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXRewardedController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXRewardedController.swift index 0000ce79e..ca823c1f8 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXRewardedController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/MAX/PrebidMAXRewardedController.swift @@ -69,7 +69,7 @@ class PrebidMAXRewardedController: NSObject, AdaptedController, PrebidConfigurab // imp[].ext.data if let adUnitContext = AppConfiguration.shared.adUnitContext { for dataPair in adUnitContext { - adUnit?.addContextData(dataPair.value, forKey: dataPair.key) + adUnit?.addContextData(key: dataPair.value, value: dataPair.key) } } diff --git a/Podfile.lock b/Podfile.lock index d741af6eb..f9efac40e 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,11 +1,11 @@ PODS: - Alamofire (4.9.1) - - AppLovinSDK (12.6.1) + - AppLovinSDK (13.0.0) - Eureka (5.5.0) - - Google-Mobile-Ads-SDK (11.8.0): + - Google-Mobile-Ads-SDK (11.10.0): - GoogleUserMessagingPlatform (>= 1.1) - GoogleAds-IMA-iOS-SDK (3.23.0) - - GoogleUserMessagingPlatform (2.5.0) + - GoogleUserMessagingPlatform (2.6.0) - RxSwift (6.7.1) - SVProgressHUD (2.3.1): - SVProgressHUD/Core (= 2.3.1) @@ -33,11 +33,11 @@ SPEC REPOS: SPEC CHECKSUMS: Alamofire: 85e8a02c69d6020a0d734f6054870d7ecb75cf18 - AppLovinSDK: a892bbeff744749a8121bd863aa1399f7eef6ef1 + AppLovinSDK: c0ecb434df24008b6ecdd7f1e5b8587bc45ff381 Eureka: 1c18c7fcd8f772cc2ca42d6be36292dffa77eecb - Google-Mobile-Ads-SDK: c1c53687b572122c5b0bdaf354335bbf40fd81ef + Google-Mobile-Ads-SDK: 13e6e98edfd78ad8d8a791edb927658cc260a56f GoogleAds-IMA-iOS-SDK: ee2a68ed7a1a17c7bb81bdb1b81590b35a3fc8f3 - GoogleUserMessagingPlatform: 6b4f48a370e77ce121d034c908cc6ee4fdafaf13 + GoogleUserMessagingPlatform: 0c3a08353e53ce8c2feab7addd0b652cde522450 RxSwift: b9a93a26031785159e11abd40d1a55bcb8057e52 SVProgressHUD: 4837c74bdfe2e51e8821c397825996a8d7de6e22 diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index fa064f43b..7e5fcc3e5 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -106,6 +106,7 @@ 53A657B12A8B650900AE0B4F /* PBMORTBSDKConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 53A657AE2A8B64B300AE0B4F /* PBMORTBSDKConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; 53A657B52A8B8B0500AE0B4F /* PBMORTBBidResponseExtPrebid.h in Headers */ = {isa = PBXBuildFile; fileRef = 53A657B42A8B8B0500AE0B4F /* PBMORTBBidResponseExtPrebid.h */; settings = {ATTRIBUTES = (Public, ); }; }; 53A657B72A8B8B6400AE0B4F /* PBMORTBBidResponseExtPrebid.m in Sources */ = {isa = PBXBuildFile; fileRef = 53A657B62A8B8B6400AE0B4F /* PBMORTBBidResponseExtPrebid.m */; }; + 53A8CBDB2C64D12A008ABEA7 /* SingleContainerInt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A8CBDA2C64D12A008ABEA7 /* SingleContainerInt.swift */; }; 53B221CF2A0E3D2800C91CCB /* PrebidJSLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53B221CE2A0E3D2800C91CCB /* PrebidJSLibrary.swift */; }; 53B221D12A0E3D3D00C91CCB /* PrebidJSLibraryManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53B221D02A0E3D3D00C91CCB /* PrebidJSLibraryManager.swift */; }; 53B221D32A0E3DA900C91CCB /* PrebidJSLibraryManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53B221D22A0E3DA900C91CCB /* PrebidJSLibraryManagerTests.swift */; }; @@ -967,6 +968,7 @@ 53A657AF2A8B64C200AE0B4F /* PBMORTBSDKConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PBMORTBSDKConfiguration.m; sourceTree = ""; }; 53A657B42A8B8B0500AE0B4F /* PBMORTBBidResponseExtPrebid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMORTBBidResponseExtPrebid.h; sourceTree = ""; }; 53A657B62A8B8B6400AE0B4F /* PBMORTBBidResponseExtPrebid.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PBMORTBBidResponseExtPrebid.m; sourceTree = ""; }; + 53A8CBDA2C64D12A008ABEA7 /* SingleContainerInt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleContainerInt.swift; sourceTree = ""; }; 53B221CE2A0E3D2800C91CCB /* PrebidJSLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidJSLibrary.swift; sourceTree = ""; }; 53B221D02A0E3D3D00C91CCB /* PrebidJSLibraryManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidJSLibraryManager.swift; sourceTree = ""; }; 53B221D22A0E3DA900C91CCB /* PrebidJSLibraryManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidJSLibraryManagerTests.swift; sourceTree = ""; }; @@ -1909,6 +1911,7 @@ 53E45A0227FCD09C0095C0B2 /* BannerParameters.swift */, 53E3910D27FCD60800DBA2F7 /* VideoParameters.swift */, 53A368EA2AB2E95E00A03B3E /* NativeParameters.swift */, + 53A8CBDA2C64D12A008ABEA7 /* SingleContainerInt.swift */, ); path = Parameters; sourceTree = ""; @@ -4194,6 +4197,7 @@ 5BC3792B271F1D0000444D5E /* PBMConstants.m in Sources */, 5BC37A61271F1D0000444D5E /* PBMORTBBidExt.m in Sources */, FAEE4D13262DC2B200AD9966 /* AdUnit.swift in Sources */, + 53A8CBDB2C64D12A008ABEA7 /* SingleContainerInt.swift in Sources */, 5BC379CF271F1D0000444D5E /* PBMAdLoadManagerBase.m in Sources */, 924F661C27FDBA5200C8DAF7 /* PBMORTBAdConfiguration.m in Sources */, 5BC379FB271F1D0000444D5E /* PBMExternalURLOpenCallbacks.m in Sources */, @@ -4531,6 +4535,8 @@ MARKETING_VERSION = 2.3.0; MODULEMAP_FILE = PrebidMobile/BuildFiles/PrebidMobile.modulemap; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -4567,6 +4573,8 @@ MARKETING_VERSION = 2.3.0; MODULEMAP_FILE = PrebidMobile/BuildFiles/PrebidMobile.modulemap; SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/PrebidMobile/AdUnits/AdUnit.swift b/PrebidMobile/AdUnits/AdUnit.swift index 9d1358942..15c29e380 100644 --- a/PrebidMobile/AdUnits/AdUnit.swift +++ b/PrebidMobile/AdUnits/AdUnit.swift @@ -13,9 +13,11 @@ import UIKit import ObjectiveC.runtime +/// Base class for ad units built for original type of integration. @objcMembers public class AdUnit: NSObject, DispatcherDelegate { + /// ORTB: imp[].ext.data.adslot public var pbAdSlot: String? { get { adUnitConfig.getPbAdSlot()} set { adUnitConfig.setPbAdSlot(newValue) } @@ -33,18 +35,25 @@ public class AdUnit: NSObject, DispatcherDelegate { private var bidRequester: PBMBidRequesterProtocol - //This flag is set to check if the refresh needs to be made though the user has not invoked the fetch demand after initialization + /// This flag is set to check if the refresh needs to be made though the user has not invoked the fetch demand after initialization private var isInitialFetchDemandCallMade = false private var adServerObject: AnyObject? private var lastFetchDemandCompletion: ((_ bidInfo: BidInfo) -> Void)? - //notification flag set to check if the prebid response is received within the specified time + /// notification flag set to check if the prebid response is received within the specified time private var didReceiveResponse = false - //notification flag set to determine if delegate call needs to be made after timeout delegate is sent + /// notification flag set to determine if delegate call needs to be made after timeout delegate is sent private var timeOutSignalSent = false + + /// Initializes a new `AdUnit` instance with the specified configuration ID, size, and ad formats. + /// + /// - Parameters: + /// - configId: The configuration ID for the ad unit. + /// - size: The primary size of the ad. If `nil`, a default size of `.zero` is used. + /// - adFormats: A set of ad formats supported by the ad unit. public init(configId: String, size: CGSize?, adFormats: Set) { adUnitConfig = AdUnitConfig(configId: configId, size: size ?? CGSize.zero) adUnitConfig.adConfiguration.isOriginalAPI = true @@ -70,6 +79,12 @@ public class AdUnit: NSObject, DispatcherDelegate { } //TODO: dynamic is used by tests + + /// Makes bid request and provides the result as a dictionary of key-value pairs. + /// + /// - Parameter completion: A closure called with the result code and an optional dictionary of targeting keywords. + /// - result: The result code indicating the outcome of the demand fetch. + /// - kvResultDict: A dictionary containing key-value pairs, or `nil` if no demand was fetched. @available(*, deprecated, message: "Deprecated. Use fetchDemand(completion: @escaping (_ bidInfo: BidInfo) -> Void) instead.") dynamic public func fetchDemand(completion: @escaping(_ result: ResultCode, _ kvResultDict: [String : String]?) -> Void) { let dictContainer = DictionaryContainer() @@ -83,10 +98,18 @@ public class AdUnit: NSObject, DispatcherDelegate { } } + /// Makes bid request and provides the result as a `BidInfo` object. + /// + /// - Parameter completionBidInfo: A closure called with a `BidInfo` object representing the fetched demand. dynamic public func fetchDemand(completionBidInfo: @escaping (_ bidInfo: BidInfo) -> Void) { baseFetchDemand(completion: completionBidInfo) } + /// Makes bid request for a specific ad object and provides the result code. Setups targeting keywords into the adObject. + /// + /// - Parameters: + /// - adObject: The ad object for which demand is being fetched. + /// - completion: A closure called with the result code indicating the outcome of the demand fetch. dynamic public func fetchDemand(adObject: AnyObject, completion: @escaping(_ result: ResultCode) -> Void) { baseFetchDemand(adObject: adObject) { bidInfo in DispatchQueue.main.async { @@ -193,35 +216,28 @@ public class AdUnit: NSObject, DispatcherDelegate { // MARK: - adunit ext data aka inventory data (imp[].ext.data) - /** - * This method obtains the context data keyword & value for adunit context targeting - * if the key already exists the value will be appended to the list. No duplicates will be added - */ + + /// This method obtains the context data keyword & value for adunit context targeting + /// If the key already exists the value will be appended to the list. No duplicates will be added @available(*, deprecated, message: "This method is deprecated. Please, use addExtData method instead.") public func addContextData(key: String, value: String) { addExtData(key: key, value: value) } - - /** - * This method obtains the context data keyword & values for adunit context targeting - * the values if the key already exist will be replaced with the new set of values - */ + + /// This method obtains the context data keyword & values for adunit context targeting + /// The values if the key already exist will be replaced with the new set of values @available(*, deprecated, message: "This method is deprecated. Please, use updateExtData method instead.") public func updateContextData(key: String, value: Set) { updateExtData(key: key, value: value) } - /** - * This method allows to remove specific context data keyword & values set from adunit context targeting - */ + /// This method allows to remove specific context data keyword & values set from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use removeExtData method instead.") public func removeContextData(forKey: String) { removeExtData(forKey: forKey) } - /** - * This method allows to remove all context data set from adunit context targeting - */ + /// This method allows to remove all context data set from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use clearExtData method instead.") public func clearContextData() { clearExtData() @@ -232,98 +248,74 @@ public class AdUnit: NSObject, DispatcherDelegate { return adUnitConfig.getExtData() } - /** - * This method obtains the ext data keyword & value for adunit targeting - * if the key already exists the value will be appended to the list. No duplicates will be added - */ + /// This method obtains the ext data keyword & value for adunit targeting. + /// If the key already exists the value will be appended to the list. No duplicates will be added public func addExtData(key: String, value: String) { adUnitConfig.addExtData(key: key, value: value) } - /** - * This method obtains the ext data keyword & values for adunit targeting - * the values if the key already exist will be replaced with the new set of values - */ + /// This method obtains the ext data keyword & values for adunit targeting + /// The values if the key already exist will be replaced with the new set of values public func updateExtData(key: String, value: Set) { adUnitConfig.updateExtData(key: key, value: value) } - /** - * This method allows to remove specific ext data keyword & values set from adunit targeting - */ + /// This method allows to remove specific ext data keyword & values set from adunit targeting public func removeExtData(forKey: String) { adUnitConfig.removeExtData(for: forKey) } - /** - * This method allows to remove all ext data set from adunit targeting - */ + /// This method allows to remove all ext data set from adunit targeting public func clearExtData() { adUnitConfig.clearExtData() } // MARK: - adunit ext keywords (imp[].ext.keywords) - /** - * This method obtains the context keyword for adunit context targeting - * Inserts the given element in the set if it is not already present. - */ + /// This method obtains the context keyword for adunit context targeting + /// Inserts the given element in the set if it is not already present. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeyword method instead.") public func addContextKeyword(_ newElement: String) { addExtKeyword(newElement) } - /** - * This method obtains the context keyword set for adunit context targeting - * Adds the elements of the given set to the set. - */ + /// This method obtains the context keyword set for adunit context targeting + /// Adds the elements of the given set to the set. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeywords method instead.") public func addContextKeywords(_ newElements: Set) { addExtKeywords(newElements) } - /** - * This method allows to remove specific context keyword from adunit context targeting - */ + /// This method allows to remove specific context keyword from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use removeExtKeyword method instead.") public func removeContextKeyword(_ element: String) { removeExtKeyword(element) } - /** - * This method allows to remove all keywords from the set of adunit context targeting - */ + /// This method allows to remove all keywords from the set of adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use clearExtKeywords method instead.") public func clearContextKeywords() { clearExtKeywords() } - - /** - * This method obtains the keyword for adunit targeting - * Inserts the given element in the set if it is not already present. - */ + + /// This method obtains the keyword for adunit targeting + /// Inserts the given element in the set if it is not already present. public func addExtKeyword(_ newElement: String) { adUnitConfig.addExtKeyword(newElement) } - /** - * This method obtains the keyword set for adunit targeting - * Adds the elements of the given set to the set. - */ + /// This method obtains the keyword set for adunit targeting + /// Adds the elements of the given set to the set. public func addExtKeywords(_ newElements: Set) { adUnitConfig.addExtKeywords(newElements) } - /** - * This method allows to remove specific keyword from adunit targeting - */ + /// This method allows to remove specific keyword from adunit targeting public func removeExtKeyword(_ element: String) { adUnitConfig.removeExtKeyword(element) } - /** - * This method allows to remove all keywords from the set of adunit targeting - */ + /// This method allows to remove all keywords from the set of adunit targeting public func clearExtKeywords() { adUnitConfig.clearExtKeywords() } @@ -335,75 +327,109 @@ public class AdUnit: NSObject, DispatcherDelegate { // MARK: - App Content (app.content.data) + /// Sets the app content object, replacing any existing content. + /// + /// - Parameter appContentObject: The `PBMORTBAppContent` object representing the app's content. public func setAppContent(_ appContentObject: PBMORTBAppContent) { adUnitConfig.setAppContent(appContentObject) } + /// Retrieves the current app content object. + /// + /// - Returns: The current `PBMORTBAppContent` object, or `nil` if no content is set. public func getAppContent() -> PBMORTBAppContent? { return adUnitConfig.getAppContent() } + /// Clears the current app content object. public func clearAppContent() { adUnitConfig.clearAppContent() } + /// Adds an array of content data objects to the app content. + /// + /// - Parameter dataObjects: An array of `PBMORTBContentData` objects to add. public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { adUnitConfig.addAppContentData(dataObjects) } + /// Removes a specific content data object from the app content. + /// + /// - Parameter dataObject: The `PBMORTBContentData` object to remove. public func removeAppContentData(_ dataObject: PBMORTBContentData) { adUnitConfig.removeAppContentData(dataObject) } + /// Clears all content data objects from the app content. public func clearAppContentData() { adUnitConfig.clearAppContentData() } // MARK: - User Data (user.data) + /// Retrieves the current user data. + /// + /// - Returns: An array of `PBMORTBContentData` objects representing the user data, or `nil` if no data is available. public func getUserData() -> [PBMORTBContentData]? { return adUnitConfig.getUserData() } + /// Adds an array of user data objects. + /// + /// - Parameter userDataObjects: An array of `PBMORTBContentData` objects to add to the user data. public func addUserData(_ userDataObjects: [PBMORTBContentData]) { adUnitConfig.addUserData(userDataObjects) } + /// Removes a specific user data object. + /// + /// - Parameter userDataObject: The `PBMORTBContentData` object to remove from the user data. public func removeUserData(_ userDataObject: PBMORTBContentData) { adUnitConfig.removeUserData(userDataObject) } + /// Clears all user data. public func clearUserData() { adUnitConfig.clearUserData() } // MARK: GPID + /// Sets the GPID for the ad unit. + /// + /// - Parameter gpid: The GPID string to set. Can be `nil` to clear the GPID. public func setGPID(_ gpid: String?) { adUnitConfig.gpid = gpid } + /// Retrieves the current GPID for the ad unit. + /// + /// - Returns: The GPID string, or `nil` if no GPID is set. public func getGPID() -> String? { return adUnitConfig.gpid } // MARK: Global ORTBObject + /// Sets the OpenRTB configuration string for the ad unit. + /// + /// - Parameter ortbObject: The OpenRTB configuration string to set. Can be `nil` to clear the configuration. public func setOrtbConfig(_ ortbObject: String?) { adUnitConfig.ortbConfig = ortbObject } + /// Retrieves the current OpenRTB configuration string for the ad unit. + /// + /// - Returns: The OpenRTB configuration string, or `nil` if no configuration is set. public func getOrtbConfig() -> String? { return adUnitConfig.ortbConfig } // MARK: - others - /** - * This method allows to set the auto refresh period for the demand - * - * - Parameter time: refresh time interval - */ + + /// This method allows to set the auto refresh period for the demand + /// - Parameter time: refresh time interval public func setAutoRefreshMillis(time: Double) { guard checkRefreshTime(time) else { @@ -425,13 +451,12 @@ public class AdUnit: NSObject, DispatcherDelegate { } } - /** - * This method stops the auto refresh of demand - */ + /// This method stops the auto refresh of demand public func stopAutoRefresh() { stopDispatcher() } + /// This method resumes the auto refresh of demand public func resumeAutoRefresh() { if dispatcher?.state == .stopped { if isInitialFetchDemandCallMade { diff --git a/PrebidMobile/AdUnits/BannerAdUnit.swift b/PrebidMobile/AdUnits/BannerAdUnit.swift index 2f9e4742a..2a6128ef7 100644 --- a/PrebidMobile/AdUnits/BannerAdUnit.swift +++ b/PrebidMobile/AdUnits/BannerAdUnit.swift @@ -15,33 +15,44 @@ import UIKit +/// A class representing a banner ad unit for original type of integration. public class BannerAdUnit: AdUnit, BannerBasedAdUnitProtocol, VideoBasedAdUnitProtocol { + /// A deprecated property for banner ad parameters. @available(*, deprecated, message: "This property is deprecated. Please, use bannerParameters instead.") public var parameters: BannerParameters { get { adUnitConfig.adConfiguration.bannerParameters } set { adUnitConfig.adConfiguration.bannerParameters = newValue } } + /// The banner ad parameters used to configure the ad unit. public var bannerParameters: BannerParameters { get { adUnitConfig.adConfiguration.bannerParameters } set { adUnitConfig.adConfiguration.bannerParameters = newValue } } + /// The video ad parameters used to configure the ad unit. public var videoParameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } set { adUnitConfig.adConfiguration.videoParameters = newValue } } + /// The set of ad formats for the ad unit. public var adFormats: Set { get { adUnitConfig.adFormats } set { adUnitConfig.adFormats = newValue } } + /// Initializes a new `BannerAdUnit` with the specified configuration ID and size. + /// - Parameters: + /// - configId: The unique identifier for the ad unit configuration. + /// - size: The size of the banner ad unit. public init(configId: String, size: CGSize) { super.init(configId: configId, size: size, adFormats: [.banner]) } + /// Adds additional sizes to the banner ad unit's configuration. + /// - Parameter sizes: An array of `CGSize` objects representing additional sizes. public func addAdditionalSize(sizes: [CGSize]) { if super.adUnitConfig.additionalSizes == nil { super.adUnitConfig.additionalSizes = [CGSize]() diff --git a/PrebidMobile/AdUnits/InstreamVideoAdUnit.swift b/PrebidMobile/AdUnits/InstreamVideoAdUnit.swift index e01bcab6d..b38874946 100644 --- a/PrebidMobile/AdUnits/InstreamVideoAdUnit.swift +++ b/PrebidMobile/AdUnits/InstreamVideoAdUnit.swift @@ -15,13 +15,18 @@ import UIKit +/// Represents an instream video ad unit for original type of integration. public class InstreamVideoAdUnit: AdUnit, VideoBasedAdUnitProtocol { + /// The video parameters for this ad unit. public var videoParameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } set { adUnitConfig.adConfiguration.videoParameters = newValue } } - + + /// Initializes a new instream video ad unit. + /// - Parameter configId: The unique identifier for the ad unit configuration. + /// - Parameter size: The size of the ad. public init(configId: String, size: CGSize) { super.init(configId: configId, size: size, adFormats: [.video]) } diff --git a/PrebidMobile/AdUnits/InterstitialAdUnit.swift b/PrebidMobile/AdUnits/InterstitialAdUnit.swift index 4433ef49b..58b042ee0 100644 --- a/PrebidMobile/AdUnits/InterstitialAdUnit.swift +++ b/PrebidMobile/AdUnits/InterstitialAdUnit.swift @@ -15,29 +15,36 @@ import UIKit +/// Represents an interstitial ad unit built for original type of integration. public class InterstitialAdUnit: AdUnit, BannerBasedAdUnitProtocol, VideoBasedAdUnitProtocol { + /// The deprecated banner parameters for this ad unit. @available(*, deprecated, message: "This property is deprecated. Please, use bannerParameters instead.") public var parameters: BannerParameters { get { adUnitConfig.adConfiguration.bannerParameters } set { adUnitConfig.adConfiguration.bannerParameters = newValue } } + /// The banner parameters for this ad unit. public var bannerParameters: BannerParameters { get { adUnitConfig.adConfiguration.bannerParameters } set { adUnitConfig.adConfiguration.bannerParameters = newValue } } + /// The video parameters for this ad unit. public var videoParameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } set { adUnitConfig.adConfiguration.videoParameters = newValue } } + /// The ad formats for the ad unit. public var adFormats: Set { get { adUnitConfig.adFormats } set { adUnitConfig.adFormats = newValue } } + /// Initializes a new interstitial ad unit with a unique configuration identifier. + /// - Parameter configId: The unique identifier for the ad unit configuration. public init(configId: String) { super.init(configId: configId, size: nil, adFormats: [.banner]) @@ -46,6 +53,10 @@ public class InterstitialAdUnit: AdUnit, BannerBasedAdUnitProtocol, VideoBasedAd adUnitConfig.adConfiguration.videoParameters.placement = .Interstitial } + /// Initializes a new interstitial ad unit with a minimum width and height percentage. + /// - Parameter configId: The unique identifier for the ad unit configuration. + /// - Parameter minWidthPerc: The minimum width percentage of the ad. + /// - Parameter minHeightPerc: The minimum height percentage of the ad. public convenience init(configId: String, minWidthPerc: Int, minHeightPerc: Int) { self.init(configId: configId) diff --git a/PrebidMobile/AdUnits/MultiformatAdUnit/BidInfo.swift b/PrebidMobile/AdUnits/MultiformatAdUnit/BidInfo.swift index c58ca6bd9..c27b6a3ab 100644 --- a/PrebidMobile/AdUnits/MultiformatAdUnit/BidInfo.swift +++ b/PrebidMobile/AdUnits/MultiformatAdUnit/BidInfo.swift @@ -15,6 +15,7 @@ limitations under the License. import Foundation +/// Contains information about bid. @objcMembers @objc(PBMBidInfo) public class BidInfo: NSObject { @@ -25,13 +26,29 @@ public class BidInfo: NSObject { /// Key to get Prebid imp event from `events` public static let EVENT_IMP = "ext.prebid.events.imp" + /// The result code of the bid request public private(set) var resultCode: ResultCode + + /// Targeting keywords associated with the bid public private(set) var targetingKeywords: [String: String]? + + /// The expiration time of the bid public private(set) var exp: Double? + + /// The cache ID for native ads public private(set) var nativeAdCacheId: String? + + /// Events related to the bid public private(set) var events: [String: String] - public init(resultCode: ResultCode, targetingKeywords: [String : String]? = nil, exp: Double? = nil, + /// Initializes a new `BidInfo` instance with the specified parameters. + /// - Parameters: + /// - resultCode: The result code of the bid request. + /// - targetingKeywords: Optional targeting keywords associated with the bid. + /// - exp: Optional expiration time of the bid. + /// - nativeAdCacheId: Optional cache ID for native ads. + /// - events: Optional dictionary of events related to the bid. + public init(resultCode: ResultCode, targetingKeywords: [String : String]? = nil, exp: Double? = nil, nativeAdCacheId: String? = nil, events: [String: String] = [:]) { self.resultCode = resultCode self.targetingKeywords = targetingKeywords @@ -43,6 +60,7 @@ public class BidInfo: NSObject { } // Obj-C API + /// Retrieves the expiration time of the bid as an `NSNumber`. public func getExp() -> NSNumber? { if let exp { return NSNumber(value: exp) diff --git a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift index 3098506f2..97c9c1b0b 100644 --- a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift +++ b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift @@ -15,9 +15,11 @@ import Foundation +/// Multiformat ad unit. Built for original API. @objcMembers public class PrebidAdUnit: NSObject { + /// The ad slot identifier for the Prebid ad unit. public var pbAdSlot: String? { get { adUnit.pbAdSlot } set { adUnit.pbAdSlot = newValue } @@ -25,11 +27,18 @@ public class PrebidAdUnit: NSObject { private let adUnit: AdUnit + /// Initializes a new `PrebidAdUnit` with the given configuration ID. + /// - Parameter configId: The configuration ID for the ad unit. public init(configId: String) { adUnit = AdUnit(configId: configId, size: CGSize.zero, adFormats: []) super.init() } + /// Makes bid request for the specified ad object and request config. Setups targeting keywords into the adObject. + /// - Parameters: + /// - adObject: The ad object to fetch demand for. + /// - request: The `PrebidRequest` containing the demand request parameters. + /// - completion: A closure to be called with the `BidInfo` result. public func fetchDemand(adObject: AnyObject, request: PrebidRequest, completion: @escaping (BidInfo) -> Void) { guard requestHasParameters(request) else { @@ -45,6 +54,10 @@ public class PrebidAdUnit: NSObject { } } + /// Makes bid request for the specified request config. + /// - Parameters: + /// - request: The `PrebidRequest` containing the demand request parameters. + /// - completion: A closure to be called with the `BidInfo` result. public func fetchDemand(request: PrebidRequest, completion: @escaping (BidInfo) -> Void) { guard requestHasParameters(request) else { completion(BidInfo(resultCode: .prebidInvalidRequest)) @@ -62,22 +75,20 @@ public class PrebidAdUnit: NSObject { // MARK: - Auto refresh API - /** - * This method allows to set the auto refresh period for the demand - * - * - Parameter time: refresh time interval - */ + + /// This method allows to set the auto refresh period for the demand + /// + /// - Parameter time: refresh time interval public func setAutoRefreshMillis(time: Double) { adUnit.setAutoRefreshMillis(time: time) } - /** - * This method stops the auto refresh of demand - */ + /// This method stops the auto refresh of demand public func stopAutoRefresh() { adUnit.stopAutoRefresh() } + /// This method resumes the auto refresh of demand public func resumeAutoRefresh() { adUnit.resumeAutoRefresh() } diff --git a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift index ea1238b20..46794377b 100644 --- a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift +++ b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift @@ -15,6 +15,7 @@ import Foundation +/// Class that contains properties and methods to configure Prebid request. @objcMembers public class PrebidRequest: NSObject { @@ -37,6 +38,13 @@ public class PrebidRequest: NSObject { private var userData: [PBMORTBContentData]? private var extKeywords = Set() + /// Initializes a new `PrebidRequest` with the given parameters. + /// - Parameters: + /// - bannerParameters: The banner parameters for the ad request. + /// - videoParameters: The video parameters for the ad request. + /// - nativeParameters: The native parameters for the ad request. + /// - isInterstitial: Indicates if the request is for an interstitial ad. + /// - isRewarded: Indicates if the request is for a rewarded ad. public init(bannerParameters: BannerParameters? = nil, videoParameters: VideoParameters? = nil, nativeParameters: NativeParameters? = nil, isInterstitial: Bool = false, isRewarded: Bool = false) { self.bannerParameters = bannerParameters @@ -50,16 +58,17 @@ public class PrebidRequest: NSObject { // MARK: GPID + /// Sets the GPID for the ad request. + /// - Parameter gpid: The GPID to set. public func setGPID(_ gpid: String?) { self.gpid = gpid } // MARK: - adunit ext data aka inventory data (imp[].ext.data) - /** - * This method obtains the ext data keyword & value for adunit targeting - * if the key already exists the value will be appended to the list. No duplicates will be added - */ + + /// This method obtains the ext data keyword & value for adunit targeting + /// if the key already exists the value will be appended to the list. No duplicates will be added public func addExtData(key: String, value: String) { if extData[key] == nil { extData[key] = Set() @@ -68,24 +77,19 @@ public class PrebidRequest: NSObject { extData[key]?.insert(value) } - /** - * This method obtains the ext data keyword & values for adunit targeting - * the values if the key already exist will be replaced with the new set of values - */ + + /// This method obtains the ext data keyword & values for adunit targeting + /// the values if the key already exist will be replaced with the new set of values public func updateExtData(key: String, value: Set) { extData[key] = value } - /** - * This method allows to remove specific ext data keyword & values set from adunit targeting - */ + /// This method allows to remove specific ext data keyword & values set from adunit targeting public func removeExtData(forKey: String) { extData.removeValue(forKey: forKey) } - /** - * This method allows to remove all ext data set from adunit targeting - */ + /// This method allows to remove all ext data set from adunit targeting public func clearExtData() { extData.removeAll() } @@ -96,32 +100,24 @@ public class PrebidRequest: NSObject { // MARK: - adunit ext keywords (imp[].ext.keywords) - /** - * This method obtains the keyword for adunit targeting - * Inserts the given element in the set if it is not already present. - */ + /// This method obtains the keyword for adunit targeting + /// Inserts the given element in the set if it is not already present. public func addExtKeyword(_ newElement: String) { extKeywords.insert(newElement) } - /** - * This method obtains the keyword set for adunit targeting - * Adds the elements of the given set to the set. - */ + /// This method obtains the keyword set for adunit targeting + /// Adds the elements of the given set to the set. public func addExtKeywords(_ newElements: Set) { extKeywords.formUnion(newElements) } - /** - * This method allows to remove specific keyword from adunit targeting - */ + /// This method allows to remove specific keyword from adunit targeting public func removeExtKeyword(_ element: String) { extKeywords.remove(element) } - /** - * This method allows to remove all keywords from the set of adunit targeting - */ + /// This method allows to remove all keywords from the set of adunit targeting public func clearExtKeywords() { extKeywords.removeAll() } @@ -132,14 +128,19 @@ public class PrebidRequest: NSObject { // MARK: - App Content (app.content.data) + /// Sets the app content for the ad request. + /// - Parameter appContentObject: The `PBMORTBAppContent` to set. public func setAppContent(_ appContentObject: PBMORTBAppContent) { self.appContent = appContentObject } + /// Clears the app content for the ad request. public func clearAppContent() { appContent = nil } + /// Adds data to the app content. + /// - Parameter dataObjects: The array of `PBMORTBContentData` to add. public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { if appContent == nil { appContent = PBMORTBAppContent() @@ -152,12 +153,15 @@ public class PrebidRequest: NSObject { appContent?.data?.append(contentsOf: dataObjects) } + /// Removes specific data from the app content. + /// - Parameter dataObject: The `PBMORTBContentData` to remove. public func removeAppContentData(_ dataObject: PBMORTBContentData) { if let appContentData = appContent?.data, appContentData.contains(dataObject) { appContent?.data?.removeAll(where: { $0 == dataObject }) } } + /// Clears all data from the app content. public func clearAppContentData() { appContent?.data?.removeAll() } @@ -168,6 +172,8 @@ public class PrebidRequest: NSObject { // MARK: - User Data (user.data) + /// Adds user data to the ad request. + /// - Parameter userDataObjects: The array of `PBMORTBContentData` to add. public func addUserData(_ userDataObjects: [PBMORTBContentData]) { if userData == nil { userData = [PBMORTBContentData]() @@ -176,12 +182,15 @@ public class PrebidRequest: NSObject { userData?.append(contentsOf: userDataObjects) } + /// Removes specific user data from the ad request. + /// - Parameter userDataObject: The `PBMORTBContentData` to remove. public func removeUserData(_ userDataObject: PBMORTBContentData) { if let userData = userData, userData.contains(userDataObject) { self.userData?.removeAll { $0 == userDataObject } } } + /// Clears all user data from the ad request. public func clearUserData() { userData?.removeAll() } diff --git a/PrebidMobile/AdUnits/Native/ContextSubType.swift b/PrebidMobile/AdUnits/Native/ContextSubType.swift index c09e27506..2ed1470d2 100644 --- a/PrebidMobile/AdUnits/Native/ContextSubType.swift +++ b/PrebidMobile/AdUnits/Native/ContextSubType.swift @@ -15,32 +15,47 @@ import Foundation +/// Native context subtype asset. @objcMembers public class ContextSubType: SingleContainerInt { + /// Represents a general context type. public static let General = ContextSubType(10) - + + + /// Represents an article context type. public static let Article = ContextSubType(11) - + + /// Represents a video context type. public static let Video = ContextSubType(12) - + + /// Represents an audio context type. public static let Audio = ContextSubType(13) - + + /// Represents an image context type. public static let Image = ContextSubType(14) - + + /// Represents a user-generated content context type. public static let UserGenerated = ContextSubType(15) - + + /// Represents a social media context type. public static let Social = ContextSubType(20) - + + /// Represents an email context type. public static let email = ContextSubType(21) - + + /// Represents a chat or instant messaging context type. public static let chatIM = ContextSubType(22) - + + /// Represents a product selling context type. public static let SellingProduct = ContextSubType(30) - + + /// Represents an App Store context type. public static let AppStore = ContextSubType(31) - + + /// Represents a review site context type. public static let ReviewSite = ContextSubType(32) - + + /// Represents a custom context type. public static let Custom = ContextSubType(500) } diff --git a/PrebidMobile/AdUnits/Native/ContextType.swift b/PrebidMobile/AdUnits/Native/ContextType.swift index d23cc16bf..6fe0104b7 100644 --- a/PrebidMobile/AdUnits/Native/ContextType.swift +++ b/PrebidMobile/AdUnits/Native/ContextType.swift @@ -15,14 +15,19 @@ import Foundation +/// Native context type asset. @objcMembers public class ContextType: SingleContainerInt { - + + /// Represents a content context type. public static let Content = ContextType(1) - + + /// Represents a social media context type. public static let Social = ContextType(2) + /// Represents a product context type. public static let Product = ContextType(3) + /// Represents a custom context type. public static let Custom = ContextType(500) } diff --git a/PrebidMobile/AdUnits/Native/NativeAd.swift b/PrebidMobile/AdUnits/Native/NativeAd.swift index 39e59b27d..f407df97e 100644 --- a/PrebidMobile/AdUnits/Native/NativeAd.swift +++ b/PrebidMobile/AdUnits/Native/NativeAd.swift @@ -16,12 +16,16 @@ import Foundation import UIKit +/// Represents a native ad and handles its various properties and functionalities. @objcMembers public class NativeAd: NSObject, CacheExpiryDelegate { // MARK: - Public properties + /// The native ad markup containing the ad assets. public var nativeAdMarkup: NativeAdMarkup? + + /// The delegate to receive native ad events. public weak var delegate: NativeAdEventDelegate? // MARK: - Internal properties @@ -44,58 +48,73 @@ public class NativeAd: NSObject, CacheExpiryDelegate { // MARK: - Array getters + /// Returns an array of titles from the native ad markup. @objc public var titles: [NativeTitle] { nativeAdMarkup?.assets?.compactMap { return $0.title } ?? [] } + /// Returns an array of data objects from the native ad markup. @objc public var dataObjects: [NativeData] { nativeAdMarkup?.assets?.compactMap { return $0.data } ?? [] } + /// Returns an array of images from the native ad markup. @objc public var images: [NativeImage] { nativeAdMarkup?.assets?.compactMap { return $0.img } ?? [] } + /// Returns an array of event trackers from the native ad markup. @objc public var eventTrackers: [NativeEventTrackerResponse]? { return nativeAdMarkup?.eventtrackers } // MARK: - Filtered array getters + /// Returns an array of data objects filtered by the specified data type. @objc public func dataObjects(of dataType: NativeDataAssetType) -> [NativeData] { dataObjects.filter { $0.type == dataType.rawValue } } + /// Returns an array of images filtered by the specified image type. @objc public func images(of imageType: NativeImageAssetType) -> [NativeImage] { images.filter { $0.type == imageType.rawValue } } // MARK: - Property getters + /// Returns the first title text from the native ad markup. @objc public var title: String? { return titles.first?.text } + /// Returns the URL of the main image from the native ad markup. @objc public var imageUrl: String? { return images(of: .main).first?.url } + /// Returns the URL of the icon image from the native ad markup. @objc public var iconUrl: String? { return images(of: .icon).first?.url } + /// Returns the sponsored by text from the native ad markup. @objc public var sponsoredBy: String? { return dataObjects(of: .sponsored).first?.value } + /// Returns the description text from the native ad markup. @objc public var text: String? { return dataObjects(of: .desc).first?.value } + /// Returns the call-to-action text from the native ad markup. @objc public var callToAction: String? { return dataObjects(of: .ctaText).first?.value } + /// Creates a `NativeAd` instance from the given cache ID. + /// - Parameter cacheId: The cache ID to retrieve the bid response. + /// - Returns: A `NativeAd` instance if successful, otherwise `nil`. public static func create(cacheId: String) -> NativeAd? { guard let bidString = CacheManager.shared.get(cacheId: cacheId), let bidDic = Utils.shared.getDictionaryFromString(bidString) else { @@ -164,7 +183,11 @@ public class NativeAd: NSObject, CacheExpiryDelegate { } } - //MARK: registerView function + /// Registers a view for tracking viewability and click events. + /// - Parameters: + /// - view: The view to register. + /// - clickableViews: An array of views that should be clickable. + /// - Returns: `true` if the view was successfully registered, otherwise `false`. @discardableResult public func registerView(view: UIView?, clickableViews: [UIView]? ) -> Bool { guard let view = view else { diff --git a/PrebidMobile/AdUnits/Native/NativeAdConfiguration.swift b/PrebidMobile/AdUnits/Native/NativeAdConfiguration.swift index 1f1022111..0772c69ce 100644 --- a/PrebidMobile/AdUnits/Native/NativeAdConfiguration.swift +++ b/PrebidMobile/AdUnits/Native/NativeAdConfiguration.swift @@ -15,18 +15,23 @@ import Foundation +/// Represents the configuration for a native ad, including markup and version information. @objc(PBMNativeAdConfiguration) @objcMembers public class NativeAdConfiguration: NSObject { /// Version of the Native Markup version in use. public var version: String = "1.2" + /// The object containing the request details for the native markup. public var markupRequestObject = NativeMarkupRequestObject() + /// Initializes a new instance of `NativeAdMarkup` with default values. public override init() { super.init() } + /// Initializes a new instance of `NativeAdConfiguration` with the specified native parameters. + /// - Parameter nativeParameters: The parameters to configure the native ad. init(nativeParameters: NativeParameters) { version = nativeParameters.version markupRequestObject = NativeMarkupRequestObject(nativeParameters: nativeParameters) diff --git a/PrebidMobile/AdUnits/Native/NativeAdDelegate.swift b/PrebidMobile/AdUnits/Native/NativeAdDelegate.swift index bef76fbe5..fb45812b9 100644 --- a/PrebidMobile/AdUnits/Native/NativeAdDelegate.swift +++ b/PrebidMobile/AdUnits/Native/NativeAdDelegate.swift @@ -15,21 +15,17 @@ limitations under the License. import Foundation -@objc public protocol NativeAdDelegate : AnyObject{ - /** - * A successful Prebid Native ad is returned - * - * @param ad use this instance for displaying - */ - func nativeAdLoaded(ad:NativeAd) - /** - * Prebid Native was not found in the server returned response, - * Please display the ad as regular ways - */ +@objc public protocol NativeAdDelegate : AnyObject { + + /// A successful Prebid Native ad is returned + /// - Parameter: A successful Prebid Native ad is returned + func nativeAdLoaded(ad: NativeAd) + + /// Prebid Native was not found in the server returned response, + /// Please display the ad as regular ways func nativeAdNotFound() - /** - * Prebid Native ad was returned, however, the bid is not valid for displaying - * Should be treated as on ad load failed - */ + + /// Prebid Native ad was returned, however, the bid is not valid for displaying + /// Should be treated as on ad load failed func nativeAdNotValid() } diff --git a/PrebidMobile/AdUnits/Native/NativeAdEventDelegate.swift b/PrebidMobile/AdUnits/Native/NativeAdEventDelegate.swift index 9e0d1b971..3da904550 100644 --- a/PrebidMobile/AdUnits/Native/NativeAdEventDelegate.swift +++ b/PrebidMobile/AdUnits/Native/NativeAdEventDelegate.swift @@ -15,17 +15,17 @@ limitations under the License. import Foundation -@objc public protocol NativeAdEventDelegate : AnyObject { - /** - * Sent when the native ad is expired. - */ +/// A protocol for handling events related to native ads. +/// +/// This protocol defines optional methods that can be implemented to handle various events associated with native ads, such as expiration, clicks, and impressions. Implementing this protocol allows for custom handling of these events within the native ad lifecycle. +@objc public protocol NativeAdEventDelegate: AnyObject { + + /// Sent when the native ad is expired. @objc optional func adDidExpire(ad:NativeAd) - /** - * Sent when the native view is clicked by the user. - */ + + /// Sent when the native view is clicked by the user. @objc optional func adWasClicked(ad:NativeAd) - /** - * Sent when an impression is recorded for an native ad - */ + + /// Sent when an impression is recorded for an native ad @objc optional func adDidLogImpression(ad:NativeAd) } diff --git a/PrebidMobile/AdUnits/Native/NativeAdMarkup.swift b/PrebidMobile/AdUnits/Native/NativeAdMarkup.swift index c263bf53d..9b9b678f4 100644 --- a/PrebidMobile/AdUnits/Native/NativeAdMarkup.swift +++ b/PrebidMobile/AdUnits/Native/NativeAdMarkup.swift @@ -15,6 +15,7 @@ import Foundation +/// Represents the markup for a native ad, including assets, tracking URLs, and other metadata. @objcMembers public class NativeAdMarkup: NSObject, JsonDecodable { @@ -66,6 +67,8 @@ public class NativeAdMarkup: NSObject, JsonDecodable { /// This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification public var ext: [String: Any]? + /// Initializes a new instance of `NativeAdMarkup` from a JSON string. + /// - Parameter jsonString: A JSON string representing the native ad markup. convenience public init?(jsonString: String?) { guard let jsonString = jsonString, !jsonString.isEmpty else { Log.warn("The native ad markup json string is empty") @@ -80,6 +83,8 @@ public class NativeAdMarkup: NSObject, JsonDecodable { self.init(jsonDictionary: jsonDic) } + /// Initializes a new instance of `NativeAdMarkup` from a JSON dictionary. + /// - Parameter jsonDictionary: A dictionary representing the native ad markup. public required init(jsonDictionary: [String: Any]) { guard !jsonDictionary.isEmpty else { Log.warn("The native ad markup json dicitonary is empty") @@ -120,6 +125,7 @@ public class NativeAdMarkup: NSObject, JsonDecodable { } } + /// Initializes a new instance of `NativeAdMarkup` with default values. public override init() { super.init() } diff --git a/PrebidMobile/AdUnits/Native/NativeAdMarkupAsset.swift b/PrebidMobile/AdUnits/Native/NativeAdMarkupAsset.swift index ab64ce3ec..488e2a752 100644 --- a/PrebidMobile/AdUnits/Native/NativeAdMarkupAsset.swift +++ b/PrebidMobile/AdUnits/Native/NativeAdMarkupAsset.swift @@ -15,6 +15,7 @@ import Foundation +/// Represents an asset in the native ad markup, which can be a title, image, data, or link. @objcMembers public class NativeAdMarkupAsset: NSObject, JsonDecodable { @@ -44,6 +45,8 @@ public class NativeAdMarkupAsset: NSObject, JsonDecodable { /// flexibility beyond the standard defined in this specification public var ext: [String: Any]? + /// Initializes a new instance of `NativeAdMarkupAsset` from a JSON dictionary. + /// - Parameter jsonDictionary: A dictionary representing the asset in the native ad markup. public required init(jsonDictionary: [String: Any]) { guard !jsonDictionary.isEmpty else { Log.warn("The native title json dicitonary is empty") @@ -67,6 +70,7 @@ public class NativeAdMarkupAsset: NSObject, JsonDecodable { } } + /// Initializes a new instance of `NativeAdMarkupAsset` with default values. public override init() { super.init() } diff --git a/PrebidMobile/AdUnits/Native/NativeAsset.swift b/PrebidMobile/AdUnits/Native/NativeAsset.swift index 6b53c5818..6aa723f10 100644 --- a/PrebidMobile/AdUnits/Native/NativeAsset.swift +++ b/PrebidMobile/AdUnits/Native/NativeAsset.swift @@ -15,16 +15,25 @@ limitations under the License. import UIKit +/// Represents a generic native ad asset which could be a title, image, or data. public class NativeAsset: NSObject { + /// Unique identifier for the asset. var id: NSInteger! - public var required: Bool = false + /// Indicates whether the asset is required for the ad to be considered valid. + public var required: Bool = false + + /// Initializes a new instance of `NativeAsset`. + /// - Parameter isRequired: A boolean indicating whether the asset is required. public init(isRequired: Bool) { super.init() required = isRequired } + /// Generates a dictionary representation of the asset object. + /// - Parameter id: An optional identifier to be included in the asset dictionary. + /// - Returns: A dictionary representing the asset with its properties. func getAssetObject(id : Int) -> [AnyHashable: Any] { var asset: [AnyHashable: Any] = [:] @@ -46,17 +55,26 @@ public class NativeAsset: NSObject { } +/// Represents a title asset in a native ad. @objcMembers public class NativeAssetTitle: NativeAsset { + /// Maximum length of the title. var length: NSInteger! = 25 + /// Additional custom properties for the title asset. public var ext: AnyObject? + /// Initializes a new instance of `NativeAssetTitle`. + /// - Parameters: + /// - length: The maximum length of the title. + /// - required: A boolean indicating whether the asset is required. public required init(length: NSInteger, required: Bool) { super.init(isRequired: required) self.length = length } + /// Generates a dictionary representation of the title asset object. + /// - Returns: A dictionary representing the title asset with its properties. func getTitleObject() -> [AnyHashable: Any] { var title: [AnyHashable: Any] = [:] @@ -65,29 +83,52 @@ public class NativeAsset: NSObject { return title } - } +/// Represents an image asset in a native ad. @objcMembers public class NativeAssetImage: NativeAsset { + /// The type of the image asset. public var type: ImageAsset? + + /// The width of the image asset. public var width: Int? + + /// The minimum width of the image asset. public var widthMin: Int? + + /// The height of the image asset. public var height: Int? + + /// The minimum height of the image asset. public var heightMin: Int? + + /// The MIME types supported for the image asset. public var mimes: Array? + + /// Additional custom properties for the image asset. public var ext: AnyObject? + /// Initializes a new instance of `NativeAssetImage`. + /// - Parameters: + /// - minimumWidth: The minimum width of the image. + /// - minimumHeight: The minimum height of the image. + /// - required: A boolean indicating whether the asset is required. public convenience init(minimumWidth: Int, minimumHeight: Int, required: Bool) { self.init(isRequired: required) self.widthMin = minimumWidth self.heightMin = minimumHeight } + /// Initializes a new instance of `NativeAssetImage`. + /// - Parameters: + /// - isRequired: A boolean indicating whether the asset is required. public override init(isRequired: Bool) { super.init(isRequired: isRequired) } + /// Generates a dictionary representation of the image asset object. + /// - Returns: A dictionary representing the image asset with its properties. func getImageObject() -> [AnyHashable: Any] { var image: [AnyHashable: Any] = [:] @@ -102,19 +143,31 @@ public class NativeAsset: NSObject { return image } - } +/// Represents a data asset in a native ad. @objcMembers public class NativeAssetData: NativeAsset { + + /// The type of the data asset. var type: DataAsset? + + /// The length of the data asset. public var length: Int? + + /// Additional custom properties for the data asset. public var ext: AnyObject? + /// Initializes a new instance of `NativeAssetData`. + /// - Parameters: + /// - type: The type of the data asset. + /// - required: A boolean indicating whether the asset is required. public required init(type: DataAsset, required: Bool) { super.init(isRequired: required) self.type = type } + /// Generates a dictionary representation of the data asset object. + /// - Returns: A dictionary representing the data asset with its properties. func getDataObject() -> [AnyHashable: Any]{ var data: [AnyHashable: Any] = [:] @@ -126,21 +179,25 @@ public class NativeAsset: NSObject { } } +/// Native image asset type. public class ImageAsset: SingleContainerInt { + /// Represents an icon image asset. @objc public static let Icon = ImageAsset(1) + /// Represents the main image asset. @objc public static let Main = ImageAsset(3) + /// Represents a custom image asset. @objc - public static let Custom = ContextType(500) - + public static let Custom = ImageAsset(500) } - +/// Enum representing different types of native data assets. @objc public enum DataAsset: Int { + case sponsored = 1 case description = 2 case rating = 3 @@ -153,22 +210,25 @@ public class ImageAsset: SingleContainerInt { case description2 = 10 case displayurl = 11 case ctatext = 12 + /// Custom type for user-defined data assets case Custom private static var customValue = 500 - - public var exchangeID:Int { - get { - switch self { - case .Custom: - return DataAsset.customValue - default: - return self.rawValue - } - } - set { - DataAsset.customValue = newValue + + /// Gets or sets the exchange ID for the asset type. + /// - Returns: The exchange ID for the asset type. + public var exchangeID:Int { + get { + switch self { + case .Custom: + return DataAsset.customValue + default: + return self.rawValue } - } + set { + DataAsset.customValue = newValue + } + + } } diff --git a/PrebidMobile/AdUnits/Native/NativeData.swift b/PrebidMobile/AdUnits/Native/NativeData.swift index 727c2b0bc..5a01db4da 100644 --- a/PrebidMobile/AdUnits/Native/NativeData.swift +++ b/PrebidMobile/AdUnits/Native/NativeData.swift @@ -15,8 +15,10 @@ import Foundation +/// Class representing native data elements in the ad markup. @objcMembers public class NativeData: NSObject, JsonDecodable { + /// The type of data element being submitted from the Data Asset Types table. /// Required for assetsurl/dcourl responses, not required for embedded asset responses. public var type: Int? @@ -34,6 +36,8 @@ public class NativeData: NSObject, JsonDecodable { /// flexibility beyond the standard defined in this specification public var ext: [String: Any]? + /// Initializes a new `NativeData` object with the provided JSON dictionary. + /// - Parameter jsonDictionary: A dictionary containing the JSON data to initialize the object. public required init(jsonDictionary: [String: Any]) { guard !jsonDictionary.isEmpty else { Log.warn("The native data json dicitonary is empty") diff --git a/PrebidMobile/AdUnits/Native/NativeDataAssetType.swift b/PrebidMobile/AdUnits/Native/NativeDataAssetType.swift index c2e1a498d..991ee9383 100644 --- a/PrebidMobile/AdUnits/Native/NativeDataAssetType.swift +++ b/PrebidMobile/AdUnits/Native/NativeDataAssetType.swift @@ -15,20 +15,36 @@ import Foundation +/// Enum representing various types of native data assets that can be used in ads. +/// +/// Each case represents a different type of data that can be provided in a native ad response. +/// Values are defined according to the type of data they represent, such as descriptive text, ratings, or pricing information. @objc public enum NativeDataAssetType: Int { case undefined = 0 - case sponsored = 1 /// Sponsored By message where response should contain the brand name of the sponsor. - case desc = 2 /// Descriptive text associated with the product or service being advertised. Longer length of text in response may be truncated or ellipsed by the exchange. - case rating = 3 /// Rating of the product being offered to the user. For example an app’s rating in an app store from 0-5. - case likes = 4 /// Number of social ratings or “likes” of the product being offered to the user. - case downloads = 5 /// Number downloads/installs of this product - case price = 6 /// Price for product / app / in-app purchase. Value should include currency symbol in localised format. - case salePrice = 7 /// Sale price that can be used together with price to indicate a discounted price compared to a regular price. Value should include currency symbol in localised format. - case phone = 8 /// Phone number - case address = 9 /// Address - case desc2 = 10 /// Additional descriptive text associated text with the product or service being advertised - case displayURL = 11 /// Display URL for the text ad. To be used when sponsoring entity doesn’t own the content. IE sponsored by BRAND on SITE (where SITE is transmitted in this field). - case ctaText = 12 /// CTA description - descriptive text describing a ‘call to action’ button for the destination URL. - - case custom = 500 /// Reserved for Exchange specific usage numbered above 500 + /// Sponsored By message where response should contain the brand name of the sponsor. + case sponsored = 1 + /// Descriptive text associated with the product or service being advertised. Longer length of text in response may be truncated or ellipsed by the exchange. + case desc = 2 + /// Rating of the product being offered to the user. For example an app’s rating in an app store from 0-5. + case rating = 3 + /// Number of social ratings or “likes” of the product being offered to the user. + case likes = 4 + /// Number downloads/installs of this product + case downloads = 5 + /// Price for product / app / in-app purchase. Value should include currency symbol in localised format. + case price = 6 + /// Sale price that can be used together with price to indicate a discounted price compared to a regular price. Value should include currency symbol in localised format. + case salePrice = 7 + /// Phone number + case phone = 8 + /// Address + case address = 9 + /// Additional descriptive text associated text with the product or service being advertised + case desc2 = 10 + /// Display URL for the text ad. To be used when sponsoring entity doesn’t own the content. IE sponsored by BRAND on SITE (where SITE is transmitted in this field). + case displayURL = 11 + /// CTA description - descriptive text describing a ‘call to action’ button for the destination URL. + case ctaText = 12 + /// Reserved for Exchange specific usage numbered above 500 + case custom = 500 } diff --git a/PrebidMobile/AdUnits/Native/NativeEventTracker.swift b/PrebidMobile/AdUnits/Native/NativeEventTracker.swift index 55e2781cb..1fa68bc10 100644 --- a/PrebidMobile/AdUnits/Native/NativeEventTracker.swift +++ b/PrebidMobile/AdUnits/Native/NativeEventTracker.swift @@ -15,18 +15,30 @@ limitations under the License. import UIKit +/// Class representing an event tracker for native ads. @objc public class NativeEventTracker: NSObject { + /// The type of event being tracked. var event: EventType + + /// List of event tracking methods to be used. var methods: Array + + /// Optional custom extensions or metadata. var ext: AnyObject? + /// Initializes a new `NativeEventTracker` instance. + /// - Parameters: + /// - event: The event type to track. + /// - methods: The methods used for tracking the event. @objc public init(event: EventType, methods: Array) { self.event = event self.methods = methods } + /// Returns a dictionary representation of the event tracker. + /// - Returns: A dictionary containing the event type and tracking methods. func getEventTracker() -> [AnyHashable: Any] { var methodsList:[Int] = [] @@ -43,6 +55,7 @@ import UIKit } } +/// Class representing different event types. public class EventType: SingleContainerInt { @objc public static let Impression = EventType(1) @@ -60,14 +73,15 @@ public class EventType: SingleContainerInt { public static let Custom = EventType(500) } - +/// Native event tracking type. public class EventTracking: SingleContainerInt { - @objc - public static let Image = EventTracking(1) - - @objc - public static let js = EventTracking(2) - - @objc - public static let Custom = EventTracking(500) + + /// Represents image-based event tracking. + @objc public static let Image = EventTracking(1) + + /// Represents JavaScript-based event tracking. + @objc public static let js = EventTracking(2) + + /// Represents a custom event tracking method. + @objc public static let Custom = EventTracking(500) } diff --git a/PrebidMobile/AdUnits/Native/NativeEventTrackerResponse.swift b/PrebidMobile/AdUnits/Native/NativeEventTrackerResponse.swift index 48bba7eca..3ec4ed18c 100644 --- a/PrebidMobile/AdUnits/Native/NativeEventTrackerResponse.swift +++ b/PrebidMobile/AdUnits/Native/NativeEventTrackerResponse.swift @@ -15,6 +15,7 @@ import Foundation +/// Class representing a response for a native event tracker. @objcMembers public class NativeEventTrackerResponse: NSObject, JsonDecodable { @@ -37,6 +38,9 @@ public class NativeEventTrackerResponse: NSObject, JsonDecodable { /// This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification public var ext: [String: Any]? + /// Initializes a `NativeEventTrackerResponse` object from a JSON dictionary. + /// + /// - Parameter jsonDictionary: A dictionary containing the JSON data to initialize the object. public required init(jsonDictionary: [String: Any]) { guard !jsonDictionary.isEmpty else { Log.warn("The native event trackers json dicitonary is empty") @@ -62,6 +66,7 @@ public class NativeEventTrackerResponse: NSObject, JsonDecodable { } } + /// Initializes with default values public override init() { super.init() } diff --git a/PrebidMobile/AdUnits/Native/NativeImage.swift b/PrebidMobile/AdUnits/Native/NativeImage.swift index 0b296ef05..ac3c4e67a 100644 --- a/PrebidMobile/AdUnits/Native/NativeImage.swift +++ b/PrebidMobile/AdUnits/Native/NativeImage.swift @@ -15,8 +15,10 @@ import Foundation +/// Class representing an image asset in a native ad. @objcMembers public class NativeImage: NSObject, JsonDecodable { + /// The type of image element being submitted from the Image Asset Types table. /// Required for assetsurl or dcourl responses, not required for embedded asset responses. public var type: Int? @@ -38,6 +40,9 @@ public class NativeImage: NSObject, JsonDecodable { /// flexibility beyond the standard defined in this specification public var ext: [String: Any]? + /// Initializes a `NativeImage` object from a JSON dictionary. + /// + /// - Parameter jsonDictionary: A dictionary containing the JSON data to initialize the object. public required init(jsonDictionary: [String: Any]) { guard !jsonDictionary.isEmpty else { Log.warn("The native image json dicitonary is empty") @@ -55,6 +60,7 @@ public class NativeImage: NSObject, JsonDecodable { } } + /// Initializes with default values public override init() { super.init() } diff --git a/PrebidMobile/AdUnits/Native/NativeImageAssetType.swift b/PrebidMobile/AdUnits/Native/NativeImageAssetType.swift index 407936648..3f9399b94 100644 --- a/PrebidMobile/AdUnits/Native/NativeImageAssetType.swift +++ b/PrebidMobile/AdUnits/Native/NativeImageAssetType.swift @@ -15,9 +15,18 @@ import Foundation +/// Enum representing various types of native image assets used in ads. +/// +/// Each case corresponds to a different type of image asset that can be used in native ads. +/// Values are defined according to the role or importance of the image in the ad. @objc public enum NativeImageAssetType: Int { + + /// Represents an icon image, which is typically a small image used as a visual representation of the product or service. case icon = 1 + + /// Represents the main image of the ad, which is usually the primary visual element and central to the ad's presentation. case main = 3 + /// Reserved for exchange-specific usage. case custom = 500 } diff --git a/PrebidMobile/AdUnits/Native/NativeLink.swift b/PrebidMobile/AdUnits/Native/NativeLink.swift index 1e11ce0b9..c72832ba5 100644 --- a/PrebidMobile/AdUnits/Native/NativeLink.swift +++ b/PrebidMobile/AdUnits/Native/NativeLink.swift @@ -15,6 +15,7 @@ import Foundation +/// Class representing a link in a native ad. @objcMembers public class NativeLink: NSObject, JsonDecodable { @@ -31,6 +32,9 @@ public class NativeLink: NSObject, JsonDecodable { /// This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification public var ext: [String: Any]? + /// Initializes a `NativeLink` object from a JSON dictionary. + /// + /// - Parameter jsonDictionary: A dictionary containing the JSON data to initialize the object. public required init(jsonDictionary: [String: Any]) { guard !jsonDictionary.isEmpty else { Log.warn("The native link json dicitonary is empty") @@ -47,6 +51,7 @@ public class NativeLink: NSObject, JsonDecodable { } } + /// Initializes with default values public override init() { super.init() } diff --git a/PrebidMobile/AdUnits/Native/NativeMarkupRequestObject.swift b/PrebidMobile/AdUnits/Native/NativeMarkupRequestObject.swift index bba96b377..a64f7bd3a 100644 --- a/PrebidMobile/AdUnits/Native/NativeMarkupRequestObject.swift +++ b/PrebidMobile/AdUnits/Native/NativeMarkupRequestObject.swift @@ -15,6 +15,7 @@ import Foundation +/// Represents a request object for native markup, including details about the ad's context, placement, and supported features. @objc(PBMNativeMarkupRequestObject) @objcMembers public class NativeMarkupRequestObject: NSObject, NSCopying, PBMJsonCodable { @@ -70,6 +71,7 @@ public class NativeMarkupRequestObject: NSObject, NSCopying, PBMJsonCodable { /// This object is a placeholder that may contain custom JSON agreed to by the parties to support flexibility beyond the standard defined in this specification public var ext: [String : Any]? + /// Initializes a `NativeMarkupRequestObject` with default values. public override init() { super.init() } @@ -92,6 +94,10 @@ public class NativeMarkupRequestObject: NSObject, NSCopying, PBMJsonCodable { // MARK: - NSCopying + /// Creates a copy of the current `NativeMarkupRequestObject` instance. + /// + /// - Parameter zone: An optional zone for the copy operation. + /// - Returns: A copy of the `NativeMarkupRequestObject` instance. public func copy(with zone: NSZone? = nil) -> Any { let clone = NativeMarkupRequestObject() @@ -112,6 +118,9 @@ public class NativeMarkupRequestObject: NSObject, NSCopying, PBMJsonCodable { // MARK: - PBMJsonCodable + /// Converts the `NativeMarkupRequestObject` instance to a JSON dictionary. + /// + /// - Returns: A dictionary representation of the `NativeMarkupRequestObject` instance. public var jsonDictionary: [String : Any]? { var json = [String : Any]() @@ -130,6 +139,10 @@ public class NativeMarkupRequestObject: NSObject, NSCopying, PBMJsonCodable { return json } + /// Converts the `NativeMarkupRequestObject` instance to a JSON string. + /// + /// - Returns: A JSON string representation of the `NativeMarkupRequestObject` instance. + /// - Throws: An error if the conversion to JSON string fails. public func toJsonString() throws -> String { try PBMFunctions.toStringJsonDictionary(jsonDictionary ?? [:]) } diff --git a/PrebidMobile/AdUnits/Native/NativeRequest.swift b/PrebidMobile/AdUnits/Native/NativeRequest.swift index 4c8ecad67..2471be756 100644 --- a/PrebidMobile/AdUnits/Native/NativeRequest.swift +++ b/PrebidMobile/AdUnits/Native/NativeRequest.swift @@ -15,84 +15,112 @@ limitations under the License. import UIKit +/// A class representing a native request for original type of integration. public class NativeRequest: AdUnit { + /// The version of the native ad specification being used. Defaults to "1.2" if not set. public var version: String { get { adUnitConfig.nativeAdConfiguration?.version ?? "1.2" } set { adUnitConfig.nativeAdConfiguration?.version = newValue } } + /// The context in which the ad appears. See `ContextType` for possible values. public var context: ContextType? { get { adUnitConfig.nativeAdConfiguration?.markupRequestObject.context } set { adUnitConfig.nativeAdConfiguration?.markupRequestObject.context = newValue } } + /// A more detailed context in which the ad appears. See `ContextSubType` for possible values. public var contextSubType: ContextSubType? { get { adUnitConfig.nativeAdConfiguration?.markupRequestObject.contextsubtype } set { adUnitConfig.nativeAdConfiguration?.markupRequestObject.contextsubtype = newValue } } + /// The design/format/layout of the ad unit being offered. See `PlacementType` for possible values. public var placementType: PlacementType? { get { adUnitConfig.nativeAdConfiguration?.markupRequestObject.plcmttype } set { adUnitConfig.nativeAdConfiguration?.markupRequestObject.plcmttype = newValue } } + /// The number of identical placements in the ad layout. Defaults to 1 if not set. public var placementCount: Int { get { adUnitConfig.nativeAdConfiguration?.markupRequestObject.plcmtcnt ?? 1 } set { adUnitConfig.nativeAdConfiguration?.markupRequestObject.plcmtcnt = newValue } } + /// The sequence number of the ad. Defaults to 0 if not set. public var sequence: Int { get { adUnitConfig.nativeAdConfiguration?.markupRequestObject.seq ?? 0 } set { adUnitConfig.nativeAdConfiguration?.markupRequestObject.seq = newValue } } + /// An array of `NativeAsset` objects representing the assets required for the native ad request. public var assets: [NativeAsset]? { get { adUnitConfig.nativeAdConfiguration?.markupRequestObject.assets } set { adUnitConfig.nativeAdConfiguration?.markupRequestObject.assets = newValue } } + /// Indicates whether the supply source/impression supports returning an `assetsurl` instead of an asset object. + /// Defaults to 0 if not set. public var asseturlsupport: Int { get { adUnitConfig.nativeAdConfiguration?.markupRequestObject.aurlsupport ?? 0 } set { adUnitConfig.nativeAdConfiguration?.markupRequestObject.aurlsupport = newValue } } + /// Indicates whether the supply source/impression supports returning a `dco` URL instead of an asset object. + /// Defaults to 0 if not set. public var durlsupport: Int { get { adUnitConfig.nativeAdConfiguration?.markupRequestObject.durlsupport ?? 0 } set { adUnitConfig.nativeAdConfiguration?.markupRequestObject.durlsupport = newValue } } + /// An array of `NativeEventTracker` objects specifying the event tracking settings for the native ad. public var eventtrackers: [NativeEventTracker]? { get { self.adUnitConfig.nativeAdConfiguration?.markupRequestObject.eventtrackers } set { self.adUnitConfig.nativeAdConfiguration?.markupRequestObject.eventtrackers = newValue } } + /// Indicates whether the native ad supports a buyer-specific privacy notice. Defaults to 0 if not set. public var privacy: Int { get { self.adUnitConfig.nativeAdConfiguration?.markupRequestObject.privacy ?? 0 } set { self.adUnitConfig.nativeAdConfiguration?.markupRequestObject.privacy = newValue } } + /// A placeholder for custom JSON agreed to by the parties to support flexibility beyond the standard specification. public var ext: [String: Any]? { get { self.adUnitConfig.nativeAdConfiguration?.markupRequestObject.ext } set { self.adUnitConfig.nativeAdConfiguration?.markupRequestObject.ext = newValue } } + /// The configuration ID for the ad unit. public var configId: String { get { adUnitConfig.configId } set { adUnitConfig.configId = newValue } } + /// Initializes a `NativeRequest` with a specified configuration ID. + /// + /// - Parameter configId: The configuration ID for the ad unit. public init(configId: String) { super.init(configId: configId, size: CGSize.zero, adFormats: [.native]) super.adUnitConfig.nativeAdConfiguration = NativeAdConfiguration() } + /// Initializes a `NativeRequest` with a specified configuration ID, assets, and event trackers. + /// + /// - Parameters: + /// - configId: The configuration ID for the ad unit. + /// - assets: An optional array of `NativeAsset` objects. + /// - eventTrackers: An optional array of `NativeEventTracker` objects. public convenience init(configId: String, assets: [NativeAsset]? = nil, eventTrackers: [NativeEventTracker]? = nil) { self.init(configId: configId) self.assets = assets self.eventtrackers = eventTrackers } + /// Adds an array of native assets to the request. If assets already exist, they will be appended. + /// + /// - Parameter assets: An array of `NativeAsset` objects to add. public func addNativeAssets(_ assets: [NativeAsset]) { if self.assets != nil { self.assets?.append(contentsOf: assets) @@ -101,6 +129,9 @@ public class NativeRequest: AdUnit { } } + /// Adds an array of native event trackers to the request. If event trackers already exist, they will be appended. + /// + /// - Parameter eventTrackers: An array of `NativeEventTracker` objects to add. public func addNativeEventTracker(_ eventTrackers: [NativeEventTracker]) { if eventtrackers != nil { self.eventtrackers?.append(contentsOf: eventTrackers) @@ -109,6 +140,9 @@ public class NativeRequest: AdUnit { } } + /// Retrieves the native request object as a JSON dictionary. + /// + /// - Returns: A dictionary representation of the native request object, or `nil` if an error occurs. public func getNativeRequestObject() -> [AnyHashable: Any]? { guard let markup = try? adUnitConfig.nativeAdConfiguration?.markupRequestObject.toJsonString() else { return nil @@ -124,5 +158,4 @@ public class NativeRequest: AdUnit { return native.toJsonDictionary() } - } diff --git a/PrebidMobile/AdUnits/Native/NativeTitle.swift b/PrebidMobile/AdUnits/Native/NativeTitle.swift index 7b17cce05..96eae665c 100644 --- a/PrebidMobile/AdUnits/Native/NativeTitle.swift +++ b/PrebidMobile/AdUnits/Native/NativeTitle.swift @@ -15,6 +15,7 @@ import Foundation +/// Represents the title element in a native ad, including optional text and length. @objcMembers public class NativeTitle: NSObject, JsonDecodable { /// The text associated with the text element. @@ -28,6 +29,9 @@ public class NativeTitle: NSObject, JsonDecodable { /// flexibility beyond the standard defined in this specification public var ext: [String: Any]? + /// Initializes a `NativeTitle` object from a JSON dictionary. + /// + /// - Parameter jsonDictionary: A dictionary containing the JSON representation of the title. public required init(jsonDictionary: [String: Any]) { guard !jsonDictionary.isEmpty else { Log.warn("The native title json dicitonary is empty") @@ -43,6 +47,7 @@ public class NativeTitle: NSObject, JsonDecodable { } } + /// Initializes a `NativeTitle` object with default values. public override init() { super.init() } diff --git a/PrebidMobile/AdUnits/Native/PlacementType.swift b/PrebidMobile/AdUnits/Native/PlacementType.swift index a7bf40da1..0416522c5 100644 --- a/PrebidMobile/AdUnits/Native/PlacementType.swift +++ b/PrebidMobile/AdUnits/Native/PlacementType.swift @@ -15,16 +15,22 @@ import Foundation +/// Represents different types of placements for native ads. @objcMembers public class PlacementType: SingleContainerInt { - + + /// Placement type indicating the ad appears within feed content. public static let FeedContent = PlacementType(1) - + + /// Placement type indicating the ad appears within atomic content. public static let AtomicContent = PlacementType(2) - + + /// Placement type indicating the ad appears outside of content. public static let OutsideContent = PlacementType(3) - + + /// Placement type indicating the ad appears within a recommendation widget. public static let RecommendationWidget = PlacementType(4) - + + /// Placement type for custom placements not predefined in the standard. public static let Custom = PlacementType(500) } diff --git a/PrebidMobile/AdUnits/Parameters/BannerParameters.swift b/PrebidMobile/AdUnits/Parameters/BannerParameters.swift index 50ee6982f..61b5390bc 100644 --- a/PrebidMobile/AdUnits/Parameters/BannerParameters.swift +++ b/PrebidMobile/AdUnits/Parameters/BannerParameters.swift @@ -22,13 +22,18 @@ public class BannerParameters: NSObject { /// List of supported API frameworks for this impression. If an API is not explicitly listed, it is assumed not to be supported. public var api: [Signals.Api]? + /// Min width percentage value for interstitial public var interstitialMinWidthPerc: Int? + + /// Min height percentage value for interstitial public var interstitialMinHeightPerc: Int? + /// Ad sizes of the ad public var adSizes: [CGSize]? // MARK: - Helpers + /// Helper for `api` values public var rawAPI: [Int]? { get { api?.toIntArray() diff --git a/PrebidMobile/AdUnits/Parameters/NativeParameters.swift b/PrebidMobile/AdUnits/Parameters/NativeParameters.swift index 8a15b08bb..b5741c013 100644 --- a/PrebidMobile/AdUnits/Parameters/NativeParameters.swift +++ b/PrebidMobile/AdUnits/Parameters/NativeParameters.swift @@ -15,22 +15,46 @@ limitations under the License. import Foundation +/// Represents parameters for a native ad request, including assets, event trackers, and configuration settings. @objcMembers public class NativeParameters: NSObject { + /// An array of `NativeAsset` objects representing the assets required for the native ad request. public var assets: [NativeAsset]? + + /// An array of `NativeEventTracker` objects specifying the event tracking settings for the native ad. public var eventtrackers: [NativeEventTracker]? + /// The version of the native ad specification being used. Defaults to "1.2". public var version: String = "1.2" + + /// The context in which the ad appears. See `ContextType` for possible values. public var context: ContextType? + + /// A more detailed context in which the ad appears. See `ContextSubType` for possible values. public var contextSubType: ContextSubType? + + /// The design/format/layout of the ad unit being offered. See `PlacementType` for possible values. public var placementType: PlacementType? + + /// The number of identical placements in the ad layout. Defaults to 1. public var placementCount: Int = 1 + + /// The sequence number of the ad. Defaults to 0. public var sequence: Int = 0 + /// Indicates whether the supply source/impression supports returning an `assetsurl` instead of an asset object. + /// Defaults to 0, indicating no support. public var asseturlsupport: Int = 0 + + /// Indicates whether the supply source/impression supports returning a `dco` URL instead of an asset object. + /// Defaults to 0, indicating no support. public var durlsupport: Int = 0 + /// Indicates whether the supply source/impression supports returning a `dco` URL instead of an asset object. + /// Defaults to 0, indicating no support. public var privacy: Int = 0 + + /// A placeholder for custom JSON agreed to by the parties to support flexibility beyond the standard specification. public var ext: [String: Any]? } diff --git a/PrebidMobile/AdUnits/Parameters/Signals.swift b/PrebidMobile/AdUnits/Parameters/Signals.swift index a8231bb90..c1750fff2 100644 --- a/PrebidMobile/AdUnits/Parameters/Signals.swift +++ b/PrebidMobile/AdUnits/Parameters/Signals.swift @@ -15,56 +15,26 @@ limitations under the License. import Foundation -public class SingleContainerInt: NSObject, ExpressibleByIntegerLiteral { - - public typealias IntegerLiteralType = Int - - @objc - public let value: Int - - @objc - public required init(integerLiteral value: Int) { - self.value = value - } - - static func == (lhs: SingleContainerInt, rhs: SingleContainerInt) -> Bool { - return lhs.value == rhs.value - } - - override public func isEqual(_ object: Any?) -> Bool { - - if let other = object as? SingleContainerInt { - if self === other { - return true - } else { - return self.value == other.value - } - } - - return false - - } - - override public var hash : Int { - return value.hashValue - } -} - +/// A class containing constants related to OpenRTB signals. +/// +/// This class provides static constants and values representing different +/// API frameworks, playback methods, protocols, start delays, and video +/// placement types as defined in the OpenRTB specification. +/// public class Signals: NSObject { - /** - # OpenRTB - API Frameworks # - ``` - | Value | Description | - |-------|-------------| - | 1 | VPAID 1.0 | - | 2 | VPAID 2.0 | - | 3 | MRAID-1 | - | 4 | ORMMA | - | 5 | MRAID-2 | - | 6 | MRAID-3 | - | 7 | OMID-1 | - ``` - */ + + ///# OpenRTB - API Frameworks # + /// ``` + /// | Value | Description | + /// |-------|-------------| + /// | 1 | VPAID 1.0 | + /// | 2 | VPAID 2.0 | + /// | 3 | MRAID-1 | + /// | 4 | ORMMA | + /// | 5 | MRAID-2 | + /// | 6 | MRAID-3 | + /// | 7 | OMID-1 | + /// ``` @objc(PBApi) public class Api: SingleContainerInt { @@ -97,19 +67,18 @@ public class Signals: NSObject { public static let OMID_1 = Api(7) } - /** - # OpenRTB - Playback Methods # - ``` - | Value | Description | - |-------|----------------------------------------------------------| - | 1 | Initiates on Page Load with Sound On | - | 2 | Initiates on Page Load with Sound Off by Default | - | 3 | Initiates on Click with Sound On | - | 4 | Initiates on Mouse-Over with Sound On | - | 5 | Initiates on Entering Viewport with Sound On | - | 6 | Initiates on Entering Viewport with Sound Off by Default | - ``` - */ + + /// # OpenRTB - Playback Methods # + /// ``` + /// | Value | Description | + /// |-------|----------------------------------------------------------| + /// | 1 | Initiates on Page Load with Sound On | + /// | 2 | Initiates on Page Load with Sound Off by Default | + /// | 3 | Initiates on Click with Sound On | + /// | 4 | Initiates on Mouse-Over with Sound On | + /// | 5 | Initiates on Entering Viewport with Sound On | + /// | 6 | Initiates on Entering Viewport with Sound Off by Default | + /// ``` @objc(PBPlaybackMethod) public class PlaybackMethod: SingleContainerInt { @@ -139,23 +108,21 @@ public class Signals: NSObject { } - /** - # OpenRTB - Protocols # - ``` - | Value | Description | - |-------|-------------------| - | 1 | VAST 1.0 | - | 2 | VAST 2.0 | - | 3 | VAST 3.0 | - | 4 | VAST 1.0 Wrapper | - | 5 | VAST 2.0 Wrapper | - | 6 | VAST 3.0 Wrapper | - | 7 | VAST 4.0 | - | 8 | VAST 4.0 Wrapper | - | 9 | DAAST 1.0 | - | 10 | DAAST 1.0 Wrapper | - ``` - */ + /// # OpenRTB - Protocols # + /// ``` + /// | Value | Description | + /// |-------|-------------------| + /// | 1 | VAST 1.0 | + /// | 2 | VAST 2.0 | + /// | 3 | VAST 3.0 | + /// | 4 | VAST 1.0 Wrapper | + /// | 5 | VAST 2.0 Wrapper | + /// | 6 | VAST 3.0 Wrapper | + /// | 7 | VAST 4.0 | + /// | 8 | VAST 4.0 Wrapper | + /// | 9 | DAAST 1.0 | + /// | 10 | DAAST 1.0 Wrapper | + /// ``` @objc(PBProtocols) public class Protocols: SingleContainerInt { @@ -201,17 +168,15 @@ public class Signals: NSObject { } - /** - # OpenRTB - Start Delay # - ``` - | Value | Description | - |-------|--------------------------------------------------| - | > 0 | Mid-Roll (value indicates start delay in second) | - | 0 | Pre-Roll | - | -1 | Generic Mid-Roll | - | -2 | Generic Post-Roll | - ``` - */ + /// # OpenRTB - Start Delay # + /// ``` + /// | Value | Description | + /// |-------|--------------------------------------------------| + /// | > 0 | Mid-Roll (value indicates start delay in second) | + /// | 0 | Pre-Roll | + /// | -1 | Generic Mid-Roll | + /// | -2 | Generic Post-Roll | + /// ``` @objc(PBStartDelay) public class StartDelay: SingleContainerInt { @@ -229,18 +194,16 @@ public class Signals: NSObject { } - /** - # OpenRTB - Video Placement Types # - ``` - | Value | Description | - |-------|------------------------------| - | 1 | In-Stream | - | 2 | In-Banner | - | 3 | In-Article | - | 4 | In-Feed | - | 5 | Interstitial/Slider/Floating | - ``` - */ + /// # OpenRTB - Video Placement Types # + /// ``` + /// | Value | Description | + /// |-------|------------------------------| + /// | 1 | In-Stream | + /// | 2 | In-Banner | + /// | 3 | In-Article | + /// | 4 | In-Feed | + /// | 5 | Interstitial/Slider/Floating | + /// ``` @objc(PBPlacement) public class Placement: SingleContainerInt { @@ -272,6 +235,7 @@ public class Signals: NSObject { @objc public static let Floating = Placement(5) + /// Helper function @objc public static func getPlacementByRawValue(_ value: Int) -> Signals.Placement? { switch value { case 1: diff --git a/PrebidMobile/AdUnits/Parameters/SingleContainerInt.swift b/PrebidMobile/AdUnits/Parameters/SingleContainerInt.swift new file mode 100644 index 000000000..1bca11123 --- /dev/null +++ b/PrebidMobile/AdUnits/Parameters/SingleContainerInt.swift @@ -0,0 +1,51 @@ +/* Copyright 2018-2019 Prebid.org, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import Foundation + +public class SingleContainerInt: NSObject, ExpressibleByIntegerLiteral { + + public typealias IntegerLiteralType = Int + + @objc + public let value: Int + + @objc + public required init(integerLiteral value: Int) { + self.value = value + } + + static func == (lhs: SingleContainerInt, rhs: SingleContainerInt) -> Bool { + return lhs.value == rhs.value + } + + override public func isEqual(_ object: Any?) -> Bool { + + if let other = object as? SingleContainerInt { + if self === other { + return true + } else { + return self.value == other.value + } + } + + return false + + } + + override public var hash: Int { + return value.hashValue + } +} diff --git a/PrebidMobile/AdUnits/Parameters/VideoParameters.swift b/PrebidMobile/AdUnits/Parameters/VideoParameters.swift index 525a7f5e6..7db51bb7b 100644 --- a/PrebidMobile/AdUnits/Parameters/VideoParameters.swift +++ b/PrebidMobile/AdUnits/Parameters/VideoParameters.swift @@ -34,14 +34,14 @@ public class VideoParameters: NSObject { /// Minimum video ad duration in seconds. public var minDuration: SingleContainerInt? - /** - Content MIME types supported. - Prebid Server required property. + + /// Content MIME types supported. + /// Prebid Server required property. - # Example # - * "video/mp4" - * "video/x-ms-wmv" - */ + /// # Example # + /// "video/mp4" + /// "video/x-ms-wmv" + /// public var mimes: [String] /// Allowed playback methods. If none specified, assume all are allowed. @@ -63,18 +63,21 @@ public class VideoParameters: NSObject { // MARK: - Helpers + /// Helper property public var rawAPI: [Int]? { get { api?.toIntArray() } } + /// Helper property public var rawPlaybackMethod: [Int]? { get { playbackMethod?.toIntArray() } } + /// Helper property public var rawProtocols: [Int]? { get { protocols?.toIntArray() @@ -86,7 +89,7 @@ public class VideoParameters: NSObject { self.mimes = mimes } - // Objective-C API + /// Objective-C API public func setSize(_ size: NSValue) { adSize = size.cgSizeValue } diff --git a/PrebidMobile/AdUnits/RewardedVideoAdUnit.swift b/PrebidMobile/AdUnits/RewardedVideoAdUnit.swift index 236204848..d58e491da 100644 --- a/PrebidMobile/AdUnits/RewardedVideoAdUnit.swift +++ b/PrebidMobile/AdUnits/RewardedVideoAdUnit.swift @@ -15,19 +15,27 @@ limitations under the License. import UIKit +/// Represents an rewarded ad unit for original type of integration. public class RewardedVideoAdUnit: AdUnit, VideoBasedAdUnitProtocol { + /// Deprecated property for video parameters. + /// + /// - Note: This property is deprecated. Please use `videoParameters` instead. @available(*, deprecated, message: "This property is deprecated. Please, use videoParameters instead.") public var parameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } set { adUnitConfig.adConfiguration.videoParameters = newValue } } + /// Property for video parameters. public var videoParameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } set { adUnitConfig.adConfiguration.videoParameters = newValue } } + /// Initializes a `RewardedVideoAdUnit` with the given configuration ID. + /// + /// - Parameter configId: The configuration ID for the ad unit. public init(configId: String) { super.init(configId: configId, size: nil, adFormats: [.video]) adUnitConfig.adConfiguration.isInterstitialAd = true @@ -35,6 +43,11 @@ public class RewardedVideoAdUnit: AdUnit, VideoBasedAdUnitProtocol { adUnitConfig.adConfiguration.videoParameters.placement = .Interstitial } + /// Initializes a `RewardedVideoAdUnit` with the given configuration ID and minimum size percentages. + /// + /// - Parameter configId: The configuration ID for the ad unit. + /// - Parameter minWidthPerc: The minimum width percentage for the ad unit. + /// - Parameter minHeightPerc: The minimum height percentage for the ad unit. public convenience init(configId: String, minWidthPerc: Int, minHeightPerc: Int) { self.init(configId: configId) adUnitConfig.minSizePerc = NSValue(cgSize: CGSize(width: minWidthPerc, height: minHeightPerc)) diff --git a/PrebidMobile/AdUnits/Utils.swift b/PrebidMobile/AdUnits/Utils.swift index 3557858c6..5d1f31361 100644 --- a/PrebidMobile/AdUnits/Utils.swift +++ b/PrebidMobile/AdUnits/Utils.swift @@ -16,20 +16,19 @@ import Foundation import WebKit +/// A utility class for handling various ad-related operations and conversions. public class Utils: NSObject { - /** - * The class is created as a singleton object & used - */ + /// The class is created as a singleton object & used @objc public static let shared = Utils() - /** - * The initializer that needs to be created only once - */ + /// The initializer that needs to be created only once private override init() { super.init() } + + /// A delegate to handle native ad events. @objc public weak var delegate: NativeAdDelegate? @@ -40,6 +39,7 @@ public class Utils: NSObject { private let GAD_CUSTOM_NATIVE_AD = "GADCustomNativeAd" private let INNNER_HTML_SCRIPT = "document.body.innerHTML" + /// Deprecated. MoPub is not available anymore. Use Prebid MAX adapters instead. @available(*, deprecated, message: "MoPub is not available anymore. Use Prebid MAX adapters instead.") @objc public func convertDictToMoPubKeywords(dict: Dictionary) -> String { @@ -241,6 +241,9 @@ public class Utils: NSObject { } } + /// Finds a native ad object within a given object. + /// + /// - Parameter adObject: The object to search within. @objc public func findNative(adObject: AnyObject){ if (self.isObjectFromClass(adObject, DFP_BANNER_VIEW_CLASSNAME)) { @@ -425,10 +428,9 @@ public class Utils: NSObject { } } -/** - 1. It is a class that allow use it as AnyObject and passs to - func fetchDemand(adObject: AnyObject, ...) - 2. It is not a public class as a result client can not directly pass it to - func fetchDemand(adObject: AnyObject, ...) - */ + +/// 1. It is a class that allow use it as AnyObject and passs to - func fetchDemand(adObject: AnyObject, ...) +/// 2. It is not a public class as a result client can not directly pass it to - func fetchDemand(adObject: AnyObject, ...) class DictionaryContainer { var dict = [T1 : T2]() } diff --git a/PrebidMobile/AdUnits/VideoAdUnit.swift b/PrebidMobile/AdUnits/VideoAdUnit.swift index 8b205b0b8..06e9a8367 100644 --- a/PrebidMobile/AdUnits/VideoAdUnit.swift +++ b/PrebidMobile/AdUnits/VideoAdUnit.swift @@ -15,18 +15,28 @@ import UIKit +/// A deprecated class for handling video ad units. @available(*, deprecated, message: "This class is deprecated. Please, use BannerAdUnit with video adFormat.") public class VideoAdUnit: AdUnit { + /// The parameters for video ads associated with this ad unit. public var parameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } set { adUnitConfig.adConfiguration.videoParameters = newValue } } + /// Initializes a new instance of `VideoAdUnit` with the specified configuration ID and size. + /// + /// - Parameters: + /// - configId: The configuration ID for the ad unit. + /// - size: The size of the ad unit. public init(configId: String, size: CGSize) { super.init(configId: configId, size: size, adFormats: [.video]) } + /// Adds additional sizes to the ad unit. + /// + /// - Parameter sizes: An array of `CGSize` objects representing the additional sizes for the ad unit. public func addAdditionalSize(sizes: [CGSize]) { if super.adUnitConfig.additionalSizes == nil { super.adUnitConfig.additionalSizes = [CGSize]() diff --git a/PrebidMobile/AdUnits/VideoInterstitialAdUnit.swift b/PrebidMobile/AdUnits/VideoInterstitialAdUnit.swift index ecff952ee..9c1659158 100644 --- a/PrebidMobile/AdUnits/VideoInterstitialAdUnit.swift +++ b/PrebidMobile/AdUnits/VideoInterstitialAdUnit.swift @@ -15,14 +15,22 @@ import UIKit +/// A deprecated class representing a video interstitial ad unit. +/// This class is used to configure and manage video interstitial ads. It inherits from `AdUnit` and provides +/// specific settings for video ads including interstitial ad configuration and placement. @available(*, deprecated, message: "This class is deprecated. Please, use InterstitialAdUnit with video adFormat.") public class VideoInterstitialAdUnit: AdUnit { + /// The video parameters for this ad unit. + /// This property allows you to get or set the video parameters for the ad unit's configuration. public var parameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } set { adUnitConfig.adConfiguration.videoParameters = newValue } } + /// Initializes a new instance of `VideoInterstitialAdUnit` with the specified configuration ID. + /// The ad unit is configured as an interstitial ad with full screen placement and video parameters set for interstitial ads. + /// - Parameter configId: The configuration ID for the ad unit. public init(configId: String) { super.init(configId: configId, size: nil, adFormats: [.video]) @@ -31,6 +39,11 @@ public class VideoInterstitialAdUnit: AdUnit { adUnitConfig.adConfiguration.videoParameters.placement = .Interstitial } + /// Convenience initializer to create a video interstitial ad unit with specified minimum width and height percentages. + /// - Parameters: + /// - configId: The configuration ID for the ad unit. + /// - minWidthPerc: The minimum width percentage of the ad unit. + /// - minHeightPerc: The minimum height percentage of the ad unit. public convenience init(configId: String, minWidthPerc: Int, minHeightPerc: Int) { self.init(configId: configId) diff --git a/PrebidMobile/ConfigurationAndTargeting/Gender.swift b/PrebidMobile/ConfigurationAndTargeting/Gender.swift index 556c57f23..e9617b729 100644 --- a/PrebidMobile/ConfigurationAndTargeting/Gender.swift +++ b/PrebidMobile/ConfigurationAndTargeting/Gender.swift @@ -15,11 +15,26 @@ import Foundation +/// An enumeration representing gender options for ad targeting or other purposes. +/// This enum is used to specify different gender options and is intended for use in contexts where gender information may be relevant. +/// +/// - `unknown`: Gender is not specified or unknown. +/// - `male`: Represents the male gender. +/// - `female`: Represents the female gender. +/// - `other`: Represents any other gender that does not fit into the male or female categories. @objc(PBMGender) public enum Gender : Int { + + /// Gender is not specified or unknown. case unknown + + /// Represents the male gender. case male + + /// Represents the female gender. case female + + /// Represents any other gender that does not fit into the male or female categories. case other } diff --git a/PrebidMobile/ConfigurationAndTargeting/Prebid.swift b/PrebidMobile/ConfigurationAndTargeting/Prebid.swift index 9ac243f50..db6438197 100644 --- a/PrebidMobile/ConfigurationAndTargeting/Prebid.swift +++ b/PrebidMobile/ConfigurationAndTargeting/Prebid.swift @@ -17,61 +17,74 @@ import Foundation fileprivate let defaultTimeoutMillis = 2000 +/// A callback used for Prebid initialization status. +/// +/// This callback is called when the Prebid SDK initialization completes. It provides the status of the initialization and any error that may have occurred. +/// +/// - Parameters: +/// - status: The status of the Prebid initialization. +/// - error: An optional error that occurred during initialization. public typealias PrebidInitializationCallback = ((PrebidInitializationStatus, Error?) -> Void) +/// The `Prebid` class manages the configuration and initialization of the PrebidMobile SDK. @objcMembers public class Prebid: NSObject { // MARK: - Public Properties (SDK) + /// The name of the bidder for AppNexus. public static let bidderNameAppNexus = "appnexus" + + /// The name of the bidder for Rubicon Project. public static let bidderNameRubiconProject = "rubicon" + /// Indicates whether the timeout value has been updated. public var timeoutUpdated: Bool = false + /// The Prebid Server account ID. public var prebidServerAccountId = "" + /// Enables or disables debug mode. + /// ORTB: bidRequest.test public var pbsDebug = false + /// Custom HTTP headers to be sent with requests. public var customHeaders: [String: String] = [:] + /// Stored bid responses identified by bidder names. public var storedBidResponses: [String: String] = [:] - /** - * This property is set by the developer when he is willing to assign the assetID for Native ad. - **/ + /// This property is set by the developer when he is willing to assign the assetID for Native ad. public var shouldAssignNativeAssetID : Bool = false - /** - * This property is set by the developer when he is willing to share the location for better ad targeting - **/ + /// This property is set by the developer when he is willing to share the location for better ad targeting public var shareGeoLocation = false - /** - * Set the desidered verbosity of the logs - */ + /// Set the desidered verbosity of the logs public var logLevel: LogLevel { get { Log.logLevel } set { Log.logLevel = newValue } } - /** - * Array containing objects that hold External UserId parameters. - */ + /// Array containing objects that hold External UserId parameters. public var externalUserIdArray = [ExternalUserId]() + /// The singleton instance of the `Prebid` class. public static let shared = Prebid() + /// The version of the PrebidMobile SDK. public var version: String { PBMFunctions.sdkVersion() } + /// The version of the OM SDK. public var omsdkVersion: String { OMSDKVersionProvider.omSDKVersionString } // MARK: - Public Properties (Prebid) + /// The host for the Prebid Server. public var prebidServerHost: PrebidHost = .Custom { didSet { timeoutMillisDynamic = NSNumber(value: timeoutMillis) @@ -79,62 +92,69 @@ public class Prebid: NSObject { } } + /// Custom status endpoint for the Prebid Server. public var customStatusEndpoint: String? { didSet { PrebidSDKInitializer.setCustomStatusEndpoint(customStatusEndpoint) } } + /// Timeout for Prebid requests in milliseconds. public var timeoutMillis: Int { didSet { timeoutMillisDynamic = NSNumber(value: timeoutMillis) } } + /// Dynamic timeout value. public var timeoutMillisDynamic: NSNumber? + /// Stored auction response. public var storedAuctionResponse: String? // MARK: - Public Properties (SDK) - //Indicates whether the PBS should cache the bid for the rendering API. - //If the value is true the SDK will make the cache request in order to report - //the impression event respectively to the legacy analytic setup. + /// Indicates whether the PBS should cache the bid for the rendering API. + /// If the value is true the SDK will make the cache request in order to report + /// the impression event respectively to the legacy analytic setup. public var useCacheForReportingWithRenderingAPI = false - //Controls how long each creative has to load before it is considered a failure. + /// Controls how long each creative has to load before it is considered a failure. public var creativeFactoryTimeout: TimeInterval = 6.0 - //Controls how long video and interstitial creatives have to load before it is considered a failure. + /// Controls how long video and interstitial creatives have to load before it is considered a failure. public var creativeFactoryTimeoutPreRenderContent: TimeInterval = 30.0 - //Controls whether to use PrebidMobile's in-app browser or the Safari App for displaying ad clickthrough content. + /// Controls whether to use PrebidMobile's in-app browser or the Safari App for displaying ad clickthrough content. public var useExternalClickthroughBrowser = false - //Indicates the type of browser opened upon clicking the creative in an app, where embedded = 0, native = 1. - //Describes an [OpenRTB](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf) imp.clickbrowser attribute. + /// Indicates the type of browser opened upon clicking the creative in an app, where embedded = 0, native = 1. + /// Describes an [OpenRTB](https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf) imp.clickbrowser attribute. public var impClickbrowserType: ClickbrowserType = .native - //If set to true, the output of PrebidMobile's internal logger is written to a text file. This can be helpful for debugging. Defaults to false. + /// If set to true, the output of PrebidMobile's internal logger is written to a text file. This can be helpful for debugging. Defaults to false. public var debugLogFileEnabled: Bool { get { Log.logToFile } set { Log.logToFile = newValue } } - //If true, the SDK will periodically try to listen for location updates in order to request location-based ads. + /// If true, the SDK will periodically try to listen for location updates in order to request location-based ads. public var locationUpdatesEnabled: Bool { get { PBMLocationManager.shared.locationUpdatesEnabled } set { PBMLocationManager.shared.locationUpdatesEnabled = newValue } } - //If true, the sdk will add `includewinners` flag inside the targeting object described in [PBS Documentation](https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-auction.html#targeting) + /// If true, the sdk will add `includewinners` flag inside the targeting object described in [PBS Documentation](https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-auction.html#targeting) public var includeWinners = false - //If true, the sdk will add `includebidderkeys` flag inside the targeting object described in [PBS Documentation](https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-auction.html#targeting) + /// If true, the sdk will add `includebidderkeys` flag inside the targeting object described in [PBS Documentation](https://docs.prebid.org/prebid-server/endpoints/openrtb2/pbs-endpoint-auction.html#targeting) public var includeBidderKeys = false // MARK: - Public Methods + /// Sets a custom Prebid Server URL. + /// - Parameter url: The custom Prebid Server URL. + /// - Throws: An error if setting the custom host URL fails. public func setCustomPrebidServer(url: String) throws { prebidServerHost = .Custom try Host.shared.setCustomHostURL(url) @@ -142,14 +162,21 @@ public class Prebid: NSObject { // MARK: - Stored Bid Response + /// Adds a stored bid response. + /// - Parameters: + /// - bidder: The name of the bidder. + /// - responseId: The response ID. public func addStoredBidResponse(bidder: String, responseId: String) { storedBidResponses[bidder] = responseId } + /// Clears all stored bid responses. public func clearStoredBidResponses() { storedBidResponses.removeAll() } + /// Retrieves stored bid responses. + /// - Returns: An array of dictionaries containing stored bid responses, or nil if there are none. public func getStoredBidResponses() -> [[String: String]]? { var storedBidResponses: [[String: String]] = [] @@ -164,10 +191,15 @@ public class Prebid: NSObject { // MARK: - Custom Headers + /// Adds a custom HTTP header. + /// - Parameters: + /// - name: The name of the header. + /// - value: The value of the header. public func addCustomHeader(name: String, value: String) { customHeaders[name] = value } + /// Clears all custom HTTP headers. public func clearCustomHeaders() { customHeaders.removeAll() } diff --git a/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift b/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift index b7948dfb7..2867036da 100644 --- a/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift +++ b/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift @@ -18,7 +18,7 @@ import Foundation class PrebidGAMVersionChecker { var latestTestedGMAVersion: (Int, Int, Int) { - (11, 8, 0) + (11, 10, 0) } var currentGMAVersion: (Int, Int, Int)? diff --git a/PrebidMobile/ConfigurationAndTargeting/PrebidInitializationStatus.swift b/PrebidMobile/ConfigurationAndTargeting/PrebidInitializationStatus.swift index c90fb2670..6d67333b8 100644 --- a/PrebidMobile/ConfigurationAndTargeting/PrebidInitializationStatus.swift +++ b/PrebidMobile/ConfigurationAndTargeting/PrebidInitializationStatus.swift @@ -15,6 +15,9 @@ import Foundation +/// Enum representing the initialization status of the Prebid SDK. +/// +/// This enum provides various statuses that indicate the result of the Prebid SDK initialization process. It helps in understanding whether the SDK was successfully initialized or if there were issues during the initialization. @objc public enum PrebidInitializationStatus: Int { /// Prebid SDK successfully initialized. case succeeded diff --git a/PrebidMobile/ConfigurationAndTargeting/Targeting.swift b/PrebidMobile/ConfigurationAndTargeting/Targeting.swift index 8d46e5333..a45879c71 100644 --- a/PrebidMobile/ConfigurationAndTargeting/Targeting.swift +++ b/PrebidMobile/ConfigurationAndTargeting/Targeting.swift @@ -22,30 +22,37 @@ fileprivate let PrebidTargetingKey_GENDER = "gen" fileprivate let PrebidTargetingKey_USER_ID = "xid" fileprivate let PrebidTargetingKey_PUB_PROVIDED_PREFIX = "c." + +/// A class that manages targeting information for ads. +/// +/// This class provides properties and methods for setting and retrieving +/// user-specific targeting information, such as user ID, gender, and custom +/// data. It also includes details for OMID (Open Measurement Interface Definition) +/// partner and supports managing user identity links and custom extensions. +/// @objcMembers public class Targeting: NSObject { + /// A shared instance of the `Targeting` class. public static var shared = Targeting() // MARK: - OMID Partner + /// The name of the OMID partner. public var omidPartnerName: String? + /// The version of the OMID partner. public var omidPartnerVersion: String? // MARK: - User Information - /** - Indicates user birth year. - */ + /// Indicates user birth year. public var yearOfBirth: Int { get { yearofbirth } set { setYearOfBirth(yob: newValue) } } - /** - * This method set the year of birth value - */ + /// This method set the year of birth value public func setYearOfBirth(yob: Int) { if AgeUtils.isYOBValid(yob) { yearofbirth = yob @@ -54,21 +61,17 @@ public class Targeting: NSObject { } } - // Objective C API + /// Objective C API public func getYearOfBirth() -> NSNumber { NSNumber(value: yearOfBirth) } - /** - * This method clears year of birth value set by the application developer - */ + /// This method clears year of birth value set by the application developer public func clearYearOfBirth() { yearofbirth = 0 } - /** - Indicates the end-user's gender. - */ + /// Indicates the end-user's gender. public var userGender: Gender { get { guard let currentValue = parameterDictionary[PrebidTargetingKey_GENDER] else { @@ -83,10 +86,8 @@ public class Targeting: NSObject { } } - /** - String representation of the users gender, - where “M” = male, “F” = female, “O” = known to be other (i.e., omitted is unknown) - */ + /// String representation of the users gender, + /// where “M” = male, “F” = female, “O” = known to be other (i.e., omitted is unknown) public func userGenderDescription() -> String? { guard let currentValue = parameterDictionary[PrebidTargetingKey_GENDER] else { return nil @@ -95,52 +96,39 @@ public class Targeting: NSObject { return GenderDescription(rawValue: currentValue)?.rawValue } - /** - Indicates the customer-provided user ID, if different from the Device ID. - */ + /// Indicates the customer-provided user ID, if different from the Device ID. public var userID: String? { get { parameterDictionary[PrebidTargetingKey_USER_ID] } set { parameterDictionary[PrebidTargetingKey_USER_ID] = newValue } } - /** - Buyer-specific ID for the user as mapped by the exchange for the buyer. - */ + /// Buyer-specific ID for the user as mapped by the exchange for the buyer. public var buyerUID: String? - /** - Optional feature to pass bidder data that was set in the - exchange’s cookie. The string must be in base85 cookie safe - characters and be in any format. Proper JSON encoding must - be used to include “escaped” quotation marks. - */ + + /// Optional feature to pass bidder data that was set in the + /// exchange’s cookie. The string must be in base85 cookie safe + /// characters and be in any format. Proper JSON encoding must + /// be used to include “escaped” quotation marks. public var userCustomData: String? - /** - Placeholder for User Identity Links. - The data from this property will be added to usr.ext.eids - */ + /// Placeholder for User Identity Links. + /// The data from this property will be added to usr.ext.eids public var eids: [[String : AnyHashable]]? - /** - Placeholder for exchange-specific extensions to OpenRTB. - */ + /// Placeholder for exchange-specific extensions to OpenRTB. public var userExt: [String : AnyHashable]? // MARK: - COPPA - /** - Objective C analog of subjectToCOPPA - */ + /// Objective C analog of subjectToCOPPA public var coppa: NSNumber? { set { UserConsentDataManager.shared.subjectToCOPPA = newValue.boolValue } get { UserConsentDataManager.shared.subjectToCOPPA.nsNumberValue } } - /** - Integer flag indicating if this request is subject to the COPPA regulations - established by the USA FTC, where 0 = no, 1 = yes - */ + /// Integer flag indicating if this request is subject to the COPPA regulations + /// established by the USA FTC, where 0 = no, 1 = yes public var subjectToCOPPA: Bool? { set { UserConsentDataManager.shared.subjectToCOPPA = newValue} get { UserConsentDataManager.shared.subjectToCOPPA } @@ -148,28 +136,26 @@ public class Targeting: NSObject { // MARK: - GDPR - /** - * The boolean value set by the user to collect user data - */ + /// The boolean value set by the user to collect user data public var subjectToGDPR: Bool? { set { UserConsentDataManager.shared.subjectToGDPR = newValue } get { UserConsentDataManager.shared.subjectToGDPR } } + /// Objective-C API public func setSubjectToGDPR(_ newValue: NSNumber?) { UserConsentDataManager.shared.subjectToGDPR = newValue?.boolValue } + /// Objective-C API public func getSubjectToGDPR() -> NSNumber? { return UserConsentDataManager.shared.subjectToGDPR_NSNumber } // MARK: - GDPR Consent - /** - * The consent string for sending the GDPR consent - */ + /// The consent string for sending the GDPR consent public var gdprConsentString: String? { set { UserConsentDataManager.shared.gdprConsentString = newValue } get { UserConsentDataManager.shared.gdprConsentString } @@ -177,37 +163,40 @@ public class Targeting: NSObject { // MARK: - TCFv2 + /// The consent string for purposes consent as per TCFv2. public var purposeConsents: String? { set { UserConsentDataManager.shared.purposeConsents = newValue } get { UserConsentDataManager.shared.purposeConsents } } - /* - Purpose 1 - Store and/or access information on a device - */ + /// Purpose 1 - Store and/or access information on a device public func getDeviceAccessConsent() -> Bool? { UserConsentDataManager.shared.getDeviceAccessConsent() } + /// Returns whether the user has consented to access device data as an `NSNumber`. public func getDeviceAccessConsentObjc() -> NSNumber? { UserConsentDataManager.shared.getDeviceAccessConsent() as NSNumber? } + /// Returns the user's consent for a specific purpose by index. public func getPurposeConsent(index: Int) -> Bool? { UserConsentDataManager.shared.getPurposeConsent(index: index) } + /// Checks if access to device data is allowed. public func isAllowedAccessDeviceData() -> Bool { UserConsentDataManager.shared.isAllowedAccessDeviceData() } // MARK: - External User Ids + /// Array of external user IDs. + /// + /// This property holds the external user IDs associated with the user. public var externalUserIds = [ExternalUserId]() - /** - * This method allows to save External User Id in the User Defaults - */ + /// This method allows to save External User Id in the User Defaults public func storeExternalUserId(_ externalUserId: ExternalUserId) { if let index = externalUserIds.firstIndex(where: {$0.source == externalUserId.source}) { externalUserIds[index] = externalUserId @@ -217,16 +206,13 @@ public class Targeting: NSObject { StorageUtils.setExternalUserIds(value: externalUserIds) } - /** - * This method allows to get All External User Ids from User Defaults - */ + + /// This method allows to get All External User Ids from User Defaults public func fetchStoredExternalUserIds()->[ExternalUserId]? { return StorageUtils.getExternalUserIds() } - /** - * This method allows to get External User Id from User Defaults by passing respective 'source' string as param - */ + /// This method allows to get External User Id from User Defaults by passing respective 'source' string as param public func fetchStoredExternalUserId(_ source : String)->ExternalUserId? { guard let array = StorageUtils.getExternalUserIds(), let externalUserId = array.first(where: {$0.source == source}) else{ return nil @@ -234,9 +220,7 @@ public class Targeting: NSObject { return externalUserId } - /** - * This method allows to remove specific External User Id from User Defaults by passing respective 'source' string as param - */ + /// This method allows to remove specific External User Id from User Defaults by passing respective 'source' string as param public func removeStoredExternalUserId(_ source : String) { if let index = externalUserIds.firstIndex(where: {$0.source == source}) { externalUserIds.remove(at: index) @@ -244,9 +228,7 @@ public class Targeting: NSObject { } } - /** - * This method allows to remove all the External User Ids from User Defaults - */ + /// This method allows to remove all the External User Ids from User Defaults public func removeStoredExternalUserIds() { if var arrayExternalUserIds = StorageUtils.getExternalUserIds(){ arrayExternalUserIds.removeAll() @@ -275,62 +257,57 @@ public class Targeting: NSObject { // MARK: - Application Information - /** - This is the deep-link URL for the app screen that is displaying the ad. This can be an iOS universal link. - */ + /// This is the deep-link URL for the app screen that is displaying the ad. This can be an iOS universal link. public var contentUrl: String? - /** - App's publisher name. - */ + /// App's publisher name. public var publisherName: String? - /** - ID of publisher app in Apple’s App Store. - */ + /// ID of publisher app in Apple’s App Store. public var sourceapp: String? + /// App store URL for an installed app public var storeURL: String? { get { parameterDictionary[PBMParameterKeys.APP_STORE_URL.rawValue] } set { parameterDictionary[PBMParameterKeys.APP_STORE_URL.rawValue] = newValue } } + /// Domain name of the app public var domain: String? - /** - * The itunes app id for targeting - */ + /// The itunes app id for targeting public var itunesID: String? - /** - * The application location for targeting - */ + /// The application location for targeting public var location: CLLocation? - /** - * The application location precision for targeting - */ + /// The application location precision for targeting public var locationPrecision: Int? + /// Objective-C API public func setLocationPrecision(_ newValue: NSNumber?) { locationPrecision = newValue?.intValue } + /// Objective-C API public func getLocationPrecision() -> NSNumber? { return locationPrecision as NSNumber? } // MARK: - Location and connection information - /** - CLLocationCoordinate2D. - See CoreLocation framework documentation. - */ + /// CLLocationCoordinate2D. + /// See CoreLocation framework documentation. public var coordinate: NSValue? // MARK: - Public Methods + /// Adds a parameter to the parameter dictionary with a specified name. + /// + /// - Parameters: + /// - value: The value of the parameter. + /// - withName: The name of the parameter. If `nil`, the parameter is not added. public func addParam(_ value: String, withName: String?) { guard let name = withName else { Log.error("Invalid user parameter.") @@ -344,6 +321,9 @@ public class Targeting: NSObject { } } + /// Sets custom parameters by adding each key-value pair to the parameter dictionary. + /// + /// - Parameter params: A dictionary of parameters to set. If `nil`, no parameters are added. public func setCustomParams(_ params: [String : String]?) { guard let params = params else { return @@ -356,6 +336,11 @@ public class Targeting: NSObject { } } + /// Adds a custom parameter to the parameter dictionary with a prefixed name. + /// + /// - Parameters: + /// - value: The value of the custom parameter. + /// - withName: The name of the custom parameter. If `nil`, the parameter is not added. public func addCustomParam(_ value: String, withName: String?) { guard let name = withName else { return @@ -365,35 +350,53 @@ public class Targeting: NSObject { addParam(value, withName:prefixedName) } - // Store location in the user's section + /// Store location in the user's section public func setLatitude(_ latitude: Double, longitude: Double) { coordinate = NSValue(mkCoordinate: CLLocationCoordinate2DMake(latitude, longitude)) } // MARK: - Access Control List (ext.prebid.data) + /// Adds a bidder to the access control list. + /// + /// - Parameter bidderName: The name of the bidder to add. public func addBidderToAccessControlList(_ bidderName: String) { rawAccessControlList.insert(bidderName) } + /// Removes a bidder from the access control list. + /// + /// - Parameter bidderName: The name of the bidder to remove. public func removeBidderFromAccessControlList(_ bidderName: String) { rawAccessControlList.remove(bidderName) } + /// Clears all bidders from the access control list. public func clearAccessControlList() { rawAccessControlList.removeAll() } + /// Retrieves the current access control list. + /// + /// - Returns: An array of bidder names in the access control list. public func getAccessControlList() -> [String] { Array(rawAccessControlList) } + /// Access control list for external use. + /// + /// - Returns: An array of bidder names in the access control list. public var accessControlList: [String] { Array(rawAccessControlList) } // MARK: - Global User Data (user.ext.data) + /// Adds user data for a specified key. + /// + /// - Parameters: + /// - key: The key for the user data. + /// - value: The value to add for the specified key. public func addUserData(key: String, value: String) { var values = rawUserDataDictionary[key] ?? Set() values.insert(value) @@ -401,48 +404,77 @@ public class Targeting: NSObject { rawUserDataDictionary[key] = values } + /// Updates user data for a specified key with a new set of values. + /// + /// - Parameters: + /// - key: The key for the user data. + /// - value: The set of values to update for the specified key. public func updateUserData(key: String, value: Set) { rawUserDataDictionary[key] = value } + /// Removes user data for a specified key. + /// + /// - Parameter key: The key for the user data to remove. public func removeUserData(for key: String) { rawUserDataDictionary.removeValue(forKey: key) } + /// Clears all user data. public func clearUserData() { rawUserDataDictionary.removeAll() } + /// Retrieves all user data. + /// + /// - Returns: A dictionary mapping keys to arrays of values. public func getUserData() -> [String: [String]] { rawUserDataDictionary.mapValues { Array($0) } } + /// User data dictionary for external use. + /// + /// - Returns: A dictionary mapping keys to arrays of values. public var userDataDictionary: [String : [String]] { rawUserDataDictionary.mapValues { Array($0) } } // MARK: - Global User Keywords (user.keywords) + /// Adds a user keyword. + /// + /// - Parameter newElement: The keyword to add. public func addUserKeyword(_ newElement: String) { userKeywordsSet.insert(newElement) } + /// Adds multiple user keywords. + /// + /// - Parameter newElements: A set of keywords to add. public func addUserKeywords(_ newElements: Set) { userKeywordsSet.formUnion(newElements) } + /// Removes a user keyword. + /// + /// - Parameter element: The keyword to remove. public func removeUserKeyword(_ element: String) { userKeywordsSet.remove(element) } + /// Clears all user keywords. public func clearUserKeywords() { userKeywordsSet.removeAll() } + /// Retrieves all user keywords. + /// + /// - Returns: An array of user keywords. public func getUserKeywords() -> [String] { return Array(userKeywordsSet) } + /// Deprecated. Use `getUserKeywords` method instead. @available(*, deprecated, message: "This property is deprecated. Please, use getUserKeywords method instead.") public var userKeywords: [String] { Array(userKeywordsSet) @@ -450,36 +482,47 @@ public class Targeting: NSObject { // MARK: - Global Data (app.ext.data) + /// Deprecated. Use `addAppExtData` method instead. @available(*, deprecated, message: "This method is deprecated. Please, use addAppExtData method instead.") public func addContextData(key: String, value: String) { addAppExtData(key: key, value: value) } + /// Deprecated. Use `updateAppExtData` method instead. @available(*, deprecated, message: "This method is deprecated. Please, use updateAppExtData method instead.") public func updateContextData(key: String, value: Set) { updateAppExtData(key: key, value: value) } + /// Deprecated. Use `removeAppExtData` method instead. @available(*, deprecated, message: "This method is deprecated. Please, use removeAppExtData method instead.") public func removeContextData(for key: String) { removeAppExtData(for: key) } + /// Deprecated. Use `clearAppExtData` method instead. @available(*, deprecated, message: "This method is deprecated. Please, use clearAppExtData method instead.") public func clearContextData() { clearAppExtData() } + /// Deprecated. Use `getAppExtData` method instead. @available(*, deprecated, message: "This method is deprecated. Please, use getAppExtData method instead.") - public func getContextData() -> [String : [String]] { + public func getContextData() -> [String: [String]] { getAppExtData() } + /// Deprecated. Use `getAppExtData` method instead. @available(*, deprecated, message: "This property is deprecated. Please, use getAppExtData method instead.") - public var contextDataDictionary: [String : [String]] { + public var contextDataDictionary: [String: [String]] { getAppExtData() } + /// Adds application-specific data for a specified key. + /// + /// - Parameters: + /// - key: The key for the application data. + /// - value: The value to add for the specified key. public func addAppExtData(key: String, value: String) { var values = rawAppExtDataDictionary[key] ?? Set() values.insert(value) @@ -487,76 +530,108 @@ public class Targeting: NSObject { rawAppExtDataDictionary[key] = values } + /// Updates application-specific data for a specified key with a new set of values. + /// + /// - Parameters: + /// - key: The key for the application data. + /// - value: The set of values to update for the specified key. public func updateAppExtData(key: String, value: Set) { rawAppExtDataDictionary[key] = value } + /// Removes application-specific data for a specified key. + /// + /// - Parameter key: The key for the application data to remove. public func removeAppExtData(for key: String) { rawAppExtDataDictionary.removeValue(forKey: key) } + /// Clears all application-specific data. public func clearAppExtData() { rawAppExtDataDictionary.removeAll() } - public func getAppExtData() -> [String : [String]] { + /// Retrieves all application-specific data. + /// + /// - Returns: A dictionary mapping keys to arrays of values. + public func getAppExtData() -> [String: [String]] { rawAppExtDataDictionary.mapValues { Array($0) } } // MARK: - Global Keywords (app.keywords) + /// Deprecated. Use `addAppKeyword` method instead. @available(*, deprecated, message: "This method is deprecated. Please, use addAppKeyword method instead.") public func addContextKeyword(_ newElement: String) { addAppKeyword(newElement) } + /// Deprecated. Use `addAppKeywords` method instead. @available(*, deprecated, message: "This method is deprecated. Please, use addAppKeywords method instead.") public func addContextKeywords(_ newElements: Set) { addAppKeywords(newElements) } + /// Deprecated. Use `removeAppKeyword` method instead. @available(*, deprecated, message: "This method is deprecated. Please, use removeAppKeyword method instead.") public func removeContextKeyword(_ element: String) { removeAppKeyword(element) } + /// Deprecated. Use `clearAppKeywords` method instead. @available(*, deprecated, message: "This method is deprecated. Please, use clearAppKeywords method instead.") public func clearContextKeywords() { clearAppKeywords() } + /// Deprecated. Use `getAppKeywords` method instead. @available(*, deprecated, message: "This method is deprecated. Please, use getAppKeywords method instead.") public func getContextKeywords() -> [String] { getAppKeywords() } + /// Deprecated. Use `getAppKeywords` method instead. @available(*, deprecated, message: "This property is deprecated. Please, use getAppKeywords method instead.") public var contextKeywords: [String] { getAppKeywords() } + /// Adds an application keyword. + /// + /// - Parameter newElement: The keyword to add. public func addAppKeyword(_ newElement: String) { appKeywordsSet.insert(newElement) } + /// Adds multiple application keywords. + /// + /// - Parameter newElements: A set of keywords to add. public func addAppKeywords(_ newElements: Set) { appKeywordsSet.formUnion(newElements) } + /// Removes an application keyword. + /// + /// - Parameter element: The keyword to remove. public func removeAppKeyword(_ element: String) { appKeywordsSet.remove(element) } + /// Clears all application keywords. public func clearAppKeywords() { appKeywordsSet.removeAll() } + /// Retrieves all application keywords. + /// + /// - Returns: An array of application keywords. public func getAppKeywords() -> [String] { return Array(appKeywordsSet) } // MARK: - Internal Properties + /// Dictionary of parameters. public var parameterDictionary = [String : String]() private var userKeywordsSet = Set() diff --git a/PrebidMobile/ExternalUserId.swift b/PrebidMobile/ExternalUserId.swift index 7758dce86..01f8c5b8f 100644 --- a/PrebidMobile/ExternalUserId.swift +++ b/PrebidMobile/ExternalUserId.swift @@ -15,31 +15,37 @@ limitations under the License. import UIKit -/** - Defines the User Id Object from an External Thrid Party Source - */ +/// Defines the User Id Object from an External Thrid Party Source @objcMembers public class ExternalUserId: NSObject, NSCoding, NSSecureCoding, JSONConvertible { + /// Indicates whether the class supports secure coding. public static var supportsSecureCoding: Bool { true } // MARK: - Properties + + /// The source of the external user ID. public var source: String + + /// The identifier of the external user ID. public var identifier: String + + /// The type of the external user ID, represented as an optional `NSNumber`. public var atype: NSNumber? + + /// Additional attributes related to the external user ID, represented as an optional dictionary. public var ext: [String: Any]? // MARK: - Initialization - /** - Initialize ExternalUserId Class - - Parameter source: Source of the External User Id String. - - Parameter identifier: String of the External User Id. - - Parameter atype: (Optional) Int of the External User Id. - - Parameter ext: (Optional) Dictionary of the External User Id. - */ + + ///Initialize ExternalUserId Class + /// - Parameter source: Source of the External User Id String. + /// - Parameter identifier: String of the External User Id. + /// - Parameter atype: (Optional) Int of the External User Id. + /// - Parameter ext: (Optional) Dictionary of the External User Id. public init(source: String, identifier: String, atype: NSNumber? = nil, ext:[String: Any]? = nil) { self.source = source self.identifier = identifier @@ -48,6 +54,7 @@ public class ExternalUserId: NSObject, NSCoding, NSSecureCoding, JSONConvertible super.init() } + /// Encodes the properties of the class using the given encoder. public func encode(with coder: NSCoder) { coder.encode(self.source, forKey: "source") coder.encode(self.identifier, forKey: "identifier") @@ -55,6 +62,7 @@ public class ExternalUserId: NSObject, NSCoding, NSSecureCoding, JSONConvertible coder.encode(self.ext, forKey: "ext") } + /// Initializes an `ExternalUserId` instance from the given decoder. public required init?(coder: NSCoder) { self.source = coder.decodeObject(forKey: "source") as? String ?? "" self.identifier = coder.decodeObject(forKey: "identifier") as? String ?? "" @@ -62,6 +70,7 @@ public class ExternalUserId: NSObject, NSCoding, NSSecureCoding, JSONConvertible self.ext = coder.decodeObject(forKey: "ext") as? [String: Any] } + /// Converts the `ExternalUserId` instance to a JSON dictionary. public func toJSONDictionary() -> [AnyHashable: Any] { guard source.count != 0 && identifier.count != 0 else { return [:] diff --git a/PrebidMobile/Host.swift b/PrebidMobile/Host.swift index 06fb571bc..b6f1caaba 100644 --- a/PrebidMobile/Host.swift +++ b/PrebidMobile/Host.swift @@ -15,20 +15,19 @@ import UIKit +/// `PrebidHost` represents various Prebid server hosts used for ad bidding. @objc public enum PrebidHost: Int { - /** - URL [https://ib.adnxs.com/openrtb2/prebid](URL) - */ + /// URL [https://ib.adnxs.com/openrtb2/prebid](URL) case Appnexus - /** - URL [https://prebid-server.rubiconproject.com/openrtb2/auction](URL) - */ + /// URL [https://prebid-server.rubiconproject.com/openrtb2/auction](URL) case Rubicon + /// Custom Prebid server URL. The URL for this case should be set separately. case Custom + /// Returns the URL associated with the `PrebidHost` enum case. func name () -> String { switch self { case .Appnexus: return "https://ib.adnxs.com/openrtb2/prebid" @@ -38,24 +37,18 @@ import UIKit } } +/// A singleton class that manages the Prebid server URL, including a custom URL. @objcMembers public class Host: NSObject { private var customHostURL: URL? - /** - * The class is created as a singleton object & used - */ + /// The class is created as a singleton object & used public static let shared = Host() - override init() { + override init() {} - } - - /** - * The CustomHost property holds the URL for the custom prebid adaptor - */ - + /// The CustomHost property holds the URL for the custom prebid adaptor @objc public func setCustomHostURL(_ urlString: String?) throws { guard let url = URL.urlWithoutEncoding(from: urlString) else { throw ErrorCode.prebidServerURLInvalid(urlString ?? "") @@ -63,9 +56,7 @@ public class Host: NSObject { customHostURL = url } - /** - * This function retrieves the prebid server URL for the selected host - */ + /// This function retrieves the prebid server URL for the selected host public func getHostURL(host: PrebidHost) throws -> String { if (host == PrebidHost.Custom) { @@ -79,9 +70,7 @@ public class Host: NSObject { return host.name() } - /** - * This function verifies if the prebid server URL is in the url format - */ + /// This function verifies if the prebid server URL is in the url format public func verifyUrl (urlString: String?) -> Bool { //Check for nil if let urlString = urlString { @@ -94,11 +83,9 @@ public class Host: NSObject { } return false } - - /** - * This function used for unit testing to reset `customHostURL`. - * Internal only. - */ + + /// This function used for unit testing to reset `customHostURL`. + /// nternal only. func reset() { customHostURL = nil } diff --git a/PrebidMobile/PrebidMobileRendering/AdFormat.swift b/PrebidMobile/PrebidMobileRendering/AdFormat.swift index cd16486d4..15997bdf6 100644 --- a/PrebidMobile/PrebidMobileRendering/AdFormat.swift +++ b/PrebidMobile/PrebidMobileRendering/AdFormat.swift @@ -15,32 +15,50 @@ import Foundation +/// `AdFormat` is a class that represents different types of ad formats using an OptionSet. +/// The class also includes a deprecated display format for backward compatibility, marked with a deprecation message. @objcMembers public class AdFormat: NSObject, OptionSet { + /// The underlying type of the raw value. public typealias RawValue = Int + /// The raw integer value representing the ad format. public let rawValue: Int + /// The string representation of the ad format. public private(set) var stringEquivalent: String? + /// Initializes an `AdFormat` instance with a specified raw value and its string equivalent. + /// - Parameters: + /// - rawValue: The raw value representing the ad format. + /// - stringEquivalent: A string equivalent of the ad format. public convenience init(rawValue: RawValue, stringEquivalent: String) { self.init(rawValue: rawValue) self.stringEquivalent = stringEquivalent } + /// Initializes an `AdFormat` instance with a specified raw value. + /// - Parameter rawValue: The raw value representing the ad format. public required init(rawValue: RawValue) { self.rawValue = rawValue super.init() } - + + /// Represents a banner ad format. public static let banner = AdFormat(rawValue: 1 << 0, stringEquivalent: "banner") + + /// Represents a video ad format. public static let video = AdFormat(rawValue: 1 << 1, stringEquivalent: "video") + + /// Represents a native ad format. public static let native = AdFormat(rawValue: 1 << 2, stringEquivalent: "native") + /// Represents a deprecated display ad format. @available(*, deprecated, message: "Display ad format is deprecated. Please, use banner ad format instead.") public static let display = AdFormat(rawValue: 1 << 3, stringEquivalent: "banner") + /// An array containing all cases of ad formats, excluding deprecated ones. public static var allCases: [AdFormat] { [.banner, .video, .native] } diff --git a/PrebidMobile/PrebidMobileRendering/AdPosition.swift b/PrebidMobile/PrebidMobileRendering/AdPosition.swift index ad944ace6..a062c2fe1 100644 --- a/PrebidMobile/PrebidMobileRendering/AdPosition.swift +++ b/PrebidMobile/PrebidMobileRendering/AdPosition.swift @@ -15,25 +15,35 @@ import Foundation -//Ad position on screen. Refer to List 5.4: -//The following table specifies the position of the ad as a relative measure of visibility or prominence. This -//OpenRTB table has values derived from the Inventory Quality Guidelines (IQG). Practitioners should -//keep in sync with updates to the IQG values as published on IAB.com. Values “4” - “7” apply to apps per -//the mobile addendum to IQG version 2.1. -//Value Description -//0 Unknown -//1 Above the Fold -//2 DEPRECATED - May or may not be initially visible depending on screen size/resolution. -//3 Below the Fold -//4 Header -//5 Footer -//6 Sidebar -//7 Full Screen +/// Ad position on screen. Refer to List 5.4: +/// The following table specifies the position of the ad as a relative measure of visibility or prominence. This +/// OpenRTB table has values derived from the Inventory Quality Guidelines (IQG). Practitioners should +/// keep in sync with updates to the IQG values as published on IAB.com. Values “4” - “7” apply to apps per +/// the mobile addendum to IQG version 2.1. +/// Value Description +/// 0 Unknown +/// 1 Above the Fold +/// 2 DEPRECATED - May or may not be initially visible depending on screen size/resolution. +/// 3 Below the Fold +/// 4 Header +/// 5 Footer +/// 6 Sidebar +/// 7 Full Screen @objc(PBMAdPosition) public enum AdPosition: Int { - case undefined = 0 //0 Unknown - case header = 4 //4 Header - case footer = 5 //5 Footer - case sidebar = 6 //6 Sidebar - case fullScreen = 7 //7 Full Screen + + /// 0 Unknown + case undefined = 0 + + /// 4 Header + case header = 4 + + /// 5 Footer + case footer = 5 + + /// 6 Sidebar + case sidebar = 6 + + /// 7 Full Screen + case fullScreen = 7 } diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Position.swift b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Position.swift index 43d42af00..cd4a0fef6 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Position.swift +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Position.swift @@ -15,6 +15,9 @@ import Foundation +/// Enum representing various positions on the screen. +/// +/// This enum defines positions that can be used for placing elements within an ad. The positions are typically used to determine where controls or components should be located within the ad's user interface. @objc(PBMPosition) public enum Position: Int { case undefined = -1 @@ -27,6 +30,10 @@ public enum Position: Int { case bottomRight case custom + /// Returns the corresponding `Position` enum value for a given string literal. + /// + /// - Parameter stringValue: A string representing the position. + /// - Returns: The `Position` enum value if it matches one of the predefined cases; otherwise, returns `nil`. public static func getPositionByStringLiteral(_ stringValue: String) -> Position? { switch stringValue { case "topleft": diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/VideoControlsConfiguration.swift b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/VideoControlsConfiguration.swift index 23194e4cb..fd80e5f2b 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/VideoControlsConfiguration.swift +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/VideoControlsConfiguration.swift @@ -15,25 +15,22 @@ import Foundation +/// A class representing the configuration for video controls in an ad. +/// This includes properties for video duration, mute status, and button positioning and visibility. +/// Configuration values can be initialized from bid response or set directly by the user. @objc(PBMVideoControlsConfiguration) @objcMembers public class VideoControlsConfiguration: NSObject { - /** - This property indicates maximum video duration. - Obtained from the field ext,prebid.passthrough[].adConfiguration.maxvideoduration. - */ + /// This property indicates maximum video duration. + /// Obtained from the field ext,prebid.passthrough[].adConfiguration.maxvideoduration. private(set) public var maxVideoDuration: NSNumber? - /** - This property indicates whether the ad should run playback with sound or not. - Obtained from the field ext,prebid.passthrough[].adConfiguration.ismuted or set by user. - */ + /// This property indicates whether the ad should run playback with sound or not. + /// Obtained from the field ext,prebid.passthrough[].adConfiguration.ismuted or set by user. public var isMuted: Bool = false - /** - This property indicates the area which the close button should occupy on the screen. - Obtained from the field ext,prebid.passthrough[].adConfiguration.closebuttonarea or set by user. - */ + /// This property indicates the area which the close button should occupy on the screen. + /// Obtained from the field ext,prebid.passthrough[].adConfiguration.closebuttonarea or set by user. public var closeButtonArea: Double { set { if newValue <= 1 && newValue >= 0 { @@ -45,10 +42,8 @@ public class VideoControlsConfiguration: NSObject { get { _closeButtonArea } } - /** - This property indicates the position of the close button on the screen. - Obtained from the field ext,prebid.passthrough[].adConfiguration.closebuttonposition or set by user. - */ + /// This property indicates the position of the close button on the screen. + /// Obtained from the field ext,prebid.passthrough[].adConfiguration.closebuttonposition or set by user. public var closeButtonPosition: Position { set { if ![Position.topRight, Position.topLeft].contains(newValue) { @@ -61,10 +56,8 @@ public class VideoControlsConfiguration: NSObject { get { _closeButtonPosition } } - /** - This property indicates the area which the skip button should occupy on the screen. - Obtained from the field ext,prebid.passthrough[].adConfiguration.skipbuttonarea or set by user. - */ + /// This property indicates the area which the skip button should occupy on the screen. + /// Obtained from the field ext,prebid.passthrough[].adConfiguration.skipbuttonarea or set by user. public var skipButtonArea: Double { set { if newValue <= 1 && newValue >= 0 { @@ -77,10 +70,8 @@ public class VideoControlsConfiguration: NSObject { get { _skipButtonArea } } - /** - This property indicates the position of the skip button on the screen. - Obtained from the field ext,prebid.passthrough[].adConfiguration.skipbuttonposition or set by user. - */ + /// This property indicates the position of the skip button on the screen. + /// Obtained from the field ext,prebid.passthrough[].adConfiguration.skipbuttonposition or set by user. public var skipButtonPosition: Position { set { if ![Position.topRight, Position.topLeft].contains(newValue) { @@ -93,20 +84,14 @@ public class VideoControlsConfiguration: NSObject { get { _skipButtonPosition } } - /** - This property indicates the number of seconds which should be passed from the start of playback until the skip or close button should be shown. - Obtained from the field ext,prebid.passthrough[].adConfiguration.skipdelay or set by user. - */ + /// This property indicates the number of seconds which should be passed from the start of playback until the skip or close button should be shown. + /// Obtained from the field ext,prebid.passthrough[].adConfiguration.skipdelay or set by user. public var skipDelay = PBMConstants.SKIP_DELAY_DEFAULT.doubleValue - /** - This property indicates whether mute controls is visible on the screen. - */ + /// This property indicates whether mute controls is visible on the screen. public var isSoundButtonVisible = false - /** - Use to initialize video controls with server values. - */ + /// Use to initialize video controls with server values. public func initialize(with ortbAdConfiguration: PBMORTBAdConfiguration?) { guard let ortbAdConfiguration = ortbAdConfiguration else { diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerEventHandler.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerEventHandler.swift index eccdb0a21..678220e88 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerEventHandler.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerEventHandler.swift @@ -16,6 +16,9 @@ import Foundation import UIKit +/// A protocol for handling events related to banner ads in the PBM SDK. +/// +/// This protocol defines methods and properties for managing events associated with banner ads, including loading events, user interactions, and ad sizes. Implementing this protocol allows for custom handling of these events within the PBM SDK. @objc public protocol BannerEventHandler : PBMPrimaryAdRequesterProtocol { /// Delegate for custom event handler to inform the PBM SDK about the events related to the ad server communication. diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift index 5f4028764..d2db18d6d 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift @@ -17,48 +17,60 @@ import UIKit fileprivate let assertionMessageMainThread = "Expected to only be called on the main thread" +/// The view that will display the particular banner ad. Built for rendering type of integration. public class BannerView: UIView, BannerAdLoaderDelegate, AdLoadFlowControllerDelegate, BannerEventInteractionDelegate, DisplayViewInteractionDelegate { + /// The ad unit configuration. public let adUnitConfig: AdUnitConfig + + /// The event handler for banner view events. public let eventHandler: BannerEventHandler? // MARK: - Public Properties + /// Banner-specific parameters. @objc public var bannerParameters: BannerParameters { get { adUnitConfig.adConfiguration.bannerParameters } } + /// Video-specific parameters. @objc public var videoParameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } } + /// The last bid response received. @objc public var lastBidResponse: BidResponse? { adLoadFlowController?.bidResponse } + /// ID of Stored Impression on the Prebid server @objc public var configID: String { adUnitConfig.configId } + /// The interval for refreshing the ad. @objc public var refreshInterval: TimeInterval { get { adUnitConfig.refreshInterval } set { adUnitConfig.refreshInterval = newValue } } + /// Additional sizes for the ad. @objc public var additionalSizes: [CGSize]? { get { adUnitConfig.additionalSizes } set { adUnitConfig.additionalSizes = newValue } } + /// The ad format (e.g., banner, video). @objc public var adFormat: AdFormat { get { adUnitConfig.adFormats.first ?? .banner } set { adUnitConfig.adFormats = [newValue] } } + /// The position of the ad on the screen. @objc public var adPosition: AdPosition { get { adUnitConfig.adPosition } set { adUnitConfig.adPosition = newValue } @@ -69,6 +81,7 @@ public class BannerView: UIView, set { adUnitConfig.ortbConfig = newValue } } + /// ORTB configuration string. @objc public weak var delegate: BannerViewDelegate? /// Subscribe to plugin renderer events @@ -115,6 +128,12 @@ public class BannerView: UIView, // MARK: - Public Methods + /// Initializes a new `BannerView`. + /// - Parameters: + /// - frame: The frame rectangle for the view. + /// - configID: The configuration ID for the ad unit. + /// - adSize: The size of the ad. + /// - eventHandler: The event handler for the banner view. @objc public init(frame: CGRect, configID: String, adSize: CGSize, @@ -162,7 +181,10 @@ public class BannerView: UIView, }) } - + /// Convenience initializer for creating a `BannerView` with a configuration ID and event handler. + /// - Parameters: + /// - configID: The configuration ID for the ad unit. + /// - eventHandler: The event handler for the banner view. @objc public convenience init(configID: String, eventHandler: BannerEventHandler) { @@ -179,6 +201,11 @@ public class BannerView: UIView, } } + /// Convenience initializer for creating a `BannerView` with a frame, configuration ID, and ad size. + /// - Parameters: + /// - frame: The frame rectangle for the view. + /// - configID: The configuration ID for the ad unit. + /// - adSize: The size of the ad. @objc public convenience init(frame: CGRect, configID: String, adSize: CGSize) { @@ -196,14 +223,18 @@ public class BannerView: UIView, fatalError("init(coder:) has not been implemented") } + /// Loads the ad for the banner view. @objc public func loadAd() { adLoadFlowController?.refresh() } + /// Sets the stored auction response. + /// - Parameter storedAuction: The stored auction response string. @objc public func setStoredAuctionResponse(storedAuction:String){ Prebid.shared.storedAuctionResponse = storedAuction } + /// Stops the auto-refresh of the ad. @objc public func stopRefresh() { adLoadFlowController?.enqueueGatedBlock { [weak self] in self?.isRefreshStopped = true @@ -212,112 +243,162 @@ public class BannerView: UIView, // MARK: - Ext Data (imp[].ext.data) + /// Adds context data for a specified key. + /// - Parameters: + /// - data: The data to add. + /// - key: The key associated with the data. @available(*, deprecated, message: "This method is deprecated. Please, use addExtData method instead.") @objc public func addContextData(_ data: String, forKey key: String) { addExtData(key: key, value: data) } + /// Updates context data for a specified key. + /// - Parameters: + /// - data: A set of data to update. + /// - key: The key associated with the data. @available(*, deprecated, message: "This method is deprecated. Please, use updateExtData method instead.") @objc public func updateContextData(_ data: Set, forKey key: String) { updateExtData(key: key, value: data) } + /// Removes context data for a specified key. + /// - Parameter key: The key associated with the data to remove. @available(*, deprecated, message: "This method is deprecated. Please, use removeExtData method instead.") @objc public func removeContextDate(forKey key: String) { removeExtData(forKey: key) } + /// Clears all context data. @available(*, deprecated, message: "This method is deprecated. Please, use clearExtData method instead.") @objc public func clearContextData() { clearExtData() } + /// Adds ext data. + /// - Parameters: + /// - key: The key for the data. + /// - value: The value for the data. @objc public func addExtData(key: String, value: String) { adUnitConfig.addExtData(key: key, value: value) } + /// Updates ext data. + /// - Parameters: + /// - key: The key for the data. + /// - value: The value for the data. @objc public func updateExtData(key: String, value: Set) { adUnitConfig.updateExtData(key: key, value: value) } + /// Removes ext data. + /// - Parameters: + /// - key: The key for the data. @objc public func removeExtData(forKey: String) { adUnitConfig.removeExtData(for: forKey) } + /// Clears ext data. @objc public func clearExtData() { adUnitConfig.clearExtData() } // MARK: - Ext keywords (imp[].ext.keywords) + /// Adds a context keyword. + /// - Parameter newElement: The keyword to add. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeyword method instead.") @objc public func addContextKeyword(_ newElement: String) { addExtKeyword(newElement) } + /// Adds a set of context keywords. + /// - Parameter newElements: A set of keywords to add. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeywords method instead.") @objc public func addContextKeywords(_ newElements: Set) { addExtKeywords(newElements) } + /// Removes a context keyword. + /// - Parameter element: The keyword to remove. @available(*, deprecated, message: "This method is deprecated. Please, use removeExtKeyword method instead.") @objc public func removeContextKeyword(_ element: String) { removeExtKeyword(element) } + /// Clears all context keywords. @available(*, deprecated, message: "This method is deprecated. Please, use clearExtKeywords method instead.") @objc public func clearContextKeywords() { clearExtKeywords() } + /// Adds an extended keyword. + /// - Parameter newElement: The keyword to be added. @objc public func addExtKeyword(_ newElement: String) { adUnitConfig.addExtKeyword(newElement) } + /// Adds multiple extended keywords. + /// - Parameter newElements: A set of keywords to be added. @objc public func addExtKeywords(_ newElements: Set) { adUnitConfig.addExtKeywords(newElements) } + /// Removes an extended keyword. + /// - Parameter element: The keyword to be removed. @objc public func removeExtKeyword(_ element: String) { adUnitConfig.removeExtKeyword(element) } + /// Clears all extended keywords. @objc public func clearExtKeywords() { adUnitConfig.clearExtKeywords() } // MARK: - App Content (app.content.data) + /// Sets the app content data. + /// - Parameter appContent: The app content data. @objc public func setAppContent(_ appContent: PBMORTBAppContent) { adUnitConfig.setAppContent(appContent) } + /// Clears the app content data. @objc public func clearAppContent() { adUnitConfig.clearAppContent() } + /// Adds app content data objects. + /// - Parameter dataObjects: The data objects to be added. @objc public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { adUnitConfig.addAppContentData(dataObjects) } - + + /// Removes an app content data object. + /// - Parameter dataObject: The data object to be removed. @objc public func removeAppContentDataObject(_ dataObject: PBMORTBContentData) { adUnitConfig.removeAppContentData(dataObject) } + /// Clears all app content data objects. @objc public func clearAppContentDataObjects() { adUnitConfig.clearAppContentData() } // MARK: - User Data (user.data) + /// Adds user data objects. + /// - Parameter userDataObjects: The user data objects to be added. @objc public func addUserData(_ userDataObjects: [PBMORTBContentData]) { adUnitConfig.addUserData(userDataObjects) } + /// Removes a user data object. + /// - Parameter userDataObject: The user data object to be removed. @objc public func removeUserData(_ userDataObject: PBMORTBContentData) { adUnitConfig.removeUserData(userDataObject) } + /// Clears all user data objects. @objc public func clearUserData() { adUnitConfig.clearUserData() } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerViewDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerViewDelegate.swift index 8d066020b..f5638ce60 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerViewDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerViewDelegate.swift @@ -16,48 +16,38 @@ import Foundation import UIKit + +/// A protocol for handling events related to banner ads in the PBM SDK. +/// +/// This protocol defines methods and properties for managing events associated with banner ads, including loading events, user interactions, and ad sizes. Implementing this protocol allows for custom handling of these events within the PBM SDK. @objc public protocol BannerViewDelegate: NSObjectProtocol { - /** @name Methods */ - /*! - @abstract Asks the delegate for a view controller instance to use for presenting modal views - as a result of user interaction on an ad. Usual implementation may simply return self, - if it is view controller class. - */ + /// Asks the delegate for a view controller instance to use for presenting modal views + /// as a result of user interaction on an ad. Usual implementation may simply return self, + /// if it is view controller class. func bannerViewPresentationController() -> UIViewController? - /*! - @abstract Notifies the delegate that an ad has been successfully loaded and rendered. - @param bannerView The BannerView instance sending the message. - */ + /// Notifies the delegate that an ad has been successfully loaded and rendered. + /// - Parameter bannerView: The BannerView instance sending the message. @objc optional func bannerView(_ bannerView: BannerView, didReceiveAdWithAdSize adSize: CGSize) - /*! - @abstract Notifies the delegate of an error encountered while loading or rendering an ad. - @param bannerView The BannerView instance sending the message. - @param error The error encountered while attempting to receive or render the - ad. - */ + /// Notifies the delegate of an error encountered while loading or rendering an ad. + /// - Parameter bannerView: The BannerView instance sending the message. + /// - Parameter error: The error encountered while attempting to receive or render the @objc optional func bannerView(_ bannerView: BannerView, didFailToReceiveAdWith error: Error) - /*! - @abstract Notifies the delegate whenever current app goes in the background due to user click. - @param bannerView The BannerView instance sending the message. - */ + /// Notifies the delegate whenever current app goes in the background due to user click. + /// - Parameter bannerView: The BannerView instance sending the message. @objc optional func bannerViewWillLeaveApplication(_ bannerView: BannerView) - /*! - @abstract Notifies delegate that the banner view will launch a modal - on top of the current view controller, as a result of user interaction. - @param bannerView The BannerView instance sending the message. - */ + /// Notifies delegate that the banner view will launch a modal + /// on top of the current view controller, as a result of user interaction. + /// - Parameter bannerView The BannerView instance sending the message. @objc optional func bannerViewWillPresentModal(_ bannerView: BannerView) - /*! - @abstract Notifies delegate that the banner view has dismissed the modal on top of - the current view controller. - @param bannerView The BannerView instance sending the message. - */ + /// Notifies delegate that the banner view has dismissed the modal on top of + /// the current view controller. + /// - Parameter bannerView: The BannerView instance sending the message. @objc optional func bannerViewDidDismissModal(_ bannerView: BannerView) } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift index 796d93ef1..3d4938b09 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift @@ -16,6 +16,7 @@ import Foundation import UIKit +/// A base class for interstitial ad units. public class BaseInterstitialAdUnit : NSObject, PBMInterstitialAdLoaderDelegate, @@ -26,32 +27,39 @@ public class BaseInterstitialAdUnit : // MARK: - Public Properties + /// The banner parameters used for configuring ad unit. @objc public var bannerParameters: BannerParameters { get { adUnitConfig.adConfiguration.bannerParameters } } + /// The video parameters used for configuring ad unit. @objc public var videoParameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } } + /// The last bid response received for the ad unit. @objc public var lastBidResponse: BidResponse? { return adLoadFlowController?.bidResponse } + /// The configuration ID for the ad unit. @objc public var configID: String { adUnitConfig.configId } + /// The set of ad formats supported by this ad unit. @objc public var adFormats: Set { get { adUnitConfig.adFormats } set { adUnitConfig.adFormats = newValue } } + /// The ORTB (OpenRTB) configuration string for the ad unit. @objc public var ortbConfig: String? { get { adUnitConfig.ortbConfig } set { adUnitConfig.ortbConfig = newValue } } + /// A Boolean value indicating whether the ad unit is ready to be displayed. @objc public var isReady: Bool { objc_sync_enter(blocksLockToken) if let block = isReadyBlock { @@ -64,28 +72,34 @@ public class BaseInterstitialAdUnit : return false } + /// A Boolean value indicating whether the video controls are muted. @objc public var isMuted: Bool { get { adUnitConfig.adConfiguration.videoControlsConfig.isMuted } set { adUnitConfig.adConfiguration.videoControlsConfig.isMuted = newValue } } + /// A Boolean value indicating whether the sound button is visible in the video controls. @objc public var isSoundButtonVisible: Bool { get { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible } set { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible = newValue } } + /// The area of the close button in the video controls as a percentage. @objc public var closeButtonArea: Double { get { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonArea } set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonArea = newValue } } + /// The position of the close button in the video controls. @objc public var closeButtonPosition: Position { get { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition } set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition = newValue } } + /// A delegate for handling interactions with the ad unit. @objc public weak var delegate: AnyObject? + /// The configuration object for the ad unit. public let adUnitConfig: AdUnitConfig // MARK: - Private Properties @@ -102,6 +116,11 @@ public class BaseInterstitialAdUnit : // MARK: - Public Methods + /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID, minimum size percentage, and event handler. + /// - Parameters: + /// - configID: The unique identifier for the ad unit configuration. + /// - minSizePerc: The minimum size percentage for the ad unit. + /// - eventHandler: An optional event handler object for handling ad events. required public init(configID: String, minSizePerc: NSValue?, eventHandler: AnyObject?) { @@ -134,6 +153,11 @@ public class BaseInterstitialAdUnit : configValidationBlock: { _,_ in true } ) } + /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID, minimum size percentage, and event handler. + /// - Parameters: + /// - configID: The unique identifier for the ad unit configuration. + /// - minSizePercentage: The minimum size percentage for the ad unit. + /// - eventHandler: An optional event handler object for handling ad events. public convenience init(configID: String, minSizePercentage: CGSize, eventHandler:AnyObject?) @@ -143,6 +167,10 @@ public class BaseInterstitialAdUnit : eventHandler: eventHandler) } + /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID and event handler. + /// - Parameters: + /// - configID: The unique identifier for the ad unit configuration. + /// - eventHandler: An optional event handler object for handling ad events. public convenience init(configID: String, eventHandler:AnyObject?) { self.init(configID: configID, @@ -151,6 +179,10 @@ public class BaseInterstitialAdUnit : } + /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID and minimum size percentage. + /// - Parameters: + /// - configID: The unique identifier for the ad unit configuration. + /// - minSizePercentage: The minimum size percentage for the ad unit. public convenience init(configID: String, minSizePercentage:CGSize) { @@ -159,6 +191,8 @@ public class BaseInterstitialAdUnit : eventHandler: nil) } + /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID. + /// - Parameter configID: The unique identifier for the ad unit configuration. public convenience init(configID: String) { self.init(configID: configID, minSizePerc:nil, @@ -166,10 +200,14 @@ public class BaseInterstitialAdUnit : // MARK: - Public Methods + /// Loads a new ad. @objc public func loadAd() { adLoadFlowController.refresh() } + /// Shows the ad from a specified view controller. + /// - Parameter controller: The view controller from which the ad will be presented. + /// - Note: This method must be called on the main thread. @objc public func show(from controller: UIViewController) { // It is expected from the user to call this method on main thread assert(Thread.isMainThread, "Expected to only be called on the main thread"); @@ -194,118 +232,169 @@ public class BaseInterstitialAdUnit : // MARK: - Ext Data (imp[].ext.data) + /// Adds context data for a specified key. + /// - Parameters: + /// - data: The data to add. + /// - key: The key associated with the data. @available(*, deprecated, message: "This method is deprecated. Please, use addExtData method instead.") @objc public func addContextData(_ data: String, forKey key: String) { addExtData(key: key, value: data) } + /// Updates context data for a specified key. + /// - Parameters: + /// - data: A set of data to update. + /// - key: The key associated with the data. @available(*, deprecated, message: "This method is deprecated. Please, use updateExtData method instead.") @objc public func updateContextData(_ data: Set, forKey key: String) { updateExtData(key: key, value: data) } + /// Removes context data for a specified key. + /// - Parameter key: The key associated with the data to remove. @available(*, deprecated, message: "This method is deprecated. Please, use removeExtData method instead.") @objc public func removeContextDate(forKey key: String) { removeExtData(forKey: key) } + /// Clears all context data. @available(*, deprecated, message: "This method is deprecated. Please, use clearExtData method instead.") @objc public func clearContextData() { clearExtData() } + /// Adds ext data. + /// - Parameters: + /// - key: The key for the data. + /// - value: The value for the data. @objc public func addExtData(key: String, value: String) { adUnitConfig.addExtData(key: key, value: value) } + /// Updates ext data. + /// - Parameters: + /// - key: The key for the data. + /// - value: The value for the data. @objc public func updateExtData(key: String, value: Set) { adUnitConfig.updateExtData(key: key, value: value) } + /// Removes ext data. + /// - Parameters: + /// - key: The key for the data. @objc public func removeExtData(forKey: String) { adUnitConfig.removeExtData(for: forKey) } + /// Clears ext data. @objc public func clearExtData() { adUnitConfig.clearExtData() } // MARK: - Ext keywords (imp[].ext.keywords) + /// Adds a context keyword. + /// - Parameter newElement: The keyword to add. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeyword method instead.") @objc public func addContextKeyword(_ newElement: String) { addExtKeyword(newElement) } + /// Adds a set of context keywords. + /// - Parameter newElements: A set of keywords to add. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeywords method instead.") @objc public func addContextKeywords(_ newElements: Set) { addExtKeywords(newElements) } + /// Removes a context keyword. + /// - Parameter element: The keyword to remove. @available(*, deprecated, message: "This method is deprecated. Please, use removeExtKeyword method instead.") @objc public func removeContextKeyword(_ element: String) { removeExtKeyword(element) } + /// Clears all context keywords. @available(*, deprecated, message: "This method is deprecated. Please, use clearExtKeywords method instead.") @objc public func clearContextKeywords() { clearExtKeywords() } + /// Adds an extended keyword. + /// - Parameter newElement: The keyword to be added. @objc public func addExtKeyword(_ newElement: String) { adUnitConfig.addExtKeyword(newElement) } + /// Adds multiple extended keywords. + /// - Parameter newElements: A set of keywords to be added. @objc public func addExtKeywords(_ newElements: Set) { adUnitConfig.addExtKeywords(newElements) } + /// Removes an extended keyword. + /// - Parameter element: The keyword to be removed. @objc public func removeExtKeyword(_ element: String) { adUnitConfig.removeExtKeyword(element) } + /// Clears all extended keywords. @objc public func clearExtKeywords() { adUnitConfig.clearExtKeywords() } // MARK: - App Content (app.content.data) + /// Sets the app content data. + /// - Parameter appContent: The app content data. @objc public func setAppContent(_ appContent: PBMORTBAppContent) { adUnitConfig.setAppContent(appContent) } + /// Clears the app content data. @objc public func clearAppContent() { adUnitConfig.clearAppContent() } + /// Adds app content data objects. + /// - Parameter dataObjects: The data objects to be added. @objc public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { adUnitConfig.addAppContentData(dataObjects) } - + + /// Removes an app content data object. + /// - Parameter dataObject: The data object to be removed. @objc public func removeAppContentDataObject(_ dataObject: PBMORTBContentData) { adUnitConfig.removeAppContentData(dataObject) } + /// Clears all app content data objects. @objc public func clearAppContentDataObjects() { adUnitConfig.clearAppContentData() } // MARK: - User Data (user.data) + /// Adds user data objects. + /// - Parameter userDataObjects: The user data objects to be added. @objc public func addUserData(_ userDataObjects: [PBMORTBContentData]) { adUnitConfig.addUserData(userDataObjects) } + /// Removes a user data object. + /// - Parameter userDataObject: The user data object to be removed. @objc public func removeUserData(_ userDataObject: PBMORTBContentData) { adUnitConfig.removeUserData(userDataObject) } + /// Clears all user data objects. @objc public func clearUserData() { adUnitConfig.clearUserData() } // MARK: - PBMInterstitialAdLoaderDelegate + /// Internal delegate method. public func interstitialAdLoader(_ interstitialAdLoader: PBMInterstitialAdLoader, loadedAd showBlock: @escaping (UIViewController?) -> Void, isReadyBlock: @escaping () -> Bool) { @@ -317,63 +406,76 @@ public class BaseInterstitialAdUnit : reportLoadingSuccess() } + /// Internal delegate method. public func interstitialAdLoader(_ interstitialAdLoader: PBMInterstitialAdLoader, createdInterstitialController interstitialController: InterstitialController) { interstitialController.interactionDelegate = self } + /// The event handler for the interstitial events. public var eventHandler: Any? // MARK: - AdLoadFlowControllerDelegate + /// Called when the ad load flow controller is about to send a bid request. public func adLoadFlowControllerWillSendBidRequest(_ adLoadFlowController: PBMAdLoadFlowController) { // nop } + /// Called when the ad load flow controller is about to request the primary ad. public func adLoadFlowControllerWillRequestPrimaryAd(_ adLoadFlowController: PBMAdLoadFlowController) { callEventHandler_setInteractionDelegate() } + /// Called to determine if the ad load flow controller should continue with the current flow. public func adLoadFlowControllerShouldContinue(_ adLoadFlowController: PBMAdLoadFlowController) -> Bool { true } + /// Called when the ad load flow controller fails with an error. public func adLoadFlowController(_ adLoadFlowController: PBMAdLoadFlowController, failedWithError error: Error?) { reportLoadingFailed(with: error) } // MARK: - InterstitialControllerInteractionDelegate + /// Tracks an impression for the given interstitial controller. public func trackImpression(forInterstitialController: InterstitialController) { DispatchQueue.main.async { self.callEventHandler_trackImpression() } } + /// Called when the ad in the interstitial controller is clicked. public func interstitialControllerDidClickAd(_ interstitialController: InterstitialController) { assert(Thread.isMainThread, "Expected to only be called on the main thread") callDelegate_didClickAd() } + /// Called when the ad in the interstitial controller is closed. public func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { assert(Thread.isMainThread, "Expected to only be called on the main thread") callDelegate_didDismissAd() } + /// Called when the ad in the interstitial controller causes the app to leave. public func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) { assert(Thread.isMainThread, "Expected to only be called on the main thread") callDelegate_willLeaveApplication() } + /// Called when the interstitial controller displays an ad. public func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) { } + /// Called when the interstitial controller completes the ad display. public func interstitialControllerDidComplete(_ interstitialController: InterstitialController) { } + /// Provides the view controller to use for modal presentation. public func viewControllerForModalPresentation(fromInterstitialController: InterstitialController) -> UIViewController? { return targetController } @@ -394,12 +496,14 @@ public class BaseInterstitialAdUnit : // MARK: - InterstitialEventInteractionDelegate + /// Called when an ad is about to be presented. public func willPresentAd() { DispatchQueue.main.async { self.callDelegate_willPresentAd() } } + /// Called when an ad has been dismissed. public func didDismissAd() { objc_sync_enter(blocksLockToken) currentAdBlock = nil @@ -410,12 +514,14 @@ public class BaseInterstitialAdUnit : } } + /// Called when the ad causes the app to leave. public func willLeaveApp() { DispatchQueue.main.async { self.callDelegate_willLeaveApplication() } } + /// Called when an ad is clicked. public func didClickAd() { DispatchQueue.main.async { self.callDelegate_didClickAd() @@ -424,56 +530,69 @@ public class BaseInterstitialAdUnit : // MARK: - BaseInterstitialAdUnitProtocol + /// Requests an ad using the provided bid response. public func callEventHandler_requestAd(with bidResponse: BidResponse?) { } + /// Displays the ad using the provided view controller. public func callEventHandler_show(from controller: UIViewController?) { } // MARK: - Abstract Methods + /// Checks if the ad unit is ready to show an ad. public func callEventHandler_isReady() -> Bool { return false // to be overridden in subclass } + /// Notifies the delegate that an ad has been successfully received. public func callDelegate_didReceiveAd() { // to be overridden in subclass } + /// Notifies the delegate that the ad failed to load. public func callDelegate_didFailToReceiveAd(with: Error?) { // to be overridden in subclass } + /// Notifies the delegate that an ad is about to be presented. public func callDelegate_willPresentAd() { // to be overridden in subclass } + /// Notifies the delegate that an ad has been dismissed. public func callDelegate_didDismissAd() { // to be overridden in subclass } + /// Notifies the delegate that the app is about to leave due to an ad. public func callDelegate_willLeaveApplication() { // to be overridden in subclass } + /// Notifies the delegate that an ad has been clicked. public func callDelegate_didClickAd() { // to be overridden in subclass } + /// Sets the loading delegate for the event handler. public func callEventHandler_setLoadingDelegate(_ loadingDelegate: NSObject?) { // to be overridden in subclass } + /// Sets the interaction delegate for the event handler. public func callEventHandler_setInteractionDelegate() { // to be overridden in subclass } + /// Shows the ad from the provided view controller. public func callEventHandler_showFromViewController(controller: UIViewController?) { // to be overridden in subclass } + /// Tracks an impression for the ad. public func callEventHandler_trackImpression() { // to be overridden in subclass } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialAdUnitDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialAdUnitDelegate.swift index 33b9760ab..9842ce003 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialAdUnitDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialAdUnitDelegate.swift @@ -15,11 +15,10 @@ import Foundation -/*! - * Protocol for interaction with the InterstitialAdUnit . - * - * All messages will be invoked on the main thread. - */ + +/// Protocol for interaction with the InterstitialAdUnit . +/// +/// All messages will be invoked on the main thread. @objc public protocol InterstitialAdUnitDelegate: NSObjectProtocol { diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift index 0190c1290..e0d9c5488 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift @@ -15,30 +15,39 @@ import UIKit +/// Represents an interstitial ad unit. Built for rendering type of integration. @objcMembers public class InterstitialRenderingAdUnit: BaseInterstitialAdUnit { + /// The area of the skip button in the video controls, specified as a percentage of the screen width. @objc public var skipButtonArea: Double { get { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonArea } set { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonArea = newValue } } + /// The position of the skip button in the video controls. @objc public var skipButtonPosition: Position { get { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonPosition } set { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonPosition = newValue } } + /// The delay before the skip button appears, in seconds. @objc public var skipDelay: Double { get { adUnitConfig.adConfiguration.videoControlsConfig.skipDelay } set { adUnitConfig.adConfiguration.videoControlsConfig.skipDelay = newValue } } + /// Initializes a new interstitial rendering ad unit with the specified configuration ID. + /// - Parameter configID: The unique identifier for the ad unit configuration. @objc public init(configID: String) { super.init(configID: configID, minSizePerc: nil, eventHandler: InterstitialEventHandlerStandalone()) } + /// Initializes a new interstitial rendering ad unit with the specified configuration ID and minimum size percentage. + /// - Parameter configID: The unique identifier for the ad unit configuration. + /// - Parameter minSizePercentage: The minimum size percentage of the ad. @objc public init(configID: String, minSizePercentage: CGSize) { super.init( configID: configID, @@ -46,6 +55,10 @@ public class InterstitialRenderingAdUnit: BaseInterstitialAdUnit { eventHandler: InterstitialEventHandlerStandalone()) } + /// Initializes a new interstitial rendering ad unit with the specified configuration ID, minimum size percentage, and event handler. + /// - Parameter configID: The unique identifier for the ad unit configuration. + /// - Parameter minSizePercentage: The minimum size percentage of the ad. + /// - Parameter eventHandler: The event handler to manage ad events. @objc public init(configID: String, minSizePercentage: CGSize, eventHandler: AnyObject) { super.init( configID: configID, @@ -53,6 +66,10 @@ public class InterstitialRenderingAdUnit: BaseInterstitialAdUnit { eventHandler: eventHandler) } + /// Initializes a new interstitial rendering ad unit with the specified configuration ID, minimum size percentage, and event handler. + /// - Parameter configID: The unique identifier for the ad unit configuration. + /// - Parameter minSizePerc: The minimum size percentage of the ad. + /// - Parameter eventHandler: The event handler to manage ad events. @objc required init(configID: String, minSizePerc: NSValue?, eventHandler: AnyObject?) { super.init( configID: configID, @@ -62,62 +79,74 @@ public class InterstitialRenderingAdUnit: BaseInterstitialAdUnit { // MARK: - Protected overrides + /// Called when an ad is successfully received. @objc public override func callDelegate_didReceiveAd() { if let delegate = self.delegate as? InterstitialAdUnitDelegate { delegate.interstitialDidReceiveAd?(self) } } + /// Called when the ad fails to be received. @objc public override func callDelegate_didFailToReceiveAd(with error: Error?) { if let delegate = self.delegate as? InterstitialAdUnitDelegate { delegate.interstitial?(self, didFailToReceiveAdWithError: error) } } + /// Called when the ad will be presented. @objc public override func callDelegate_willPresentAd() { if let delegate = self.delegate as? InterstitialAdUnitDelegate { delegate.interstitialWillPresentAd?(self) } } + /// Called when the ad is dismissed. @objc public override func callDelegate_didDismissAd() { if let delegate = self.delegate as? InterstitialAdUnitDelegate { delegate.interstitialDidDismissAd?(self) } } + /// Called when the user will leave the application. @objc public override func callDelegate_willLeaveApplication() { if let delegate = self.delegate as? InterstitialAdUnitDelegate { delegate.interstitialWillLeaveApplication?(self) } } + /// Called when the ad is clicked. @objc public override func callDelegate_didClickAd() { if let delegate = self.delegate as? InterstitialAdUnitDelegate { delegate.interstitialDidClickAd?(self) } } - + + /// Checks if the ad is ready to be displayed. @objc public override func callEventHandler_isReady() -> Bool { interstitialEventHandler?.isReady ?? false } - + + /// Sets the loading delegate for the event handler. @objc public override func callEventHandler_setLoadingDelegate(_ loadingDelegate: NSObject?) { interstitialEventHandler?.loadingDelegate = loadingDelegate as? RewardedEventLoadingDelegate } + /// Sets the interaction delegate for the event handler. @objc public override func callEventHandler_setInteractionDelegate() { interstitialEventHandler?.interactionDelegate = self } + /// Requests an ad with the specified bid response @objc public override func callEventHandler_requestAd(with bidResponse: BidResponse?) { interstitialEventHandler?.requestAd(with: bidResponse) } + /// Shows the ad from the specified view controller. @objc public override func callEventHandler_show(from controller: UIViewController?) { interstitialEventHandler?.show(from: controller) } + /// Tracks an impression for the ad. @objc public override func callEventHandler_trackImpression() { interstitialEventHandler?.trackImpression?() } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift index fb5ba25e0..6a2ab7afb 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift @@ -15,14 +15,20 @@ import UIKit +/// Represents an rewarded ad unit. Built for rendering type of integration. @objc public class RewardedAdUnit: BaseInterstitialAdUnit, RewardedEventInteractionDelegate { + /// The reward object for the ad unit. @objc public private(set) var reward: NSObject? // MARK: - Lifecycle + /// Initializes a `RewardedAdUnit` with the given configuration ID and event handler. + /// + /// - Parameter configID: The configuration ID for the ad unit. + /// - Parameter eventHandler: The event handler for the ad unit. @objc public convenience init(configID: String, eventHandler: AnyObject) { self.init( configID: configID, @@ -30,6 +36,9 @@ public class RewardedAdUnit: BaseInterstitialAdUnit, eventHandler: eventHandler) } + /// Initializes a `RewardedAdUnit` with the given configuration ID and a default event handler. + /// + /// - Parameter configID: The configuration ID for the ad unit. @objc public convenience init(configID: String) { self.init( configID: configID, @@ -37,6 +46,11 @@ public class RewardedAdUnit: BaseInterstitialAdUnit, eventHandler: RewardedEventHandlerStandalone()) } + /// Initializes a `RewardedAdUnit` with the given configuration ID, minimum size percentage, and event handler. + /// + /// - Parameter configID: The configuration ID for the ad unit. + /// - Parameter minSizePerc: The minimum size percentage for the ad unit. + /// - Parameter eventHandler: The event handler for the ad unit. @objc required init(configID:String, minSizePerc: NSValue?, eventHandler: AnyObject?) { super.init( configID: configID, @@ -48,6 +62,10 @@ public class RewardedAdUnit: BaseInterstitialAdUnit, } // MARK: - PBMRewardedEventDelegate + + /// Called when the user earns a reward. + /// + /// - Parameter reward: The reward object associated with the event. @objc public func userDidEarnReward(_ reward: NSObject?) { DispatchQueue.main.async(execute: { [weak self] in self?.reward = reward @@ -57,6 +75,9 @@ public class RewardedAdUnit: BaseInterstitialAdUnit, // MARK: - BaseInterstitialAdUnitProtocol protocol + /// Called when the interstitial ad is closed. + /// + /// - Parameter interstitialController: The controller managing the interstitial ad. @objc public override func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { callDelegate_rewardedAdUserDidEarnReward() super.interstitialControllerDidCloseAd(interstitialController) @@ -64,42 +85,53 @@ public class RewardedAdUnit: BaseInterstitialAdUnit, // MARK: - Protected overrides + /// Called when the ad unit receives an ad. @objc public override func callDelegate_didReceiveAd() { if let delegate = self.delegate as? RewardedAdUnitDelegate { delegate.rewardedAdDidReceiveAd?(self) } } + /// Called when the ad unit fails to receive an ad. + /// + /// - Parameter error: The error describing the failure. @objc public override func callDelegate_didFailToReceiveAd(with error: Error?) { if let delegate = self.delegate as? RewardedAdUnitDelegate { delegate.rewardedAd?(self, didFailToReceiveAdWithError: error) } } + /// Called when the ad unit will present an ad. @objc public override func callDelegate_willPresentAd() { if let delegate = self.delegate as? RewardedAdUnitDelegate { delegate.rewardedAdWillPresentAd?(self) } } + /// Called when the ad unit dismisses an ad. @objc public override func callDelegate_didDismissAd() { if let delegate = self.delegate as? RewardedAdUnitDelegate { delegate.rewardedAdDidDismissAd?(self) } } + /// Called when the ad unit will leave the application. @objc public override func callDelegate_willLeaveApplication() { if let delegate = self.delegate as? RewardedAdUnitDelegate { delegate.rewardedAdWillLeaveApplication?(self) } } + /// Called when the ad unit is clicked. @objc public override func callDelegate_didClickAd() { if let delegate = self.delegate as? RewardedAdUnitDelegate { delegate.rewardedAdDidClickAd?(self) } } + /// Returns whether the event handler is ready. + /// + /// - Returns: A boolean indicating if the event handler is ready. @objc public override func callEventHandler_isReady() -> Bool { if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { return eventHandler.isReady @@ -108,30 +140,41 @@ public class RewardedAdUnit: BaseInterstitialAdUnit, } } + /// Sets the loading delegate for the event handler. + /// + /// - Parameter loadingDelegate: The loading delegate to set. @objc public override func callEventHandler_setLoadingDelegate(_ loadingDelegate: NSObject?) { if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { eventHandler.loadingDelegate = loadingDelegate as? RewardedEventLoadingDelegate } } + /// Sets the interaction delegate for the event handler. @objc public override func callEventHandler_setInteractionDelegate() { if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { eventHandler.interactionDelegate = self } } + /// Requests an ad with the given bid response. + /// + /// - Parameter bidResponse: The bid response to use for the ad request. @objc public override func callEventHandler_requestAd(with bidResponse: BidResponse?) { if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { eventHandler.requestAd(with: bidResponse) } } + /// Shows the ad from the specified view controller. + /// + /// - Parameter controller: The view controller from which to present the ad. @objc public override func callEventHandler_show(from controller: UIViewController?) { if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { eventHandler.show(from: controller) } } + /// Tracks the impression for the ad. @objc public override func callEventHandler_trackImpression() { if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { eventHandler.trackImpression?() @@ -140,6 +183,7 @@ public class RewardedAdUnit: BaseInterstitialAdUnit, // MARK: - Private helpers + /// Calls the delegate method to notify that the user has earned a reward. func callDelegate_rewardedAdUserDidEarnReward() { if let delegate = self.delegate as? RewardedAdUnitDelegate { delegate.rewardedAdUserDidEarnReward?(self) diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnitDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnitDelegate.swift index f4dfdbdcf..247ed78f2 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnitDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnitDelegate.swift @@ -15,11 +15,8 @@ import Foundation -/*! - * Protocol for interaction with the PBMRewardedAdUnit . - * - * All messages will be invoked on the main thread. - */ +/// Protocol for interaction with the RewardedAdUnit +/// All messages will be invoked on the main thread. @objc public protocol RewardedAdUnitDelegate: NSObjectProtocol { /// Called when an ad is loaded and ready for display diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventHandlerProtocol.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventHandlerProtocol.swift index 8699e7e0b..f44615211 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventHandlerProtocol.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventHandlerProtocol.swift @@ -15,7 +15,9 @@ import Foundation - +/// A protocol for handling events related to rewarded ads. +/// +/// This protocol extends `PBMInterstitialAd` and defines properties for delegates that handle events related to the ad server communication and user interactions with rewarded ads. Implementing this protocol allows for custom handling of these events within the rewarded ad lifecycle. @objc public protocol RewardedEventHandlerProtocol: PBMInterstitialAd { /// Delegate for custom event handler to inform the PBM SDK about the events related to the ad server communication. diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBannerAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBannerAdUnit.swift index ffe318625..8e6882482 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBannerAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBannerAdUnit.swift @@ -16,6 +16,8 @@ import Foundation import UIKit +/// This class is responsible for making bid request and providing the winning bid and targeting keywords to mediating SDKs. +/// This class is a part of Mediation API. @objcMembers public class MediationBannerAdUnit : NSObject { @@ -34,42 +36,51 @@ public class MediationBannerAdUnit : NSObject { var adUnitConfig: AdUnitConfig + /// Property that performs certain utilty work for the `MediationBannerAdUnit` public let mediationDelegate: PrebidMediationDelegate // MARK: - Computed properties + /// The configuration ID for an ad unit public var configID: String { adUnitConfig.configId } + /// The ad format for the ad unit. public var adFormat: AdFormat { get { adUnitConfig.adFormats.first ?? .banner } set { adUnitConfig.adFormats = [newValue] } } + /// The position of the ad on the screen. public var adPosition: AdPosition { get { adUnitConfig.adPosition } set { adUnitConfig.adPosition = newValue } } + /// Parameters for configuring banner ads. public var bannerParameters: BannerParameters { get { adUnitConfig.adConfiguration.bannerParameters } } + /// Parameters for configuring video ads. public var videoParameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } } + /// The refresh interval for the ad. public var refreshInterval: TimeInterval { get { adUnitConfig.refreshInterval } set { adUnitConfig.refreshInterval = newValue } } + /// Additional sizes for the ad unit. public var additionalSizes: [CGSize]? { get { adUnitConfig.additionalSizes } set { adUnitConfig.additionalSizes = newValue } } + /// OpenRTB configuration public var ortbConfig: String? { get { adUnitConfig.ortbConfig } set { adUnitConfig.ortbConfig = newValue } @@ -77,118 +88,171 @@ public class MediationBannerAdUnit : NSObject { // MARK: - Ext Data (imp[].ext.data) + /// This method obtains the context data keyword & value for adunit context targeting + /// if the key already exists the value will be appended to the list. No duplicates will be added @available(*, deprecated, message: "This method is deprecated. Please, use addExtData method instead.") - public func addContextData(_ data: String, forKey key: String) { - addExtData(key: key, value: data) + public func addContextData(key: String, value: String) { + addExtData(key: key, value: value) } - + + /// This method obtains the context data keyword & values for adunit context targeting + /// the values if the key already exist will be replaced with the new set of values @available(*, deprecated, message: "This method is deprecated. Please, use updateExtData method instead.") - public func updateContextData(_ data: Set, forKey key: String) { - updateExtData(key: key, value: data) + public func updateContextData(key: String, value: Set) { + updateExtData(key: key, value: value) } + /// This method allows to remove specific context data keyword & values set from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use removeExtData method instead.") - public func removeContextDate(forKey key: String) { - removeExtData(forKey: key) + public func removeContextData(forKey: String) { + removeExtData(forKey: forKey) } + /// This method allows to remove all context data set from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use clearExtData method instead.") public func clearContextData() { clearExtData() } + /// This method obtains the ext data keyword & value for adunit targeting. + /// If the key already exists the value will be appended to the list. No duplicates will be added public func addExtData(key: String, value: String) { adUnitConfig.addExtData(key: key, value: value) } + /// This method obtains the ext data keyword & values for adunit targeting. + /// The values if the key already exist will be replaced with the new set of values public func updateExtData(key: String, value: Set) { adUnitConfig.updateExtData(key: key, value: value) } + /// This method allows to remove specific ext data keyword & values set from adunit targeting public func removeExtData(forKey: String) { adUnitConfig.removeExtData(for: forKey) } + /// This method allows to remove all ext data set from adunit targeting public func clearExtData() { adUnitConfig.clearExtData() } // MARK: - Ext keywords (imp[].ext.keywords) + /// This method obtains the context keyword for adunit context targeting + /// Inserts the given element in the set if it is not already present. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeyword method instead.") - @objc public func addContextKeyword(_ newElement: String) { + public func addContextKeyword(_ newElement: String) { addExtKeyword(newElement) } + /// This method obtains the context keyword set for adunit context targeting + /// Adds the elements of the given set to the set. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeywords method instead.") - @objc public func addContextKeywords(_ newElements: Set) { + public func addContextKeywords(_ newElements: Set) { addExtKeywords(newElements) } + /// This method allows to remove specific context keyword from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use removeExtKeyword method instead.") - @objc public func removeContextKeyword(_ element: String) { + public func removeContextKeyword(_ element: String) { removeExtKeyword(element) } - + + /// This method allows to remove all keywords from the set of adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use clearExtKeywords method instead.") - @objc public func clearContextKeywords() { + public func clearContextKeywords() { clearExtKeywords() } + /// This method obtains the keyword for adunit targeting + /// Inserts the given element in the set if it is not already present. public func addExtKeyword(_ newElement: String) { adUnitConfig.addExtKeyword(newElement) } + /// This method obtains the keyword set for adunit targeting + /// Adds the elements of the given set to the set. public func addExtKeywords(_ newElements: Set) { adUnitConfig.addExtKeywords(newElements) } + /// This method allows to remove specific keyword from adunit targeting public func removeExtKeyword(_ element: String) { adUnitConfig.removeExtKeyword(element) } + /// This method allows to remove all keywords from the set of adunit targeting public func clearExtKeywords() { adUnitConfig.clearExtKeywords() } // MARK: - App Content (app.content.data) - public func setAppContent(_ appContent: PBMORTBAppContent) { - adUnitConfig.setAppContent(appContent) + /// Sets the app content object, replacing any existing content. + /// + /// - Parameter appContentObject: The `PBMORTBAppContent` object representing the app's content. + public func setAppContent(_ appContentObject: PBMORTBAppContent) { + adUnitConfig.setAppContent(appContentObject) + } + + /// Retrieves the current app content object. + /// + /// - Returns: The current `PBMORTBAppContent` object, or `nil` if no content is set. + public func getAppContent() -> PBMORTBAppContent? { + return adUnitConfig.getAppContent() } + /// Clears the current app content object. public func clearAppContent() { adUnitConfig.clearAppContent() } + /// Adds an array of content data objects to the app content. + /// + /// - Parameter dataObjects: An array of `PBMORTBContentData` objects to add. public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { adUnitConfig.addAppContentData(dataObjects) } - public func removeAppContentDataObject(_ dataObject: PBMORTBContentData) { + /// Removes a specific content data object from the app content. + /// + /// - Parameter dataObject: The `PBMORTBContentData` object to remove. + public func removeAppContentData(_ dataObject: PBMORTBContentData) { adUnitConfig.removeAppContentData(dataObject) } - public func clearAppContentDataObjects() { + /// Clears all content data objects from the app content. + public func clearAppContentData() { adUnitConfig.clearAppContentData() } // MARK: - User Data (user.data) + /// Adds an array of user data objects. + /// + /// - Parameter userDataObjects: An array of `PBMORTBContentData` objects to add to the user data. public func addUserData(_ userDataObjects: [PBMORTBContentData]) { adUnitConfig.addUserData(userDataObjects) } + /// Removes a specific user data object. + /// + /// - Parameter userDataObject: The `PBMORTBContentData` object to remove from the user data. public func removeUserData(_ userDataObject: PBMORTBContentData) { adUnitConfig.removeUserData(userDataObject) } + /// Clears all user data. public func clearUserData() { adUnitConfig.clearUserData() } // MARK: - Public Methods + /// Initializes a new mediation banner ad unit with the specified configuration ID, size, and mediation delegate. + /// - Parameter configID: The unique identifier for the ad unit configuration. + /// - Parameter size: The size of the ad. + /// - Parameter mediationDelegate: The delegate for handling mediation. public init(configID: String, size: CGSize, mediationDelegate: PrebidMediationDelegate) { adUnitConfig = AdUnitConfig(configId: configID, size: size) adUnitConfig.adConfiguration.bannerParameters.api = PrebidConstants.supportedRenderingBannerAPISignals @@ -219,6 +283,8 @@ public class MediationBannerAdUnit : NSObject { }) } + /// Makes bid request and setups mediation parameters. + /// - Parameter completion: The completion handler to call when the demand fetch is complete. public func fetchDemand(completion: ((ResultCode)->Void)?) { fetchDemand(connection: PrebidServerConnection.shared, @@ -227,10 +293,14 @@ public class MediationBannerAdUnit : NSObject { completion: completion) } + /// Stops the auto-refresh for the ad unit. public func stopRefresh() { isRefreshStopped = true } + /// Handles the event when the ad object fails to load an ad. + /// - Parameter adObject: The ad object that failed to load the ad. + /// - Parameter error: The error that occurred during the ad load. public func adObjectDidFailToLoadAd(adObject: UIView, with error: Error) { if adObject === self.adView || adObject === self.lastAdView { diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift index 0e153ec81..f67e23387 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift @@ -14,37 +14,45 @@  */ import Foundation +/// Base class for interstitial ads in Mediation API. @objcMembers public class MediationBaseInterstitialAdUnit : NSObject { + /// Parameters for configuring banner ads. public var bannerParameters: BannerParameters { get { adUnitConfig.adConfiguration.bannerParameters } } + /// Parameters for configuring video ads. public var videoParameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } } + /// Indicates whether the video ad is muted. public var isMuted: Bool { get { adUnitConfig.adConfiguration.videoControlsConfig.isMuted } set { adUnitConfig.adConfiguration.videoControlsConfig.isMuted = newValue } } + /// Indicates whether the sound button is visible in the video ad. public var isSoundButtonVisible: Bool { get { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible } set { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible = newValue } } - + + /// The area for the close button in the video ad. public var closeButtonArea: Double { get { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonArea } set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonArea = newValue } } - + + /// The position of the close button in the video ad. public var closeButtonPosition: Position { get { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition } set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition = newValue } } + /// OpenRTB configuration for the ad unit. public var ortbConfig: String? { get { adUnitConfig.ortbConfig } set { adUnitConfig.ortbConfig = newValue } @@ -52,6 +60,7 @@ public class MediationBaseInterstitialAdUnit : NSObject { let adUnitConfig: AdUnitConfig + /// The configuration ID for the ad unit. public var configId: String { adUnitConfig.configId } @@ -74,6 +83,9 @@ public class MediationBaseInterstitialAdUnit : NSObject { videoParameters.placement = .Interstitial } + /// Makes bid request and setups mediation parameters. + /// - Parameters: + /// - completion: A closure called with the result code indicating the outcome of the demand fetch. public func fetchDemand(completion: ((ResultCode)->Void)?) { fetchDemand(connection: PrebidServerConnection.shared, sdkConfiguration: Prebid.shared, @@ -83,112 +95,154 @@ public class MediationBaseInterstitialAdUnit : NSObject { // MARK: - Ext Data (imp[].ext.data) + /// This method obtains the context data keyword & value for adunit context targeting + /// if the key already exists the value will be appended to the list. No duplicates will be added @available(*, deprecated, message: "This method is deprecated. Please, use addExtData method instead.") - public func addContextData(_ data: String, forKey key: String) { - adUnitConfig.addExtData(key: key, value: data) + public func addContextData(key: String, value: String) { + addExtData(key: key, value: value) } - + + /// This method obtains the context data keyword & values for adunit context targeting + /// the values if the key already exist will be replaced with the new set of values @available(*, deprecated, message: "This method is deprecated. Please, use updateExtData method instead.") - public func updateContextData(_ data: Set, forKey key: String) { - adUnitConfig.updateExtData(key: key, value: data) + public func updateContextData(key: String, value: Set) { + updateExtData(key: key, value: value) } + /// This method allows to remove specific context data keyword & values set from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use removeExtData method instead.") - public func removeContextDate(forKey key: String) { - adUnitConfig.removeExtData(for: key) + public func removeContextData(forKey: String) { + removeExtData(forKey: forKey) } + /// This method allows to remove all context data set from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use clearExtData method instead.") public func clearContextData() { - adUnitConfig.clearExtData() + clearExtData() } + /// This method obtains the ext data keyword & value for adunit targeting. + /// If the key already exists the value will be appended to the list. No duplicates will be added public func addExtData(key: String, value: String) { adUnitConfig.addExtData(key: key, value: value) } + /// This method obtains the ext data keyword & values for adunit targeting. + /// The values if the key already exist will be replaced with the new set of values public func updateExtData(key: String, value: Set) { adUnitConfig.updateExtData(key: key, value: value) } + /// This method allows to remove specific ext data keyword & values set from adunit targeting public func removeExtData(forKey: String) { adUnitConfig.removeExtData(for: forKey) } + /// This method allows to remove all ext data set from adunit targeting public func clearExtData() { adUnitConfig.clearExtData() } // MARK: - Ext keywords (imp[].ext.keywords) + /// This method obtains the context keyword for adunit context targeting + /// Inserts the given element in the set if it is not already present. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeyword method instead.") - @objc public func addContextKeyword(_ newElement: String) { - adUnitConfig.addExtKeyword(newElement) + public func addContextKeyword(_ newElement: String) { + addExtKeyword(newElement) } + /// This method obtains the context keyword set for adunit context targeting + /// Adds the elements of the given set to the set. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeywords method instead.") - @objc public func addContextKeywords(_ newElements: Set) { - adUnitConfig.addExtKeywords(newElements) + public func addContextKeywords(_ newElements: Set) { + addExtKeywords(newElements) } + /// This method allows to remove specific context keyword from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use removeExtKeyword method instead.") - @objc public func removeContextKeyword(_ element: String) { - adUnitConfig.removeExtKeyword(element) + public func removeContextKeyword(_ element: String) { + removeExtKeyword(element) } - + + /// This method allows to remove all keywords from the set of adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use clearExtKeywords method instead.") - @objc public func clearContextKeywords() { - adUnitConfig.clearExtKeywords() + public func clearContextKeywords() { + clearExtKeywords() } + /// This method obtains the keyword for adunit targeting + /// Inserts the given element in the set if it is not already present. public func addExtKeyword(_ newElement: String) { adUnitConfig.addExtKeyword(newElement) } + /// This method obtains the keyword set for adunit targeting + /// Adds the elements of the given set to the set. public func addExtKeywords(_ newElements: Set) { adUnitConfig.addExtKeywords(newElements) } + /// This method allows to remove specific keyword from adunit targeting public func removeExtKeyword(_ element: String) { adUnitConfig.removeExtKeyword(element) } + /// This method allows to remove all keywords from the set of adunit targeting public func clearExtKeywords() { adUnitConfig.clearExtKeywords() } // MARK: - App Content (app.content.data) - public func setAppContent(_ appContent: PBMORTBAppContent) { - adUnitConfig.setAppContent(appContent) + /// Sets the app content object, replacing any existing content. + /// + /// - Parameter appContentObject: The `PBMORTBAppContent` object representing the app's content. + public func setAppContent(_ appContentObject: PBMORTBAppContent) { + adUnitConfig.setAppContent(appContentObject) } + /// Clears the current app content object. public func clearAppContent() { adUnitConfig.clearAppContent() } + /// Adds an array of content data objects to the app content. + /// + /// - Parameter dataObjects: An array of `PBMORTBContentData` objects to add. public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { adUnitConfig.addAppContentData(dataObjects) } - public func removeAppContentDataObject(_ dataObject: PBMORTBContentData) { + /// Removes a specific content data object from the app content. + /// + /// - Parameter dataObject: The `PBMORTBContentData` object to remove. + public func removeAppContentData(_ dataObject: PBMORTBContentData) { adUnitConfig.removeAppContentData(dataObject) } - public func clearAppContentDataObjects() { + /// Clears all content data objects from the app content. + public func clearAppContentData() { adUnitConfig.clearAppContentData() } // MARK: - User Data (user.data) + /// Adds an array of user data objects. + /// + /// - Parameter userDataObjects: An array of `PBMORTBContentData` objects to add to the user data. public func addUserData(_ userDataObjects: [PBMORTBContentData]) { adUnitConfig.addUserData(userDataObjects) } + /// Removes a specific user data object. + /// + /// - Parameter userDataObject: The `PBMORTBContentData` object to remove from the user data. public func removeUserData(_ userDataObject: PBMORTBContentData) { adUnitConfig.removeUserData(userDataObject) } + /// Clears all user data. public func clearUserData() { adUnitConfig.clearUserData() } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationInterstitialAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationInterstitialAdUnit.swift index c27a2b75f..698ec72e4 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationInterstitialAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationInterstitialAdUnit.swift @@ -16,31 +16,38 @@ import Foundation import UIKit +/// This class is responsible for making bid request and providing the winning bid and targeting keywords to mediating SDKs. +/// This class is a part of Mediation API. @objcMembers -public class MediationInterstitialAdUnit : MediationBaseInterstitialAdUnit { +public class MediationInterstitialAdUnit: MediationBaseInterstitialAdUnit { // MARK: - Public Properties + /// The ad format for the ad unit. public var adFormats: Set { get { adUnitConfig.adFormats } set { adUnitConfig.adFormats = newValue } } + /// Additional sizes for the ad unit. public var additionalSizes: [CGSize]? { get { adUnitConfig.additionalSizes } set { adUnitConfig.additionalSizes = newValue } } + /// The area for the skip button in the video ad. public var skipButtonArea: Double { get { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonArea } set { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonArea = newValue } } + /// The position of the skip button in the video ad. public var skipButtonPosition: Position { get { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonPosition } set { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonPosition = newValue } } + /// The delay before the skip button appears in the video ad. public var skipDelay: Double { get { adUnitConfig.adConfiguration.videoControlsConfig.skipDelay } set { adUnitConfig.adConfiguration.videoControlsConfig.skipDelay = newValue } @@ -48,10 +55,19 @@ public class MediationInterstitialAdUnit : MediationBaseInterstitialAdUnit { // MARK: - Public Methods + /// Convenience initializer for the mediation interstitial ad unit. + /// - Parameters: + /// - configId: The unique identifier for the ad unit configuration. + /// - mediationDelegate: The delegate for handling mediation. public override convenience init(configId: String, mediationDelegate: PrebidMediationDelegate) { self.init(configId: configId, minSizePercentage: nil, mediationDelegate: mediationDelegate) } + /// Initializes a new mediation interstitial ad unit with the specified configuration ID, minimum size percentage, and mediation delegate. + /// - Parameters: + /// - configId: The unique identifier for the ad unit configuration. + /// - minSizePercentage: The minimum size percentage for the ad. + /// - mediationDelegate: The delegate for handling mediation. public init(configId: String, minSizePercentage: CGSize?, mediationDelegate: PrebidMediationDelegate) { super.init(configId: configId, mediationDelegate: mediationDelegate) @@ -62,6 +78,7 @@ public class MediationInterstitialAdUnit : MediationBaseInterstitialAdUnit { // MARK: - Computed Properties + /// The configuration ID for the ad unit. public override var configId: String { adUnitConfig.configId } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationNativeAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationNativeAdUnit.swift index 2ac556bc3..9fc8564a0 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationNativeAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationNativeAdUnit.swift @@ -15,6 +15,8 @@ import Foundation +/// This class is responsible for making bid request and providing the winning bid and targeting keywords to mediating SDKs. +/// This class is a part of Mediation API. @objcMembers public class MediationNativeAdUnit : NSObject { @@ -23,178 +25,247 @@ public class MediationNativeAdUnit : NSObject { // MARK: - Public Properties + /// The native ad unit that makes native request. public var nativeAdUnit: NativeRequest var configID: String // MARK: - Public Methods + /// Initializes a new instance of the `MediationNativeAdUnit` with the specified configuration ID and mediation delegate. + /// - Parameters: + /// - configId: The configuration ID for the ad unit. + /// - mediationDelegate: The delegate for mediation-related tasks. public init(configId: String, mediationDelegate: PrebidMediationDelegate) { self.configID = configId self.mediationDelegate = mediationDelegate self.nativeAdUnit = NativeRequest(configId: configId) } + /// Adds event trackers to the native ad unit. + /// - Parameter eventTrackers: An array of `NativeEventTracker` objects to add. public func addEventTracker(_ eventTrackers: [NativeEventTracker]) { nativeAdUnit.addNativeEventTracker(eventTrackers) } + /// Adds native assets to the native ad unit. + /// - Parameter assets: An array of `NativeAsset` objects to add. public func addNativeAssets(_ assets: [NativeAsset]) { nativeAdUnit.addNativeAssets(assets) } + /// Sets the context type for the native ad unit. + /// - Parameter contextType: The context type to set. public func setContextType(_ contextType: ContextType) { nativeAdUnit.context = contextType } + /// Sets the placement type for the native ad unit. + /// - Parameter placementType: The placement type to set. public func setPlacementType(_ placementType: PlacementType) { nativeAdUnit.placementType = placementType } + /// Sets the placement count for the native ad unit. + /// - Parameter placementCount: The placement count to set. public func setPlacementCount(_ placementCount: Int) { nativeAdUnit.placementCount = placementCount } + /// Sets the context subtype for the native ad unit. + /// - Parameter contextSubType: The context subtype to set. public func setContextSubType(_ contextSubType: ContextSubType) { nativeAdUnit.contextSubType = contextSubType } + /// Sets the sequence for the native ad unit. + /// - Parameter sequence: The sequence to set. public func setSequence(_ sequence: Int) { nativeAdUnit.sequence = sequence } + /// Sets the asset URL support for the native ad unit. + /// - Parameter assetURLSupport: The asset URL support value to set. public func setAssetURLSupport(_ assetURLSupport: Int) { nativeAdUnit.asseturlsupport = assetURLSupport } + /// Sets the DURL support for the native ad unit. + /// - Parameter dURLSupport: The DURL support value to set. public func setDURLSupport(_ dURLSupport: Int) { nativeAdUnit.durlsupport = dURLSupport } + /// Sets the privacy value for the native ad unit. + /// - Parameter privacy: The privacy value to set. public func setPrivacy(_ privacy: Int) { nativeAdUnit.privacy = privacy } + /// Sets the extended data for the native ad unit. + /// - Parameter ext: A dictionary containing the extended data to set. public func setExt(_ ext: [String: Any]) { nativeAdUnit.ext = ext } + /// Retrieves the OpenRTB configuration for the native ad unit. + /// - Returns: The OpenRTB configuration as a string, or `nil` if not set. public func getOrtbConfig() -> String? { return nativeAdUnit.getOrtbConfig() } + /// Sets the OpenRTB configuration for the native ad unit. + /// - Parameter ortbConfig: The OpenRTB configuration to set. public func setOrtbConfig(_ ortbConfig: String?) { nativeAdUnit.setOrtbConfig(ortbConfig) } // MARK: - App Content (app.content.data) + /// Sets the app content object, replacing any existing content. + /// - Parameter appContent: The `PBMORTBAppContent` object representing the app's content. public func setAppContent(_ appContent: PBMORTBAppContent) { nativeAdUnit.setAppContent(appContent) } + /// Clears the current app content object. public func clearAppContent() { nativeAdUnit.clearAppContent() } + /// Adds an array of content data objects to the app content. + /// - Parameter dataObjects: An array of `PBMORTBContentData` objects to add. public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { nativeAdUnit.addAppContentData(dataObjects) } + /// Removes a specific content data object from the app content. + /// - Parameter dataObject: The `PBMORTBContentData` object to remove. public func removeAppContent(_ dataObject: PBMORTBContentData) { nativeAdUnit.removeAppContentData(dataObject) } // MARK: - User Data (user.data) + /// Adds an array of user data objects. + /// - Parameter userDataObjects: An array of `PBMORTBContentData` objects to add to the user data. public func addUserData(_ userDataObjects: [PBMORTBContentData]) { nativeAdUnit.addUserData(userDataObjects) } + /// Removes a specific user data object. + /// - Parameter userDataObject: The `PBMORTBContentData` object to remove from the user data. public func removeUserData(_ userDataObject: PBMORTBContentData) { nativeAdUnit.removeUserData(userDataObject) } + /// Clears all user data. public func clearUserData() { nativeAdUnit.clearUserData() } // MARK: - Ext Data (imp[].ext.data) + /// This method obtains the context data keyword & value for adunit context targeting + /// if the key already exists the value will be appended to the list. No duplicates will be added @available(*, deprecated, message: "This method is deprecated. Please, use addExtData method instead.") - public func addContextData(_ data: String, forKey key: String) { - addExtData(key: key, value: data) + public func addContextData(key: String, value: String) { + addExtData(key: key, value: value) } - + + /// This method obtains the context data keyword & values for adunit context targeting + /// the values if the key already exist will be replaced with the new set of values @available(*, deprecated, message: "This method is deprecated. Please, use updateExtData method instead.") - public func updateContextData(_ data: Set, forKey key: String) { - updateExtData(key: key, value: data) + public func updateContextData(key: String, value: Set) { + updateExtData(key: key, value: value) } + /// This method allows to remove specific context data keyword & values set from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use removeExtData method instead.") - public func removeContextDate(forKey key: String) { - removeExtData(forKey: key) + public func removeContextData(forKey: String) { + removeExtData(forKey: forKey) } + /// This method allows to remove all context data set from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use clearExtData method instead.") public func clearContextData() { clearExtData() } + /// This method obtains the ext data keyword & value for adunit targeting. + /// If the key already exists the value will be appended to the list. No duplicates will be added public func addExtData(key: String, value: String) { nativeAdUnit.addExtData(key: key, value: value) } + /// This method obtains the ext data keyword & values for adunit targeting. + /// The values if the key already exist will be replaced with the new set of values public func updateExtData(key: String, value: Set) { nativeAdUnit.updateExtData(key: key, value: value) } + /// This method allows to remove specific ext data keyword & values set from adunit targeting public func removeExtData(forKey: String) { nativeAdUnit.removeExtData(forKey: forKey) } + /// This method allows to remove all ext data set from adunit targeting public func clearExtData() { nativeAdUnit.clearExtData() } // MARK: - Ext keywords (imp[].ext.keywords) + /// This method obtains the context keyword for adunit context targeting + /// Inserts the given element in the set if it is not already present. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeyword method instead.") - @objc public func addContextKeyword(_ newElement: String) { + public func addContextKeyword(_ newElement: String) { addExtKeyword(newElement) } + /// This method obtains the context keyword set for adunit context targeting + /// Adds the elements of the given set to the set. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeywords method instead.") - @objc public func addContextKeywords(_ newElements: Set) { + public func addContextKeywords(_ newElements: Set) { addExtKeywords(newElements) } + /// This method allows to remove specific context keyword from adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use removeExtKeyword method instead.") - @objc public func removeContextKeyword(_ element: String) { + public func removeContextKeyword(_ element: String) { removeExtKeyword(element) } - + + /// This method allows to remove all keywords from the set of adunit context targeting @available(*, deprecated, message: "This method is deprecated. Please, use clearExtKeywords method instead.") - @objc public func clearContextKeywords() { + public func clearContextKeywords() { clearExtKeywords() } + /// This method obtains the keyword for adunit targeting + /// Inserts the given element in the set if it is not already present. public func addExtKeyword(_ newElement: String) { nativeAdUnit.addExtKeyword(newElement) } + /// This method obtains the keyword set for adunit targeting + /// Adds the elements of the given set to the set. public func addExtKeywords(_ newElements: Set) { nativeAdUnit.addExtKeywords(newElements) } + /// This method allows to remove specific keyword from adunit targeting public func removeExtKeyword(_ element: String) { nativeAdUnit.removeExtKeyword(element) } + /// This method allows to remove all keywords from the set of adunit targeting public func clearExtKeywords() { nativeAdUnit.clearExtKeywords() } + /// Makes bid request for the native ad unit and setups mediation parameters. + /// - Parameter completion: The completion handler to call with the result code. public func fetchDemand(completion: ((ResultCode)->Void)?) { self.completion = completion diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationRewardedAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationRewardedAdUnit.swift index 694fa3966..3b8179710 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationRewardedAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationRewardedAdUnit.swift @@ -15,11 +15,17 @@ import Foundation +/// This class is responsible for making bid request and providing the winning bid and targeting keywords to mediating SDKs. +/// This class is a part of Mediation API. @objcMembers public class MediationRewardedAdUnit : MediationBaseInterstitialAdUnit { // - MARK: Public Methods + /// Initializes a new instance of the `MediationRewardedAdUnit` with the specified configuration ID and mediation delegate. + /// - Parameters: + /// - configId: The configuration ID for the ad unit. + /// - mediationDelegate: The delegate for mediation-related tasks. public override init(configId: String, mediationDelegate: PrebidMediationDelegate) { super.init(configId: configId, mediationDelegate: mediationDelegate) diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/PrebidMediationDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/PrebidMediationDelegate.swift index f020169d6..df8f8e81f 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/PrebidMediationDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/PrebidMediationDelegate.swift @@ -16,24 +16,20 @@ import Foundation import UIKit -// This protocol is dedicated to manage the work with Mediation SDKs. +/// This protocol is dedicated to manage the work with Mediation SDKs. @objc public protocol PrebidMediationDelegate { - /** - Puts to ad object's localExtra the ad object (winning bid or native ad) and configId - and populates adObject's keywords by targeting info - @return YES on success and NO otherwise (when the passed ad has wrong type) - */ + /// Puts to ad object's localExtra the ad object (winning bid or native ad) and configId + /// and populates adObject's keywords by targeting info + /// @return YES on success and NO otherwise (when the passed ad has wrong type) func setUpAdObject(with values: [String: Any]) -> Bool - /** - Removes an bid info from ad object's localExtra - and prebid-specific keywords from ad object's keywords - */ + + /// Removes an bid info from ad object's localExtra + /// and prebid-specific keywords from ad object's keywords func cleanUpAdObject() - /** - Returns ad view that was passed into PrebidMediationDelegate earlier. - Returns nil if there was no view passed. - */ + + /// Returns ad view that was passed into PrebidMediationDelegate earlier. + /// Returns nil if there was no view passed. func getAdView() -> UIView? } diff --git a/PrebidMobile/ResultCode.swift b/PrebidMobile/ResultCode.swift index d8c219132..401fde43b 100644 --- a/PrebidMobile/ResultCode.swift +++ b/PrebidMobile/ResultCode.swift @@ -15,33 +15,68 @@ import Foundation +/// Enum representing the result codes for various operations within the Prebid SDK. +/// +/// This enum provides a range of result codes indicating different outcomes or errors that may occur during SDK operations. Each case corresponds to a specific result or error, which helps in diagnosing issues and understanding the status of SDK operations. @objc public enum ResultCode : Int { + + /// The demand fetch request was successful. case prebidDemandFetchSuccess = 0 + + /// The Prebid server was not specified in the request. case prebidServerNotSpecified + + /// The account ID provided is not recognized by the Prebid server. case prebidInvalidAccountId + + /// The config ID provided is not recognized by the Prebid server. case prebidInvalidConfigId + + /// The size requested is not recognized by the Prebid server. case prebidInvalidSize + + /// There was a network error during the operation. case prebidNetworkError + + /// The Prebid server encountered an error while processing the request. case prebidServerError + + /// The Prebid server did not return any bids. case prebidDemandNoBids + + /// The demand request timed out. case prebidDemandTimedOut + + /// The URL of the Prebid server is invalid. case prebidServerURLInvalid + + /// An unknown error occurred within the Prebid SDK. case prebidUnknownError + /// The structure of the response received is invalid. case prebidInvalidResponseStructure = 1000 + /// An internal error occurred within the SDK. case prebidInternalSDKError = 7000 + + /// Incorrect arguments were provided to the SDK. case prebidWrongArguments + + /// No VAST tag was found in the media data. case prebidNoVastTagInMediaData + /// Misuse of the SDK was detected. case prebidSDKMisuse = 8000 + + /// SDK misuse due to a previous fetch operation not being completed yet. case prebidSDKMisusePreviousFetchNotCompletedYet + /// The Prebid request does not contain any parameters. case prebidInvalidRequest - public func name () -> String { + /// Returns a descriptive name for the result code. + public func name() -> String { switch self { - case .prebidDemandFetchSuccess: return "Prebid demand fetch successful" case .prebidServerNotSpecified: diff --git a/README.md b/README.md index cf2661e61..0f36aa6f5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -[![Build Status](https://api.travis-ci.org/prebid/prebid-mobile-ios.svg?branch=master)](https://travis-ci.org/prebid/prebid-mobile-ios) - # Prebid Mobile iOS SDK To work with Prebid Mobile, you will need accesss to a Prebid Server. See [this page](https://docs.prebid.org/prebid-server/overview/prebid-server-overview.html) for options. diff --git a/README_API_DOC.md b/README_API_DOC.md new file mode 100644 index 000000000..fb83748d7 --- /dev/null +++ b/README_API_DOC.md @@ -0,0 +1,9 @@ +# Prebid Mobile SDK - iOS API Reference + +Prebid iOS SDK is an open-source monetization library that allows you to integrate header bidding demand partners into the mobile app in various ways. The integration documentation can be found [here](https://docs.prebid.org/prebid-mobile/prebid-mobile.html). + +This section is autogenerated documentation describing the SDK's public classes, methods, and other code structures. It doesn't provide any integration guides but always provides updated API references. + +The documentation is generated for each SDK release. + +If you see any inconsistency or want to improve the process, you are always welcome to contribute to [Prebid Mobile iOS SDK](https://github.com/prebid/prebid-mobile-ios) on GitHub. diff --git a/scripts/testPrebidMobile.sh b/scripts/testPrebidMobile.sh index e20c8e954..fb4f7ebf3 100755 --- a/scripts/testPrebidMobile.sh +++ b/scripts/testPrebidMobile.sh @@ -32,6 +32,10 @@ pod install --repo-update echo -e "\n\n${GREEN}TEST PREBID MOBILE${NC}\n\n" +echo -e "\n${GREEN}Clean build\n" + +xcodebuild clean build + if [ "$run_only_with_latest_ios" != "YES" ] then echo -e "\n${GREEN}Running some unit tests for iOS 13${NC} \n" @@ -57,6 +61,8 @@ xcodebuild test \ -scheme "PrebidMobileTests" \ -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=latest' | xcpretty --color --test + + if [[ ${PIPESTATUS[0]} == 0 ]]; then echo "✅ PrebidMobile Unit Tests Passed" else From 1759b6841c23638bae86d4d4aea40b5eb80b786e Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:53:52 +0200 Subject: [PATCH 04/23] Support GMA SDK 11.12.0 (#1062) * feat: support GMA SDK 11.12.0 * feat: replace xcpretty with xcbeautify --- .../PrebidAdMobMediationBaseAdapter.swift | 2 +- .../Sources/GAMUtils.swift | 2 +- Podfile.lock | 16 ++++++++-------- .../PrebidGAMVersionChecker.swift | 2 +- scripts/testPrebidMobile.sh | 12 ++++++------ 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift index f58f7f9de..810338876 100644 --- a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift +++ b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift @@ -41,7 +41,7 @@ public class PrebidAdMobMediationBaseAdapter: NSObject, GADMediationAdapter { // Added for tests static func latestTestedGMAVersion() -> GADVersionNumber { - return GADVersionNumber(majorVersion: 11, minorVersion: 10, patchVersion: 0) + return GADVersionNumber(majorVersion: 11, minorVersion: 12, patchVersion: 0) } required public override init() { diff --git a/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift index 1b73d07e8..d6181fa63 100644 --- a/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift +++ b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift @@ -59,7 +59,7 @@ public class GAMUtils: NSObject { // Added for tests static func latestTestedGMAVersion() -> GADVersionNumber { - return GADVersionNumber(majorVersion: 11, minorVersion: 10, patchVersion: 0) + return GADVersionNumber(majorVersion: 11, minorVersion: 12, patchVersion: 0) } // MARK: Private Methods diff --git a/Podfile.lock b/Podfile.lock index f9efac40e..895d4d694 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,12 +1,12 @@ PODS: - Alamofire (4.9.1) - - AppLovinSDK (13.0.0) + - AppLovinSDK (13.0.1) - Eureka (5.5.0) - - Google-Mobile-Ads-SDK (11.10.0): + - Google-Mobile-Ads-SDK (11.12.0): - GoogleUserMessagingPlatform (>= 1.1) - GoogleAds-IMA-iOS-SDK (3.23.0) - - GoogleUserMessagingPlatform (2.6.0) - - RxSwift (6.7.1) + - GoogleUserMessagingPlatform (2.7.0) + - RxSwift (6.8.0) - SVProgressHUD (2.3.1): - SVProgressHUD/Core (= 2.3.1) - SVProgressHUD/Core (2.3.1) @@ -33,12 +33,12 @@ SPEC REPOS: SPEC CHECKSUMS: Alamofire: 85e8a02c69d6020a0d734f6054870d7ecb75cf18 - AppLovinSDK: c0ecb434df24008b6ecdd7f1e5b8587bc45ff381 + AppLovinSDK: fdae6a4361c9c9b09f8d7d18ede792368221d987 Eureka: 1c18c7fcd8f772cc2ca42d6be36292dffa77eecb - Google-Mobile-Ads-SDK: 13e6e98edfd78ad8d8a791edb927658cc260a56f + Google-Mobile-Ads-SDK: 3a76704456669fbed2057efc8bdb99050ad0e3fb GoogleAds-IMA-iOS-SDK: ee2a68ed7a1a17c7bb81bdb1b81590b35a3fc8f3 - GoogleUserMessagingPlatform: 0c3a08353e53ce8c2feab7addd0b652cde522450 - RxSwift: b9a93a26031785159e11abd40d1a55bcb8057e52 + GoogleUserMessagingPlatform: a8b56893477f67212fbc8411c139e61d463349f5 + RxSwift: 4e28be97cbcfeee614af26d83415febbf2bf6f45 SVProgressHUD: 4837c74bdfe2e51e8821c397825996a8d7de6e22 PODFILE CHECKSUM: bae4436ed691a1d2217fde386d8881d6e7e06963 diff --git a/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift b/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift index 2867036da..933d022d5 100644 --- a/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift +++ b/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift @@ -18,7 +18,7 @@ import Foundation class PrebidGAMVersionChecker { var latestTestedGMAVersion: (Int, Int, Int) { - (11, 10, 0) + (11, 12, 0) } var currentGMAVersion: (Int, Int, Int)? diff --git a/scripts/testPrebidMobile.sh b/scripts/testPrebidMobile.sh index fb4f7ebf3..35ba1aca2 100755 --- a/scripts/testPrebidMobile.sh +++ b/scripts/testPrebidMobile.sh @@ -24,7 +24,7 @@ echo -e "\n\n${GREEN}INSTALL PODS${NC}\n\n" cd .. -gem install xcpretty --user-install +brew install xcbeautify export PATH="/Users/distiller/.gem/ruby/2.7.0/bin:$PATH" gem install cocoapods @@ -43,7 +43,7 @@ then -workspace PrebidMobile.xcworkspace \ -scheme "PrebidMobileTests" \ -destination 'platform=iOS Simulator,name=iPhone 11 Pro Max,OS=13.7' \ - -only-testing PrebidMobileTests/RequestBuilderTests/testPostData | xcpretty --color --test + -only-testing PrebidMobileTests/RequestBuilderTests/testPostData | xcbeautify if [[ ${PIPESTATUS[0]} == 0 ]]; then echo "✅ unit tests for iOS 13 Passed" @@ -59,7 +59,7 @@ xcodebuild test \ -workspace PrebidMobile.xcworkspace \ -retry-tests-on-failure \ -scheme "PrebidMobileTests" \ - -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=latest' | xcpretty --color --test + -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=latest' | xcbeautify @@ -74,7 +74,7 @@ echo -e "\n${GREEN}Running PrebidMobileGAMEventHandlers unit tests${NC} \n" xcodebuild test \ -workspace PrebidMobile.xcworkspace \ -scheme "PrebidMobileGAMEventHandlersTests" \ - -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=latest' | xcpretty --color --test + -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=latest' | xcbeautify if [[ ${PIPESTATUS[0]} == 0 ]]; then echo "✅ PrebidMobileGAMEventHandlers Unit Tests Passed" @@ -84,7 +84,7 @@ else fi echo -e "\n${GREEN}Running PrebidMobileAdMobAdapters unit tests${NC} \n" -xcodebuild test -workspace PrebidMobile.xcworkspace -scheme "PrebidMobileAdMobAdaptersTests" -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=latest' | xcpretty -f `xcpretty-travis-formatter` --color --test +xcodebuild test -workspace PrebidMobile.xcworkspace -scheme "PrebidMobileAdMobAdaptersTests" -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=latest' | xcbeautify if [[ ${PIPESTATUS[0]} == 0 ]]; then echo "✅ PrebidMobileAdMobAdapters Unit Tests Passed" @@ -94,7 +94,7 @@ else fi echo -e "\n${GREEN}Running PrebidMobileMAXAdapters unit tests${NC} \n" -xcodebuild test -workspace PrebidMobile.xcworkspace -scheme "PrebidMobileMAXAdaptersTests" -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=latest' | xcpretty -f `xcpretty-travis-formatter` --color --test +xcodebuild test -workspace PrebidMobile.xcworkspace -scheme "PrebidMobileMAXAdaptersTests" -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=latest' | xcbeautify if [[ ${PIPESTATUS[0]} == 0 ]]; then echo "✅ PrebidMobileMAXAdapters Unit Tests Passed" From 34ad42d70e4b4b91d022cabd9a7873e99216ee45 Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:53:36 +0200 Subject: [PATCH 05/23] Support GMA SDK 11.13.0 (#1070) --- .../Sources/PrebidAdMobMediationBaseAdapter.swift | 2 +- .../PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift | 2 +- Podfile.lock | 6 +++--- .../ConfigurationAndTargeting/PrebidGAMVersionChecker.swift | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift index 810338876..aec6b4bfb 100644 --- a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift +++ b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift @@ -41,7 +41,7 @@ public class PrebidAdMobMediationBaseAdapter: NSObject, GADMediationAdapter { // Added for tests static func latestTestedGMAVersion() -> GADVersionNumber { - return GADVersionNumber(majorVersion: 11, minorVersion: 12, patchVersion: 0) + return GADVersionNumber(majorVersion: 11, minorVersion: 13, patchVersion: 0) } required public override init() { diff --git a/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift index d6181fa63..9f23b9d10 100644 --- a/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift +++ b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMUtils.swift @@ -59,7 +59,7 @@ public class GAMUtils: NSObject { // Added for tests static func latestTestedGMAVersion() -> GADVersionNumber { - return GADVersionNumber(majorVersion: 11, minorVersion: 12, patchVersion: 0) + return GADVersionNumber(majorVersion: 11, minorVersion: 13, patchVersion: 0) } // MARK: Private Methods diff --git a/Podfile.lock b/Podfile.lock index 895d4d694..097717072 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -2,7 +2,7 @@ PODS: - Alamofire (4.9.1) - AppLovinSDK (13.0.1) - Eureka (5.5.0) - - Google-Mobile-Ads-SDK (11.12.0): + - Google-Mobile-Ads-SDK (11.13.0): - GoogleUserMessagingPlatform (>= 1.1) - GoogleAds-IMA-iOS-SDK (3.23.0) - GoogleUserMessagingPlatform (2.7.0) @@ -35,7 +35,7 @@ SPEC CHECKSUMS: Alamofire: 85e8a02c69d6020a0d734f6054870d7ecb75cf18 AppLovinSDK: fdae6a4361c9c9b09f8d7d18ede792368221d987 Eureka: 1c18c7fcd8f772cc2ca42d6be36292dffa77eecb - Google-Mobile-Ads-SDK: 3a76704456669fbed2057efc8bdb99050ad0e3fb + Google-Mobile-Ads-SDK: 14f57f2dc33532a24db288897e26494640810407 GoogleAds-IMA-iOS-SDK: ee2a68ed7a1a17c7bb81bdb1b81590b35a3fc8f3 GoogleUserMessagingPlatform: a8b56893477f67212fbc8411c139e61d463349f5 RxSwift: 4e28be97cbcfeee614af26d83415febbf2bf6f45 @@ -43,4 +43,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: bae4436ed691a1d2217fde386d8881d6e7e06963 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift b/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift index 933d022d5..26128d73a 100644 --- a/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift +++ b/PrebidMobile/ConfigurationAndTargeting/PrebidGAMVersionChecker.swift @@ -18,7 +18,7 @@ import Foundation class PrebidGAMVersionChecker { var latestTestedGMAVersion: (Int, Int, Int) { - (11, 12, 0) + (11, 13, 0) } var currentGMAVersion: (Int, Int, Int)? From 833d351ab9dd0b1b014b9e810ba0a6712f9c42d7 Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk Date: Thu, 5 Dec 2024 11:54:53 +0200 Subject: [PATCH 06/23] Rewarded Ad Unit Enhancements (#1058) * refactor: isOptIn -> isRewarded * feat: introduce Prebid reward * fix: resolve warnings * refactor: introduce base interstitial and base rewarded internal classes * feat: add comments to the new functionality * feat: introduce rewarded configuration * feat: add rewarded endcard time test case * feat: implement reward behavior & add unit tests * feat: add rwdd flag to request & update tests and InternalTestApp * feat: add test cases to apps & minor changes * feat: changes in adapters * feat: changes in adapters * clean: remove unneeded * feat: minor enhancement in demo apps * feat: minor changes * feat: minor changes in demo apps * fix: avoid probable crash by converting nsnull to nil * feat: update modulemap --- .../EventHandlers.xcodeproj/project.pbxproj | 24 +- .../PrebidAdMobMediationBaseAdapter.swift | 5 +- .../Sources/PrebidAdMobRewardedAdapter.swift | 166 +++++ .../PrebidAdMobRewardedVideoAdapter.swift | 147 +---- .../Sources/GAMInterstitialEventHandler.swift | 4 +- .../Sources/GAMRewardedEventHandler.swift | 10 +- .../Sources/Utils/GADReward+Extensions.swift | 18 +- ...nterstitialAdapter+MARewardedAdapter.swift | 164 +++-- .../PrebidDemo.xcodeproj/project.pbxproj | 40 ++ .../AdMobDisplayRewardedViewController.h | 28 + .../AdMobDisplayRewardedViewController.m | 90 +++ .../AdMob/AdMobVideoRewardedViewController.m | 9 +- .../GAMDisplayRewardedViewController.h | 28 + .../GAMDisplayRewardedViewController.m | 64 ++ .../GAMVideoRewardedViewController.m | 10 +- .../InAppDisplayRewardedViewController.h | 26 + .../InAppDisplayRewardedViewController.m | 61 ++ .../InApp/InAppVideoRewardedViewController.m | 10 +- .../MAX/MAXDisplayRewardedViewController.h | 28 + .../MAX/MAXDisplayRewardedViewController.m | 93 +++ .../MAX/MAXVideoRewardedViewController.m | 3 +- .../IntegrationCase/AdFormat.h | 1 + .../IntegrationCase/AdFormatDescriptor.m | 2 + .../IntegrationCase/IntegrationCaseManager.m | 44 ++ .../AdMobDisplayRewardedViewController.swift | 83 +++ .../AdMobVideoRewardedViewController.swift | 11 +- .../GAMDisplayRewardedViewController.swift | 66 ++ .../GAMVideoRewardedViewController.swift | 16 +- .../InAppDisplayRewardedViewController.swift | 56 ++ ...oInterstitialLandscapeViewController.swift | 2 +- ...eoInterstitialVerticalViewController.swift | 2 +- .../InAppVideoRewardedViewController.swift | 10 +- .../MAXDisplayRewardedViewController.swift | 85 +++ .../MAX/MAXVideoRewardedViewController.swift | 11 +- .../IntegrationCase/AdFormat.swift | 3 + .../IntegrationCaseManager.swift | 40 +- .../InternalTestApp.xcodeproj/project.pbxproj | 53 +- .../Model/TestCasesManager.swift | 271 +++++---- .../Resources/Info.plist | 2 - .../GAM/PrebidGAMRewardedController.swift | 2 +- .../OpenX/PrebidInterstitialController.swift | 7 +- .../OpenX/PrebidRewardedController.swift | 3 +- .../UITests/AdsLoaderUITestCase.swift | 25 +- .../UITests/PrebidExamplesUITest.swift | 112 ++-- .../UITests/PrebidRewardedVideoUITest.swift | 29 +- .../UITests/PrebidServerUITests.swift | 19 - .../UITests/XCTestCaseExt.swift | 5 +- PrebidMobile.xcodeproj/project.pbxproj | 182 ++++-- .../MultiformatAdUnit/PrebidAdUnit.swift | 2 +- .../BuildFiles/PrebidMobile.modulemap | 10 + .../BuildFiles/PrebidMobileSwiftHeaders.h | 9 + PrebidMobile/Dispatcher.swift | 2 +- .../AdTypes/AdView/AdConfiguration.swift | 8 +- .../AdView/Modals/PBMModalViewController.h | 1 + .../AdView/Modals/PBMModalViewController.m | 25 +- .../AdTypes/AdView/PBMAdViewManager.m | 19 +- .../AdTypes/AdView/PBMAdViewManagerDelegate.h | 3 + .../AdTypes/AdView/PBMCloseActionManager.h | 32 + .../AdTypes/AdView/PBMCloseActionManager.m | 30 + .../AdTypes/AdView/PBMCreativeModel.h | 7 + .../AdTypes/AdView/PBMCreativeModel.m | 3 + .../AdTypes/AdView/PBMCreativeViewDelegate.h | 3 + .../AdTypes/AdView/PBMTrackingEvent.h | 5 +- .../AdTypes/AdView/PBMTrackingEvent.m | 69 +++ .../AdTypes/AdView/PBMVideoCreative.h | 2 + .../AdTypes/AdView/PBMVideoCreative.m | 102 +++- .../AdTypes/AdView/PBMVideoView.h | 3 + .../AdTypes/AdView/PBMVideoView.m | 117 +++- .../AdTypes/AdView/PBMVideoViewDelegate.h | 2 + .../AdTypes/AdView/PBMWebView.h | 2 + .../AdTypes/AdView/PBMWebView.m | 18 +- .../AdTypes/AdView/PBMWebViewDelegate.h | 1 + .../AdTypes/PBMHTMLCreative.h | 1 + .../AdTypes/PBMHTMLCreative.m | 116 +++- .../NSObject+PBMExtensions.h | 22 + .../NSObject+PBMExtensions.m | 24 + .../ORTB/PBMORTBAbstract+Protected.h | 1 + .../PrebidMobileRendering/ORTB/PBMORTBImp.h | 3 + .../PrebidMobileRendering/ORTB/PBMORTBImp.m | 2 + .../AdLoadFlowControllerDelegate.swift | 2 - .../GAM/AdLoading/PBMAdLoadFlowController.h | 1 + .../GAM/AdLoading/PBMAdLoadFlowController.m | 12 +- .../GAM/AdLoading/PBMInterstitialAdLoader.h | 4 +- .../GAM/AdLoading/PBMInterstitialAdLoader.m | 24 +- .../PBMInterstitialAdLoaderDelegate.h | 8 +- .../Prebid/Integrations/GAM/BannerView.swift | 1 + .../GAM/BaseInterstitialAdUnit.swift | 568 ++++-------------- .../GAM/BaseInterstitialAdUnitProtocol.swift | 4 +- .../Integrations/GAM/BaseRewardedAdUnit.swift | 53 ++ ...InterstitialEventInteractionDelegate.swift | 16 +- .../GAM/InterstitialRenderingAdUnit.swift | 468 +++++++++++---- .../Integrations/GAM/PrebidReward.swift | 31 + .../Integrations/GAM/RewardedAdUnit.swift | 466 ++++++++++---- .../GAM/RewardedAdUnitDelegate.swift | 2 +- .../Integrations/GAM/RewardedConfig.swift | 81 +++ .../GAM/RewardedEventHandlerProtocol.swift | 2 +- .../RewardedEventInteractionDelegate.swift | 9 +- .../MediationRewardedAdUnit.swift | 2 +- .../RewardedEventHandlerStandalone.swift | 2 +- .../InterstitialController.swift | 28 +- ...stitialControllerInteractionDelegate.swift | 3 +- .../PBMCacheRenderers/PrebidRenderer.swift | 5 +- .../Prebid/PBMCore/AdUnitConfig.swift | 34 +- .../Prebid/PBMCore/Bid.swift | 4 + .../ORTB/Prebid/PBMORTBExtPrebidPassthrough.h | 3 + .../ORTB/Prebid/PBMORTBExtPrebidPassthrough.m | 9 + .../ORTB/Prebid/PBMORTBRewardedClose.h | 35 ++ .../ORTB/Prebid/PBMORTBRewardedClose.m | 42 ++ .../ORTB/Prebid/PBMORTBRewardedCompletion.h | 33 + .../ORTB/Prebid/PBMORTBRewardedCompletion.m | 45 ++ .../Prebid/PBMORTBRewardedCompletionBanner.h | 31 + .../Prebid/PBMORTBRewardedCompletionBanner.m | 42 ++ .../Prebid/PBMORTBRewardedCompletionVideo.h | 37 ++ .../Prebid/PBMORTBRewardedCompletionVideo.m | 44 ++ .../PBMORTBRewardedCompletionVideoEndcard.h | 31 + .../PBMORTBRewardedCompletionVideoEndcard.m | 42 ++ .../Prebid/PBMORTBRewardedConfiguration.h | 38 ++ .../Prebid/PBMORTBRewardedConfiguration.m | 48 ++ .../ORTB/Prebid/PBMORTBRewardedReward.h | 35 ++ .../ORTB/Prebid/PBMORTBRewardedReward.m | 44 ++ .../PBMCore/PBMPrebidParameterBuilder.m | 6 +- .../Prebid/PBMCore/PBMSafariVCOpener.m | 44 +- .../Utilities/BackgroundAwareTimer.swift | 103 ++++ .../AdUnitTests/PrebidAdUnitTests.swift | 4 +- .../PrebidMobileTest-Bridging-Header.h | 2 + .../RenderingTests/Mocks/MockVideoView.swift | 26 + .../PBMVideoView+pbmTestExtension.h | 2 + .../Tests/CloseActionManagerTests.swift | 40 ++ .../Tests/PBMCreativeFactoryJobTest.swift | 4 +- .../Tests/PBMCreativeFactoryTest.swift | 2 +- .../Tests/PBMHTMLCreativeTest.swift | 230 +++++-- .../Tests/PBMHTMLCreativeTest_Base.swift | 2 + .../PBMHTMLCreativeTest_MRAIDClose.swift | 1 + .../Tests/PBMMRAIDControllerTest_Base.swift | 1 + .../Tests/PBMModalViewControllerTest.swift | 17 +- .../Tests/PBMORTBAbstractTest.swift | 3 +- ...MRewardedVideoCreativeTestCloseDelay.swift | 2 +- .../PBMVideoCreativeTestCloseDelay.swift | 4 +- .../Tests/PBMVideoViewTest.swift | 260 +++++++- .../RenderingTests/Tests/PBMWebViewObjCTest.m | 2 +- .../RenderingTests/Tests/PBMWebViewTest.swift | 10 +- .../BasicParameterBuilderTest.swift | 13 +- .../PrebidParameterBuilderTest.swift | 23 +- .../PBMAdLoadFlowControllerTest.swift | 35 +- .../Prebid/BaseInterstitialAdUnitTest.swift | 41 +- .../MediationRewardedAdUnitTest.swift | 2 +- ...aseInterstitialAdUnit_DelegationTest.swift | 82 +-- .../Tests/Prebid/PBMORTBTest.swift | 29 + .../PBMRewardedVideoViewTest.swift | 4 +- .../RewardedVideo_CompanionTest.swift | 2 +- .../PBMRewardedVideoCreativeTest.swift | 1 + .../VASTTests/PBMVideoCreativeTest.swift | 80 ++- .../VASTTests/RewardedVideoEventsTest.swift | 3 +- .../VASTTests/VastEventTrackingTest.swift | 4 +- .../Tests/VASTTests/VideoEventsTest.swift | 2 + .../Tests/VASTTests/VideoFileTypeTest.swift | 3 + 156 files changed, 4763 insertions(+), 1529 deletions(-) create mode 100644 EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedAdapter.swift rename PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventLoadingDelegate.swift => EventHandlers/PrebidMobileGAMEventHandlers/Sources/Utils/GADReward+Extensions.swift (64%) create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobDisplayRewardedViewController.h create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobDisplayRewardedViewController.m create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.h create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.m create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayRewardedViewController.h create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayRewardedViewController.m create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXDisplayRewardedViewController.h create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXDisplayRewardedViewController.m create mode 100644 Example/PrebidDemo/PrebidDemoSwift/Examples/AdMob/AdMobDisplayRewardedViewController.swift create mode 100644 Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.swift create mode 100644 Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayRewardedViewController.swift create mode 100644 Example/PrebidDemo/PrebidDemoSwift/Examples/MAX/MAXDisplayRewardedViewController.swift create mode 100644 PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCloseActionManager.h create mode 100644 PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCloseActionManager.m create mode 100644 PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/NSObject+PBMExtensions.h create mode 100644 PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/NSObject+PBMExtensions.m create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseRewardedAdUnit.swift create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/PrebidReward.swift create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedConfig.swift create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedClose.h create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedClose.m create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletion.h create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletion.m create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionBanner.h create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionBanner.m create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideo.h create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideo.m create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideoEndcard.h create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideoEndcard.m create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedConfiguration.h create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedConfiguration.m create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedReward.h create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedReward.m create mode 100644 PrebidMobile/PrebidMobileRendering/Utilities/BackgroundAwareTimer.swift create mode 100644 PrebidMobileTests/RenderingTests/Mocks/MockVideoView.swift create mode 100644 PrebidMobileTests/RenderingTests/Tests/CloseActionManagerTests.swift diff --git a/EventHandlers/EventHandlers.xcodeproj/project.pbxproj b/EventHandlers/EventHandlers.xcodeproj/project.pbxproj index 06e83745a..59a7bd111 100644 --- a/EventHandlers/EventHandlers.xcodeproj/project.pbxproj +++ b/EventHandlers/EventHandlers.xcodeproj/project.pbxproj @@ -15,8 +15,10 @@ 344A7F0E250B749600743A93 /* GAMRequestWrapperTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 344A7F0D250B749600743A93 /* GAMRequestWrapperTest.swift */; }; 344A7F14250B823F00743A93 /* GAMInterstitialAdWrapperTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 344A7F13250B823F00743A93 /* GAMInterstitialAdWrapperTest.swift */; }; 344A7F1A250B8E5400743A93 /* GADRewardedAdWrapperTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 344A7F19250B8E5400743A93 /* GADRewardedAdWrapperTest.swift */; }; + 534C61682CB861930026119A /* PrebidAdMobRewardedAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534C61672CB861930026119A /* PrebidAdMobRewardedAdapter.swift */; }; 53632B24290BC57A00139EF3 /* AdMobGMATests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53632B23290BC57A00139EF3 /* AdMobGMATests.swift */; }; 53632B2C290BE17300139EF3 /* GMATests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53632B2B290BE17300139EF3 /* GMATests.swift */; }; + 53F2A57B2CB94FBE009B74CA /* GADReward+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F2A57A2CB94FB3009B74CA /* GADReward+Extensions.swift */; }; 5B36172626370A8700AFA1C2 /* GAMUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B36172526370A8700AFA1C2 /* GAMUtils.swift */; }; 5B3617FC2638131A00AFA1C2 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3617FB2638131A00AFA1C2 /* Constants.swift */; }; 5B36182326384C7D00AFA1C2 /* GAMRewardedEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B36182226384C7D00AFA1C2 /* GAMRewardedEventHandler.swift */; }; @@ -112,8 +114,10 @@ 34B64D5F24F7E41600FDD9F4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39183ECBC4E402BC48B3691D /* Pods_PrebidMobileGAMEventHandlersTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PrebidMobileGAMEventHandlersTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 42CE6323AC67980202BFDE74 /* Pods-PrebidMobileAdMobAdapters.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PrebidMobileAdMobAdapters.release.xcconfig"; path = "Target Support Files/Pods-PrebidMobileAdMobAdapters/Pods-PrebidMobileAdMobAdapters.release.xcconfig"; sourceTree = ""; }; + 534C61672CB861930026119A /* PrebidAdMobRewardedAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidAdMobRewardedAdapter.swift; sourceTree = ""; }; 53632B23290BC57A00139EF3 /* AdMobGMATests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdMobGMATests.swift; sourceTree = ""; }; 53632B2B290BE17300139EF3 /* GMATests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GMATests.swift; sourceTree = ""; }; + 53F2A57A2CB94FB3009B74CA /* GADReward+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GADReward+Extensions.swift"; sourceTree = ""; }; 58B193FE14C1F3D7E795F7AC /* Pods-PrebidMobileMAXAdapters.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PrebidMobileMAXAdapters.release.xcconfig"; path = "Target Support Files/Pods-PrebidMobileMAXAdapters/Pods-PrebidMobileMAXAdapters.release.xcconfig"; sourceTree = ""; }; 5B36172526370A8700AFA1C2 /* GAMUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GAMUtils.swift; sourceTree = ""; }; 5B3617FB2638131A00AFA1C2 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; @@ -336,6 +340,8 @@ 34B64D6D24F7E54700FDD9F4 /* Sources */ = { isa = PBXGroup; children = ( + 34C7C9E8250901410021C6D0 /* Boxes */, + 53F2A5792CB94FA8009B74CA /* Utils */, 5B69677F2642A56A0042372E /* PrebidMobileGAMEventHandlers.h */, 5B3617FB2638131A00AFA1C2 /* Constants.swift */, 5BF8E76D263BF4DF00341EE4 /* GAMBannerEventHandler.swift */, @@ -343,7 +349,6 @@ 5BF8E6B6263AC32800341EE4 /* GAMInterstitialEventHandler.swift */, 5B36182226384C7D00AFA1C2 /* GAMRewardedEventHandler.swift */, 5B36172526370A8700AFA1C2 /* GAMUtils.swift */, - 34C7C9E8250901410021C6D0 /* Boxes */, ); path = Sources; sourceTree = ""; @@ -361,6 +366,14 @@ path = Boxes; sourceTree = ""; }; + 53F2A5792CB94FA8009B74CA /* Utils */ = { + isa = PBXGroup; + children = ( + 53F2A57A2CB94FB3009B74CA /* GADReward+Extensions.swift */, + ); + path = Utils; + sourceTree = ""; + }; 927ADB28280583AF006EB8D5 /* Sources */ = { isa = PBXGroup; children = ( @@ -390,12 +403,13 @@ 92C7A868279AAEF1000D31CB /* AdMobMediationNativeUtils.swift */, 92C474EC2795B57E00C26E27 /* AdMobMediationRewardedUtils.swift */, 92A2380C27731B48000157AA /* AdMobUtils.swift */, + 92C474F42795E1A600C26E27 /* PrebidAdMobMediationBaseAdapter.swift */, 92E924FA2769ED96002B57F3 /* PrebidAdMobBannerAdapter.swift */, 926CBF1B278C837800E07BF2 /* PrebidAdMobInterstitialAdapter.swift */, - 92C474F42795E1A600C26E27 /* PrebidAdMobMediationBaseAdapter.swift */, - 92EB6DE42799FB4700470DEC /* PrebidAdMobNativeAdapter.swift */, - 92DDA43B278EE8EC0035F03E /* PrebidAdMobRewardedVideoAdapter.swift */, 9285B46527AAC5B1009BD3D3 /* PrebidAdMobVideoInterstitialAdapter.swift */, + 534C61672CB861930026119A /* PrebidAdMobRewardedAdapter.swift */, + 92DDA43B278EE8EC0035F03E /* PrebidAdMobRewardedVideoAdapter.swift */, + 92EB6DE42799FB4700470DEC /* PrebidAdMobNativeAdapter.swift */, ); path = Sources; sourceTree = ""; @@ -951,6 +965,7 @@ 5BF8E781263C363800341EE4 /* GAMRequestWrapper.swift in Sources */, 5BF8E789263C3B4000341EE4 /* GADNativeAdWrapper.swift in Sources */, 5B36172626370A8700AFA1C2 /* GAMUtils.swift in Sources */, + 53F2A57B2CB94FBE009B74CA /* GADReward+Extensions.swift in Sources */, 5B6967702642993B0042372E /* GAMEventHandlerError.swift in Sources */, 5BF8E779263C291300341EE4 /* GAMBannerViewWrapper.swift in Sources */, ); @@ -990,6 +1005,7 @@ 9267B400276FCF9300F1ADF1 /* AdMobConstants.swift in Sources */, 92C474F52795E1A600C26E27 /* PrebidAdMobMediationBaseAdapter.swift in Sources */, 92E924FD276A0FD8002B57F3 /* AdMobMediationBannerUtils.swift in Sources */, + 534C61682CB861930026119A /* PrebidAdMobRewardedAdapter.swift in Sources */, 9285B46627AAC5B1009BD3D3 /* PrebidAdMobVideoInterstitialAdapter.swift in Sources */, 92C7A869279AAEF2000D31CB /* AdMobMediationNativeUtils.swift in Sources */, 92E924FF276A193E002B57F3 /* AdMobAdaptersError.swift in Sources */, diff --git a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift index aec6b4bfb..ea1d40cd9 100644 --- a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift +++ b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobMediationBaseAdapter.swift @@ -48,7 +48,10 @@ public class PrebidAdMobMediationBaseAdapter: NSObject, GADMediationAdapter { super.init() } - public static func setUpWith(_ configuration: GADMediationServerConfiguration, completionHandler: @escaping GADMediationAdapterSetUpCompletionBlock) { + public static func setUpWith( + _ configuration: GADMediationServerConfiguration, + completionHandler: @escaping GADMediationAdapterSetUpCompletionBlock + ) { // TODO: Add Prebid SDK initialization logic } } diff --git a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedAdapter.swift b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedAdapter.swift new file mode 100644 index 000000000..759310bcc --- /dev/null +++ b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedAdapter.swift @@ -0,0 +1,166 @@ +/*   Copyright 2018-2021 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import PrebidMobile +import GoogleMobileAds + +@objc(PrebidAdMobRewardedAdapter) +public class PrebidAdMobRewardedAdapter: + PrebidAdMobMediationBaseAdapter, + GADMediationRewardedAd, + InterstitialControllerLoadingDelegate, + InterstitialControllerInteractionDelegate { + + // MARK: - Private Properties + + var interstitialController: InterstitialController? + weak var rootViewController: UIViewController? + var adAvailable = false + + weak var delegate: GADMediationRewardedAdEventDelegate? + var completionHandler: GADMediationRewardedLoadCompletionHandler? + + // MARK: - GADMediationAdapter + + public func loadRewardedAd(for adConfiguration: GADMediationRewardedAdConfiguration, + completionHandler: @escaping GADMediationRewardedLoadCompletionHandler) { + self.completionHandler = completionHandler + + switch createInterstitialController(with: adConfiguration) { + case .success(let controller): + self.interstitialController = controller + interstitialController?.loadAd() + case .failure(let error): + delegate = completionHandler(nil, error) + } + } + + // MARK: - Helpers + + func createInterstitialController( + with adConfiguration: GADMediationRewardedAdConfiguration + ) -> Result { + + guard let serverParameter = adConfiguration.credentials.settings["parameter"] as? String else { + return .failure(AdMobAdaptersError.noServerParameter) + } + + guard let eventExtras = adConfiguration.extras as? GADCustomEventExtras, + let eventExtrasDictionary = eventExtras.extras(forLabel: AdMobConstants.PrebidAdMobEventExtrasLabel), + !eventExtrasDictionary.isEmpty else { + return .failure(AdMobAdaptersError.emptyCustomEventExtras) + } + + guard let targetingInfo = eventExtrasDictionary[PBMMediationTargetingInfoKey] as? [String: String] else { + return .failure(AdMobAdaptersError.noTargetingInfoInEventExtras) + } + + guard MediationUtils.isServerParameterInTargetingInfoDict(serverParameter, targetingInfo) else { + return .failure(AdMobAdaptersError.wrongServerParameter) + } + + guard let bid = eventExtrasDictionary[PBMMediationAdUnitBidKey] as? Bid else { + return .failure(AdMobAdaptersError.noBidInEventExtras) + } + + guard let configId = eventExtrasDictionary[PBMMediationConfigIdKey] as? String else { + return .failure(AdMobAdaptersError.noConfigIDInEventExtras) + } + + let interstitialController = InterstitialController(bid: bid, configId: configId) + interstitialController.loadingDelegate = self + interstitialController.interactionDelegate = self + interstitialController.isRewarded = true + + if let videoAdConfig = eventExtrasDictionary[PBMMediationVideoAdConfiguration] as? VideoControlsConfiguration { + interstitialController.videoControlsConfig = videoAdConfig + } + + if let videoParameters = eventExtrasDictionary[PBMMediationVideoParameters] as? VideoParameters { + interstitialController.videoParameters = videoParameters + } + + return .success(interstitialController) + } + + // MARK: - GADMediationRewardedAd + + public func present(from viewController: UIViewController) { + if adAvailable { + rootViewController = viewController + interstitialController?.show() + } else { + let error = AdMobAdaptersError.noAd + delegate?.didFailToPresentWithError(error) + + if let handler = completionHandler { + delegate = handler(nil, error) + } + } + } + + // MARK: - InterstitialControllerLoadingDelegate + + public func interstitialControllerDidLoadAd(_ interstitialController: InterstitialController) { + adAvailable = true + + if let handler = completionHandler { + delegate = handler(self, nil) + } + } + + public func interstitialController(_ interstitialController: InterstitialController, didFailWithError error: Error) { + adAvailable = false + } + + // MARK: - InterstitialControllerInteractionDelegate + + public func trackImpression(forInterstitialController: InterstitialController) { + delegate?.reportImpression() + } + + public func trackUserReward(_ interstitialController: InterstitialController, _ reward: PrebidReward) { + delegate?.didRewardUser() + } + + public func interstitialControllerDidClickAd(_ interstitialController: InterstitialController) { + delegate?.reportClick() + } + + public func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { + adAvailable = false + delegate?.willDismissFullScreenView() + delegate?.didDismissFullScreenView() + } + + public func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) { + delegate?.willPresentFullScreenView() + delegate?.didStartVideo() + delegate?.didEndVideo() + } + + public func interstitialControllerDidComplete(_ interstitialController: InterstitialController) { + adAvailable = false + rootViewController = nil + + delegate?.didRewardUser() + } + + public func viewControllerForModalPresentation(fromInterstitialController: InterstitialController) -> UIViewController? { + rootViewController + } + + public func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) {} +} diff --git a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedVideoAdapter.swift b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedVideoAdapter.swift index 181e6cdbd..d570d9096 100644 --- a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedVideoAdapter.swift +++ b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedVideoAdapter.swift @@ -13,149 +13,22 @@  limitations under the License.  */ -import Foundation import PrebidMobile import GoogleMobileAds +@available(*, deprecated, message: "This class is deprecated. Use PrebidAdMobRewardedAdapter instead.") @objc(PrebidAdMobRewardedVideoAdapter) -public class PrebidAdMobRewardedVideoAdapter: - PrebidAdMobMediationBaseAdapter, - GADMediationRewardedAd, - InterstitialControllerLoadingDelegate, - InterstitialControllerInteractionDelegate { +public class PrebidAdMobRewardedVideoAdapter: PrebidAdMobRewardedAdapter { - // MARK: - Private Properties - - var interstitialController: InterstitialController? - weak var rootViewController: UIViewController? - var adAvailable = false - - weak var delegate: GADMediationRewardedAdEventDelegate? - var completionHandler: GADMediationRewardedLoadCompletionHandler? - - // MARK: - GADMediationAdapter - - public func loadRewardedAd(for adConfiguration: GADMediationRewardedAdConfiguration, - completionHandler: @escaping GADMediationRewardedLoadCompletionHandler) { - self.completionHandler = completionHandler - - guard let serverParameter = adConfiguration.credentials.settings["parameter"] as? String else { - let error = AdMobAdaptersError.noServerParameter - delegate = completionHandler(nil, error) - return - } - - guard let eventExtras = adConfiguration.extras as? GADCustomEventExtras, - let eventExtrasDictionary = eventExtras.extras(forLabel: AdMobConstants.PrebidAdMobEventExtrasLabel), - !eventExtrasDictionary.isEmpty else { - let error = AdMobAdaptersError.emptyCustomEventExtras - delegate = completionHandler(nil, error) - return - } - - guard let targetingInfo = eventExtrasDictionary[PBMMediationTargetingInfoKey] as? [String: String] else { - let error = AdMobAdaptersError.noTargetingInfoInEventExtras - delegate = completionHandler(nil, error) - return - } - - guard MediationUtils.isServerParameterInTargetingInfoDict(serverParameter, targetingInfo) else { - let error = AdMobAdaptersError.wrongServerParameter - delegate = completionHandler(nil, error) - return - } - - guard let bid = eventExtrasDictionary[PBMMediationAdUnitBidKey] as? Bid else { - let error = AdMobAdaptersError.noBidInEventExtras - delegate = completionHandler(nil, error) - return - } - - guard let configId = eventExtrasDictionary[PBMMediationConfigIdKey] as? String else { - let error = AdMobAdaptersError.noConfigIDInEventExtras - delegate = completionHandler(nil, error) - return - } - - interstitialController = InterstitialController(bid: bid, configId: configId) - interstitialController?.loadingDelegate = self - interstitialController?.interactionDelegate = self - interstitialController?.adFormats = [.video] - interstitialController?.isOptIn = true - - if let videoAdConfig = eventExtrasDictionary[PBMMediationVideoAdConfiguration] as? VideoControlsConfiguration { - interstitialController?.videoControlsConfig = videoAdConfig - } - - if let videoParameters = eventExtrasDictionary[PBMMediationVideoParameters] as? VideoParameters { - interstitialController?.videoParameters = videoParameters - } - - interstitialController?.loadAd() - } - - // MARK: - GADMediationRewardedAd - - public func present(from viewController: UIViewController) { - if adAvailable { - rootViewController = viewController - interstitialController?.show() - } else { - let error = AdMobAdaptersError.noAd - delegate?.didFailToPresentWithError(error) - - if let handler = completionHandler { - delegate = handler(nil, error) - } + override func createInterstitialController( + with adConfiguration: GADMediationRewardedAdConfiguration + ) -> Result { + let result = super.createInterstitialController(with: adConfiguration) + if case .success(let controller) = result { + controller.adFormats = [.video] + return .success(controller) } - } - - // MARK: - InterstitialControllerLoadingDelegate - - public func interstitialControllerDidLoadAd(_ interstitialController: InterstitialController) { - adAvailable = true - if let handler = completionHandler { - delegate = handler(self, nil) - } + return result } - - public func interstitialController(_ interstitialController: InterstitialController, didFailWithError error: Error) { - adAvailable = false - } - - // MARK: - InterstitialControllerInteractionDelegate - - public func trackImpression(forInterstitialController: InterstitialController) { - delegate?.reportImpression() - } - - public func interstitialControllerDidClickAd(_ interstitialController: InterstitialController) { - delegate?.reportClick() - } - - public func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { - adAvailable = false - delegate?.willDismissFullScreenView() - delegate?.didDismissFullScreenView() - } - - public func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) { - delegate?.willPresentFullScreenView() - delegate?.didStartVideo() - delegate?.didEndVideo() - } - - public func interstitialControllerDidComplete(_ interstitialController: InterstitialController) { - adAvailable = false - rootViewController = nil - - delegate?.didRewardUser() - } - - public func viewControllerForModalPresentation(fromInterstitialController: InterstitialController) -> UIViewController? { - rootViewController - } - - public func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) {} } diff --git a/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMInterstitialEventHandler.swift b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMInterstitialEventHandler.swift index f62a88f5a..f21afab45 100644 --- a/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMInterstitialEventHandler.swift +++ b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMInterstitialEventHandler.swift @@ -127,9 +127,7 @@ public class GAMInterstitialEventHandler : } } - func interstitial(didFailToReceive ad:GAMInterstitialAdWrapper, - error: Error) - { + func interstitial(didFailToReceive ad: GAMInterstitialAdWrapper, error: Error) { if requestInterstitial === ad { requestInterstitial = nil forgetCurrentInterstitial() diff --git a/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMRewardedEventHandler.swift b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMRewardedEventHandler.swift index 20b4759fe..54a3ec377 100644 --- a/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMRewardedEventHandler.swift +++ b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/GAMRewardedEventHandler.swift @@ -58,8 +58,7 @@ public class GAMRewardedAdEventHandler : // This is a very dirty hack based on dynamic properties of Objc Object. // Need to rewrite Interstitial ad loader to swift and find out how to pass the reward - public weak var loadingDelegate: RewardedEventLoadingDelegate? - + public weak var loadingDelegate: InterstitialEventLoadingDelegate? public weak var interactionDelegate: RewardedEventInteractionDelegate? // MARK: - Public Methods @@ -169,7 +168,7 @@ public class GAMRewardedAdEventHandler : forgetCurrentRewarded() embeddedRewarded = rewarded - loadingDelegate?.reward = rewarded?.reward + interactionDelegate?.userDidEarnReward(rewarded?.reward?.toPrebidReward()) loadingDelegate?.adServerDidWin() } } @@ -190,9 +189,9 @@ public class GAMRewardedAdEventHandler : embeddedRewarded = rewarded isExpectingAppEvent = false appEventTimer?.invalidate() - appEventTimer = nil; + appEventTimer = nil - loadingDelegate?.reward = rewarded?.reward + interactionDelegate?.userDidEarnReward(rewarded?.reward?.toPrebidReward()) loadingDelegate?.adServerDidWin() } @@ -211,7 +210,6 @@ public class GAMRewardedAdEventHandler : forgetCurrentRewarded() proxyRewarded = rewarded - loadingDelegate?.reward = rewarded?.reward loadingDelegate?.prebidDidWin() } } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventLoadingDelegate.swift b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/Utils/GADReward+Extensions.swift similarity index 64% rename from PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventLoadingDelegate.swift rename to EventHandlers/PrebidMobileGAMEventHandlers/Sources/Utils/GADReward+Extensions.swift index 85441cb4c..78a5981ae 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventLoadingDelegate.swift +++ b/EventHandlers/PrebidMobileGAMEventHandlers/Sources/Utils/GADReward+Extensions.swift @@ -1,4 +1,4 @@ -/*   Copyright 2018-2021 Prebid.org, Inc. +/*   Copyright 2018-2024 Prebid.org, Inc.  Licensed under the Apache License, Version 2.0 (the "License");  you may not use this file except in compliance with the License. @@ -12,12 +12,16 @@  See the License for the specific language governing permissions and  limitations under the License.  */ -import Foundation -@objc public protocol RewardedEventLoadingDelegate : InterstitialEventLoadingDelegate { +import GoogleMobileAds +import PrebidMobile - /*! - @abstract The reward to be given to the user. May be assigned on successful loading. - */ - weak var reward: NSObject? { get set } +extension GADAdReward { + + func toPrebidReward() -> PrebidReward { + let reward = PrebidReward() + reward.type = type + reward.count = amount + return reward + } } diff --git a/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAInterstitialAdapter+MARewardedAdapter.swift b/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAInterstitialAdapter+MARewardedAdapter.swift index aa2c3b495..3dd04e14b 100644 --- a/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAInterstitialAdapter+MARewardedAdapter.swift +++ b/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAInterstitialAdapter+MARewardedAdapter.swift @@ -13,7 +13,6 @@  limitations under the License.  */ -import Foundation import PrebidMobile import AppLovinSDK @@ -24,52 +23,20 @@ extension PrebidMAXMediationAdapter: MAInterstitialAdapter, // MARK: - MAInterstitialAdapter - public func loadInterstitialAd(for parameters: MAAdapterResponseParameters, andNotify delegate: MAInterstitialAdapterDelegate) { + public func loadInterstitialAd( + for parameters: MAAdapterResponseParameters, + andNotify delegate: MAInterstitialAdapterDelegate + ) { interstitialDelegate = delegate - guard let serverParameter = parameters.serverParameters[MAXCustomParametersKey] as? [String: String] else { - let error = MAAdapterError(nsError: MAXAdaptersError.noServerParameter) - interstitialDelegate?.didFailToLoadInterstitialAdWithError(error) - return + switch createInterstitialController(with: parameters) { + case .success(let controller): + self.interstitialController = controller + interstitialController?.loadAd() + case .failure(let error): + let maError = MAAdapterError(nsError: error) + interstitialDelegate?.didFailToLoadInterstitialAdWithError(maError) } - - guard let bid = parameters.localExtraParameters[PBMMediationAdUnitBidKey] as? Bid else { - let error = MAAdapterError(nsError: MAXAdaptersError.noBidInLocalExtraParameters) - interstitialDelegate?.didFailToLoadInterstitialAdWithError(error) - return - } - - guard let targetingInfo = bid.targetingInfo else { - let error = MAAdapterError(nsError: MAXAdaptersError.noTargetingInfoInBid) - interstitialDelegate?.didFailToLoadInterstitialAdWithError(error) - return - } - - guard MediationUtils.isServerParameterDictInTargetingInfoDict(serverParameter, targetingInfo) else { - let error = MAAdapterError(nsError: MAXAdaptersError.wrongServerParameter) - interstitialDelegate?.didFailToLoadInterstitialAdWithError(error) - return - } - - guard let configId = parameters.localExtraParameters[PBMMediationConfigIdKey] as? String else { - let error = MAAdapterError(nsError: MAXAdaptersError.noConfigIdInLocalExtraParameters) - interstitialDelegate?.didFailToLoadInterstitialAdWithError(error) - return - } - - interstitialController = InterstitialController(bid: bid, configId: configId) - interstitialController?.loadingDelegate = self - interstitialController?.interactionDelegate = self - - if let videoAdConfig = parameters.localExtraParameters[PBMMediationVideoAdConfiguration] as? VideoControlsConfiguration { - interstitialController?.videoControlsConfig = videoAdConfig - } - - if let videoParameters = parameters.localExtraParameters[PBMMediationVideoParameters] as? VideoParameters { - interstitialController?.videoParameters = videoParameters - } - - interstitialController?.loadAd() } public func showInterstitialAd(for parameters: MAAdapterResponseParameters, andNotify delegate: MAInterstitialAdapterDelegate) { @@ -85,58 +52,71 @@ extension PrebidMAXMediationAdapter: MAInterstitialAdapter, public func loadRewardedAd(for parameters: MAAdapterResponseParameters, andNotify delegate: MARewardedAdapterDelegate) { rewardedDelegate = delegate - guard let serverParameter = parameters.serverParameters[MAXCustomParametersKey] as? [String: String] else { - let error = MAAdapterError(nsError: MAXAdaptersError.noServerParameter) - rewardedDelegate?.didFailToLoadRewardedAdWithError(error) - return + switch createInterstitialController(with: parameters) { + case .success(let controller): + interstitialController = controller + interstitialController?.isRewarded = true + interstitialController?.loadAd() + case .failure(let error): + let maError = MAAdapterError(nsError: error) + rewardedDelegate?.didFailToLoadRewardedAdWithError(maError) + } + } + + public func showRewardedAd( + for parameters: MAAdapterResponseParameters, + andNotify delegate: MARewardedAdapterDelegate + ) { + if interstitialAdAvailable { + interstitialController?.show() + } else { + interstitialDelegate?.didFailToLoadInterstitialAdWithError(MAAdapterError.adNotReady) + } + } + + private func createInterstitialController( + with parameters: MAAdapterResponseParameters + ) -> Result { + + guard let serverParameter = parameters + .serverParameters[MAXCustomParametersKey] as? [String: String] else { + return .failure(MAXAdaptersError.noServerParameter) } - guard let bid = parameters.localExtraParameters[PBMMediationAdUnitBidKey] as? Bid else { - let error = MAAdapterError(nsError: MAXAdaptersError.noBidInLocalExtraParameters) - rewardedDelegate?.didFailToLoadRewardedAdWithError(error) - return + guard let bid = parameters + .localExtraParameters[PBMMediationAdUnitBidKey] as? Bid else { + return .failure(MAXAdaptersError.noBidInLocalExtraParameters) } guard let targetingInfo = bid.targetingInfo else { - let error = MAAdapterError(nsError: MAXAdaptersError.noTargetingInfoInBid) - rewardedDelegate?.didFailToLoadRewardedAdWithError(error) - return + return .failure(MAXAdaptersError.noTargetingInfoInBid) } - guard MediationUtils.isServerParameterDictInTargetingInfoDict(serverParameter, targetingInfo) else { - let error = MAAdapterError(nsError: MAXAdaptersError.wrongServerParameter) - rewardedDelegate?.didFailToLoadRewardedAdWithError(error) - return + guard MediationUtils + .isServerParameterDictInTargetingInfoDict(serverParameter, targetingInfo) else { + return .failure(MAXAdaptersError.wrongServerParameter) } - guard let configId = parameters.localExtraParameters[PBMMediationConfigIdKey] as? String else { - let error = MAAdapterError(nsError: MAXAdaptersError.noConfigIdInLocalExtraParameters) - rewardedDelegate?.didFailToLoadRewardedAdWithError(error) - return + guard let configId = parameters + .localExtraParameters[PBMMediationConfigIdKey] as? String else { + return .failure(MAXAdaptersError.noConfigIdInLocalExtraParameters) } - interstitialController = InterstitialController(bid: bid, configId: configId) - interstitialController?.loadingDelegate = self - interstitialController?.interactionDelegate = self - interstitialController?.isOptIn = true + let interstitialController = InterstitialController(bid: bid, configId: configId) + interstitialController.loadingDelegate = self + interstitialController.interactionDelegate = self - if let videoAdConfig = parameters.localExtraParameters[PBMMediationVideoAdConfiguration] as? VideoControlsConfiguration { - interstitialController?.videoControlsConfig = videoAdConfig + if let videoAdConfig = parameters + .localExtraParameters[PBMMediationVideoAdConfiguration] as? VideoControlsConfiguration { + interstitialController.videoControlsConfig = videoAdConfig } - if let videoParameters = parameters.localExtraParameters[PBMMediationVideoParameters] as? VideoParameters { - interstitialController?.videoParameters = videoParameters + if let videoParameters = parameters + .localExtraParameters[PBMMediationVideoParameters] as? VideoParameters { + interstitialController.videoParameters = videoParameters } - interstitialController?.loadAd() - } - - public func showRewardedAd(for parameters: MAAdapterResponseParameters, andNotify delegate: MARewardedAdapterDelegate) { - if interstitialAdAvailable { - interstitialController?.show() - } else { - interstitialDelegate?.didFailToLoadInterstitialAdWithError(MAAdapterError.adNotReady) - } + return .success(interstitialController) } // MARK: - InterstitialControllerLoadingDelegate @@ -156,9 +136,7 @@ extension PrebidMAXMediationAdapter: MAInterstitialAdapter, // MARK: - InterstitialControllerInteractionDelegate - public func trackImpression(forInterstitialController: InterstitialController) { - - } + public func trackImpression(forInterstitialController: InterstitialController) {} public func interstitialControllerDidClickAd(_ interstitialController: InterstitialController) { interstitialDelegate?.didClickInterstitialAd() @@ -170,9 +148,7 @@ extension PrebidMAXMediationAdapter: MAInterstitialAdapter, rewardedDelegate?.didHideRewardedAd() } - public func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) { - - } + public func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) {} public func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) { interstitialDelegate?.didDisplayInterstitialAd() @@ -184,7 +160,21 @@ extension PrebidMAXMediationAdapter: MAInterstitialAdapter, rewardedDelegate?.didRewardUser(with: MAReward()) } - public func viewControllerForModalPresentation(fromInterstitialController: InterstitialController) -> UIViewController? { + public func viewControllerForModalPresentation( + fromInterstitialController: InterstitialController + ) -> UIViewController? { return UIApplication.shared.windows.first?.rootViewController } + + public func trackUserReward( + _ interstitialController: InterstitialController, + _ reward: PrebidReward + ) { + let reward = MAReward( + amount: reward.count?.intValue ?? 0, + label: reward.type ?? "" + ) + + rewardedDelegate?.didRewardUser(with: reward) + } } diff --git a/Example/PrebidDemo/PrebidDemo.xcodeproj/project.pbxproj b/Example/PrebidDemo/PrebidDemo.xcodeproj/project.pbxproj index b054d5d0d..31472ff3a 100644 --- a/Example/PrebidDemo/PrebidDemo.xcodeproj/project.pbxproj +++ b/Example/PrebidDemo/PrebidDemo.xcodeproj/project.pbxproj @@ -67,6 +67,14 @@ 5344E6C12922A97200A1F582 /* MAXVideoInterstitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5344E6C02922A97200A1F582 /* MAXVideoInterstitialViewController.swift */; }; 5344E6C32922AAE500A1F582 /* MAXVideoRewardedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5344E6C22922AAE500A1F582 /* MAXVideoRewardedViewController.swift */; }; 5344E6C52922AC2C00A1F582 /* MAXNativeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5344E6C42922AC2C00A1F582 /* MAXNativeViewController.swift */; }; + 534C614D2CB7EFD50026119A /* InAppDisplayRewardedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534C614C2CB7EFD50026119A /* InAppDisplayRewardedViewController.swift */; }; + 534C614F2CB7F20A0026119A /* GAMDisplayRewardedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534C614E2CB7F20A0026119A /* GAMDisplayRewardedViewController.swift */; }; + 534C61552CB7F32D0026119A /* MAXDisplayRewardedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534C61542CB7F32D0026119A /* MAXDisplayRewardedViewController.swift */; }; + 534C61572CB7F4780026119A /* AdMobDisplayRewardedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534C61562CB7F4780026119A /* AdMobDisplayRewardedViewController.swift */; }; + 534C615B2CB809F70026119A /* InAppDisplayRewardedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C615A2CB809F70026119A /* InAppDisplayRewardedViewController.m */; }; + 534C615E2CB84DCB0026119A /* AdMobDisplayRewardedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C615D2CB84DCB0026119A /* AdMobDisplayRewardedViewController.m */; }; + 534C61612CB850FD0026119A /* GAMDisplayRewardedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C61602CB850FD0026119A /* GAMDisplayRewardedViewController.m */; }; + 534C61642CB851C10026119A /* MAXDisplayRewardedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C61632CB851C10026119A /* MAXDisplayRewardedViewController.m */; }; 539928BF292534EB0078053C /* IntegrationCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 539928BE292534EA0078053C /* IntegrationCase.m */; }; 539928C2292535B70078053C /* AdFormatDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 539928C1292535B70078053C /* AdFormatDescriptor.m */; }; 539928C92925379C0078053C /* IntegrationKindDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 539928C82925379C0078053C /* IntegrationKindDescriptor.m */; }; @@ -273,6 +281,18 @@ 5344E6C02922A97200A1F582 /* MAXVideoInterstitialViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MAXVideoInterstitialViewController.swift; sourceTree = ""; }; 5344E6C22922AAE500A1F582 /* MAXVideoRewardedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MAXVideoRewardedViewController.swift; sourceTree = ""; }; 5344E6C42922AC2C00A1F582 /* MAXNativeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MAXNativeViewController.swift; sourceTree = ""; }; + 534C614C2CB7EFD50026119A /* InAppDisplayRewardedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppDisplayRewardedViewController.swift; sourceTree = ""; }; + 534C614E2CB7F20A0026119A /* GAMDisplayRewardedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GAMDisplayRewardedViewController.swift; sourceTree = ""; }; + 534C61542CB7F32D0026119A /* MAXDisplayRewardedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MAXDisplayRewardedViewController.swift; sourceTree = ""; }; + 534C61562CB7F4780026119A /* AdMobDisplayRewardedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdMobDisplayRewardedViewController.swift; sourceTree = ""; }; + 534C61592CB809F70026119A /* InAppDisplayRewardedViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InAppDisplayRewardedViewController.h; sourceTree = ""; }; + 534C615A2CB809F70026119A /* InAppDisplayRewardedViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InAppDisplayRewardedViewController.m; sourceTree = ""; }; + 534C615C2CB84DCB0026119A /* AdMobDisplayRewardedViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AdMobDisplayRewardedViewController.h; sourceTree = ""; }; + 534C615D2CB84DCB0026119A /* AdMobDisplayRewardedViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AdMobDisplayRewardedViewController.m; sourceTree = ""; }; + 534C615F2CB850FD0026119A /* GAMDisplayRewardedViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GAMDisplayRewardedViewController.h; sourceTree = ""; }; + 534C61602CB850FD0026119A /* GAMDisplayRewardedViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GAMDisplayRewardedViewController.m; sourceTree = ""; }; + 534C61622CB851C10026119A /* MAXDisplayRewardedViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MAXDisplayRewardedViewController.h; sourceTree = ""; }; + 534C61632CB851C10026119A /* MAXDisplayRewardedViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MAXDisplayRewardedViewController.m; sourceTree = ""; }; 5354C0C62923E99800DB25EA /* TestUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TestUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 539928BD292534BB0078053C /* IntegrationCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IntegrationCase.h; sourceTree = ""; }; 539928BE292534EA0078053C /* IntegrationCase.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IntegrationCase.m; sourceTree = ""; }; @@ -536,6 +556,7 @@ 53072B802922753D00228462 /* InAppVideoInterstitialLandscapeViewController.swift */, 53072B7E2922748200228462 /* InAppVideoInterstitialVerticalViewController.swift */, 53072B7C2922733800228462 /* InAppVideoInterstitialViewController.swift */, + 534C614C2CB7EFD50026119A /* InAppDisplayRewardedViewController.swift */, 53072B82292275EF00228462 /* InAppVideoRewardedViewController.swift */, ); path = "In-App"; @@ -558,6 +579,7 @@ 5344E6B629228CE600A1F582 /* AdMobNativeViewController.swift */, 5344E6AE2922885200A1F582 /* AdMobVideoBannerViewController.swift */, 5344E6B229228AAB00A1F582 /* AdMobVideoInterstitialViewController.swift */, + 534C61562CB7F4780026119A /* AdMobDisplayRewardedViewController.swift */, 5344E6B429228B7C00A1F582 /* AdMobVideoRewardedViewController.swift */, ); path = AdMob; @@ -572,6 +594,7 @@ 5344E6C42922AC2C00A1F582 /* MAXNativeViewController.swift */, 5344E6BC2922A67D00A1F582 /* MAXVideoBannerViewController.swift */, 5344E6C02922A97200A1F582 /* MAXVideoInterstitialViewController.swift */, + 534C61542CB7F32D0026119A /* MAXDisplayRewardedViewController.swift */, 5344E6C22922AAE500A1F582 /* MAXVideoRewardedViewController.swift */, ); path = MAX; @@ -642,6 +665,7 @@ 5344E6AA2922826100A1F582 /* GAMNativeViewController.swift */, 5344E6A229227DE100A1F582 /* GAMVideoBannerViewController.swift */, 5344E6A62922808500A1F582 /* GAMVideoInterstitialViewController.swift */, + 534C614E2CB7F20A0026119A /* GAMDisplayRewardedViewController.swift */, 5344E6A82922815000A1F582 /* GAMVideoRewardedViewController.swift */, ); path = RenderingAPI; @@ -791,6 +815,8 @@ 53F35F0829267A30001C1183 /* GAMDisplayInterstitialViewController.m */, 53F35F0A29267C65001C1183 /* GAMVideoInterstitialViewController.h */, 53F35F0B29267C65001C1183 /* GAMVideoInterstitialViewController.m */, + 534C615F2CB850FD0026119A /* GAMDisplayRewardedViewController.h */, + 534C61602CB850FD0026119A /* GAMDisplayRewardedViewController.m */, 53F35F0D29267E36001C1183 /* GAMVideoRewardedViewController.h */, 53F35F0E29267E36001C1183 /* GAMVideoRewardedViewController.m */, 53F35F1029267FEE001C1183 /* GAMNativeViewController.h */, @@ -812,6 +838,8 @@ 53F35F1729268C62001C1183 /* AdMobVideoBannerViewController.m */, 53F35F1D29269668001C1183 /* AdMobVideoInterstitialViewController.h */, 53F35F1E29269668001C1183 /* AdMobVideoInterstitialViewController.m */, + 534C615C2CB84DCB0026119A /* AdMobDisplayRewardedViewController.h */, + 534C615D2CB84DCB0026119A /* AdMobDisplayRewardedViewController.m */, 53F35F202926982E001C1183 /* AdMobVideoRewardedViewController.h */, 53F35F212926982E001C1183 /* AdMobVideoRewardedViewController.m */, ); @@ -830,6 +858,8 @@ 53F35F452926CCD5001C1183 /* MAXDisplayInterstitialViewController.m */, 53F35F472926CFE3001C1183 /* MAXVideoInterstitialViewController.h */, 53F35F482926CFE3001C1183 /* MAXVideoInterstitialViewController.m */, + 534C61622CB851C10026119A /* MAXDisplayRewardedViewController.h */, + 534C61632CB851C10026119A /* MAXDisplayRewardedViewController.m */, 53F35F4A2926D130001C1183 /* MAXVideoRewardedViewController.h */, 53F35F4B2926D130001C1183 /* MAXVideoRewardedViewController.m */, 53F35F4D2926D5C0001C1183 /* MAXNativeViewController.h */, @@ -853,6 +883,8 @@ 53F35F332926BAFE001C1183 /* InAppVideoInterstitialVerticalViewController.m */, 53F35F352926BCAF001C1183 /* InAppVideoInterstitialLandscapeViewController.h */, 53F35F362926BCAF001C1183 /* InAppVideoInterstitialLandscapeViewController.m */, + 534C61592CB809F70026119A /* InAppDisplayRewardedViewController.h */, + 534C615A2CB809F70026119A /* InAppDisplayRewardedViewController.m */, 53F35F382926BDB7001C1183 /* InAppVideoRewardedViewController.h */, 53F35F392926BDB7001C1183 /* InAppVideoRewardedViewController.m */, 53F35F3B2926BF39001C1183 /* InAppNativeViewController.h */, @@ -1476,6 +1508,7 @@ 5344E6C32922AAE500A1F582 /* MAXVideoRewardedViewController.swift in Sources */, 53072B6929217D1800228462 /* GAMOriginalAPIVideoInstreamViewController.swift in Sources */, 5344E6A92922815000A1F582 /* GAMVideoRewardedViewController.swift in Sources */, + 534C61572CB7F4780026119A /* AdMobDisplayRewardedViewController.swift in Sources */, 53072B7B292270EE00228462 /* InAppDisplayInterstitialViewController.swift in Sources */, 53072B5E2921522000228462 /* GAMOriginalAPIVideoRewardedViewController.swift in Sources */, 530D4AA6291A4BC600A2C796 /* AppDelegate.swift in Sources */, @@ -1484,6 +1517,7 @@ 53072B812922753D00228462 /* InAppVideoInterstitialLandscapeViewController.swift in Sources */, 5344E6C12922A97200A1F582 /* MAXVideoInterstitialViewController.swift in Sources */, 53072B7D2922733800228462 /* InAppVideoInterstitialViewController.swift in Sources */, + 534C614F2CB7F20A0026119A /* GAMDisplayRewardedViewController.swift in Sources */, 5344E6C52922AC2C00A1F582 /* MAXNativeViewController.swift in Sources */, 53B03DA5291BCCE1000117E1 /* GAMOriginalAPIDisplayBannerViewController.swift in Sources */, 53A368F82AB8482300A03B3E /* GAMOriginalAPIMultiformatInAppNativeViewController.swift in Sources */, @@ -1504,7 +1538,9 @@ 53072B592921023E00228462 /* InterstitialBaseViewController.swift in Sources */, 53072B672921726C00228462 /* GAMOriginalAPINativeViewController.swift in Sources */, 53072B732922621600228462 /* GAMOriginalAPIVideoBannerViewController.swift in Sources */, + 534C61552CB7F32D0026119A /* MAXDisplayRewardedViewController.swift in Sources */, 53072B6529215D4200228462 /* GAMOriginalAPINativeBannerViewController.swift in Sources */, + 534C614D2CB7EFD50026119A /* InAppDisplayRewardedViewController.swift in Sources */, 5344E6A129227BFE00A1F582 /* GAMDisplayBannerViewController.swift in Sources */, 530D4AA8291A4BC600A2C796 /* SceneDelegate.swift in Sources */, 5344E6BF2922A79000A1F582 /* MAXDisplayInterstitialViewController.swift in Sources */, @@ -1531,6 +1567,7 @@ 53BA02BA292531D000BE6015 /* ExamplesViewController.m in Sources */, 5399290429264C5A0078053C /* GAMOriginalAPIVideoInstreamViewController.m in Sources */, 53F35F2E2926A6D6001C1183 /* InAppDisplayInterstitialViewController.m in Sources */, + 534C615E2CB84DCB0026119A /* AdMobDisplayRewardedViewController.m in Sources */, 53F35F1229267FEE001C1183 /* GAMNativeViewController.m in Sources */, 53F35F06292675A0001C1183 /* GAMVideoBannerViewController.m in Sources */, 539928BF292534EB0078053C /* IntegrationCase.m in Sources */, @@ -1542,6 +1579,7 @@ 53A369012AB8CDEB00A03B3E /* GAMOriginalAPIMultiformatInAppNativeViewController.m in Sources */, 539928DB29254F2A0078053C /* BannerBaseViewController.m in Sources */, 53F35F57292792D6001C1183 /* SettingsViewController.m in Sources */, + 534C61642CB851C10026119A /* MAXDisplayRewardedViewController.m in Sources */, 539928E22925505B0078053C /* InterstitialBaseViewController.m in Sources */, 53F35F432926C84E001C1183 /* MAXVideoBannerViewController.m in Sources */, 5399290A292665D80078053C /* GAMOriginalAPINativeViewController.m in Sources */, @@ -1549,6 +1587,7 @@ 539F960629DEEA0A0061E7A5 /* GAMOriginalAPIMultiformatBannerViewController.m in Sources */, 53BA02B4292531D000BE6015 /* AppDelegate.m in Sources */, 539929072926580B0078053C /* GAMOriginalAPINativeBannerViewController.m in Sources */, + 534C615B2CB809F70026119A /* InAppDisplayRewardedViewController.m in Sources */, 53F35F492926CFE3001C1183 /* MAXVideoInterstitialViewController.m in Sources */, 53F35F03292671E7001C1183 /* GAMDisplayBannerViewController.m in Sources */, 533784CC2AC2CFF5009A8650 /* GAMOriginalAPIMultiformatNativeStylesViewController.m in Sources */, @@ -1562,6 +1601,7 @@ 53F35F152926845C001C1183 /* AdMobDisplayBannerViewController.m in Sources */, 539928F12925519D0078053C /* InstreamBaseViewController.m in Sources */, 53F35F222926982E001C1183 /* AdMobVideoRewardedViewController.m in Sources */, + 534C61612CB850FD0026119A /* GAMDisplayRewardedViewController.m in Sources */, 53F35F402926C2B7001C1183 /* MAXDisplayBannerViewController.m in Sources */, 539928CC292539700078053C /* IntegrationCaseManager.m in Sources */, 53F35F372926BCAF001C1183 /* InAppVideoInterstitialLandscapeViewController.m in Sources */, diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobDisplayRewardedViewController.h b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobDisplayRewardedViewController.h new file mode 100644 index 000000000..2587466e5 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobDisplayRewardedViewController.h @@ -0,0 +1,28 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "InterstitialBaseViewController.h" + +@import GoogleMobileAds; +@import PrebidMobile; +@import PrebidMobileAdMobAdapters; + +NS_ASSUME_NONNULL_BEGIN + +@interface AdMobDisplayRewardedViewController : InterstitialBaseViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobDisplayRewardedViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobDisplayRewardedViewController.m new file mode 100644 index 000000000..7031294ba --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobDisplayRewardedViewController.m @@ -0,0 +1,90 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "AdMobDisplayRewardedViewController.h" +#import "PrebidDemoMacros.h" + +NSString * const storedImpDisplayRewardedAdMob = @"prebid-demo-banner-rewarded-time"; +NSString * const adMobAdUnitDisplayRewardedId = @"ca-app-pub-5922967660082475/5628505938"; + +@interface AdMobDisplayRewardedViewController () + +// Prebid +@property (nonatomic) MediationRewardedAdUnit * admobRewardedAdUnit; +@property (nonatomic) AdMobMediationRewardedUtils * mediationDelegate; + +// AdMob +@property (nonatomic) GADRewardedAd * gadRewardedAd; + +@end + +@implementation AdMobDisplayRewardedViewController + +- (void)loadView { + [super loadView]; + + [self createAd]; +} + +- (void)createAd { + // 1. Create a GADRequest + GADRequest * gadRequest = [GADRequest new]; + + // 2. Create an AdMobMediationRewardedUtils + self.mediationDelegate = [[AdMobMediationRewardedUtils alloc] initWithGadRequest:gadRequest]; + + // 3. Create a MediationRewardedAdUnit + self.admobRewardedAdUnit = [[MediationRewardedAdUnit alloc] initWithConfigId:storedImpDisplayRewardedAdMob + mediationDelegate:self.mediationDelegate]; + + // 4. Make a bid request to Prebid Server + @weakify(self); + [self.admobRewardedAdUnit fetchDemandWithCompletion:^(enum ResultCode resultCode) { + @strongify(self); + + // 5. Load the rewarded ad + @weakify(self); + [GADRewardedAd loadWithAdUnitID:adMobAdUnitDisplayRewardedId + request:gadRequest + completionHandler:^(GADRewardedAd * _Nullable rewardedAd, NSError * _Nullable error) { + @strongify(self); + if (!self) { return; } + + if (error != nil) { + PBMLogError(@"%@", error.localizedDescription); + return; + } + + // 6. Present the rewarded ad + if (rewardedAd != nil) { + self.gadRewardedAd = rewardedAd; + self.gadRewardedAd.fullScreenContentDelegate = self; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self.gadRewardedAd presentFromRootViewController:self userDidEarnRewardHandler:^{ + NSLog(@"User did earn reward."); + }]; + }); + } + }]; + }]; +} + +// MARK: - GADFullScreenContentDelegate + +- (void)ad:(id)ad didFailToPresentFullScreenContentWithError:(NSError *)error { + PBMLogError(@"%@", error.localizedDescription); +} + +@end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobVideoRewardedViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobVideoRewardedViewController.m index 2fbb0ebe9..0b4aac480 100644 --- a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobVideoRewardedViewController.m +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/AdMob/AdMobVideoRewardedViewController.m @@ -16,8 +16,8 @@ #import "AdMobVideoRewardedViewController.h" #import "PrebidDemoMacros.h" -NSString * const storedImpVideoRewardedAdMob = @"prebid-demo-video-rewarded-320-480"; -NSString * const adMobAdUnitRewardedId = @"ca-app-pub-5922967660082475/7397370641"; +NSString * const storedImpVideoRewardedAdMob = @"prebid-demo-video-rewarded-endcard-time"; +NSString * const adMobAdUnitVideoRewardedId = @"ca-app-pub-5922967660082475/5628505938"; @interface AdMobVideoRewardedViewController () @@ -55,7 +55,9 @@ - (void)createAd { // 5. Load the rewarded ad @weakify(self); - [GADRewardedAd loadWithAdUnitID:adMobAdUnitRewardedId request:gadRequest completionHandler:^(GADRewardedAd * _Nullable rewardedAd, NSError * _Nullable error) { + [GADRewardedAd loadWithAdUnitID:adMobAdUnitVideoRewardedId + request:gadRequest + completionHandler:^(GADRewardedAd * _Nullable rewardedAd, NSError * _Nullable error) { @strongify(self); if (!self) { return; } @@ -70,6 +72,7 @@ - (void)createAd { self.gadRewardedAd.fullScreenContentDelegate = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self.gadRewardedAd presentFromRootViewController:self userDidEarnRewardHandler:^{ + NSLog(@"User did earn reward."); }]; }); } diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.h b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.h new file mode 100644 index 000000000..dff54e985 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.h @@ -0,0 +1,28 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "InterstitialBaseViewController.h" + +@import GoogleMobileAds; +@import PrebidMobile; +@import PrebidMobileGAMEventHandlers; + +NS_ASSUME_NONNULL_BEGIN + +@interface GAMDisplayRewardedViewController : InterstitialBaseViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.m new file mode 100644 index 000000000..cbde008df --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.m @@ -0,0 +1,64 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "GAMDisplayRewardedViewController.h" + +NSString * const storedImpGAMDisplayRewarded = @"prebid-demo-banner-rewarded-time"; +NSString * const gamAdUnitDisplayRewardedRendering = @"/21808260008/prebid_oxb_rewarded_video_test"; + +@interface GAMDisplayRewardedViewController () + +// Prebid +@property (nonatomic) RewardedAdUnit * rewardedAdUnit; + +@end + +@implementation GAMDisplayRewardedViewController + +- (void)loadView { + [super loadView]; + + [self createAd]; +} + +- (void)createAd { + // 1. Create a GAMRewardedAdEventHandler + GAMRewardedAdEventHandler * eventHandler = [[GAMRewardedAdEventHandler alloc] initWithAdUnitID:gamAdUnitDisplayRewardedRendering]; + + // 2. Create a RewardedAdUnit + self.rewardedAdUnit = [[RewardedAdUnit alloc] initWithConfigID:storedImpGAMDisplayRewarded eventHandler:eventHandler]; + self.rewardedAdUnit.delegate = self; + + // 3. Load the rewarded ad + [self.rewardedAdUnit loadAd]; +} + +// MARK: - RewardedAdUnitDelegate + +- (void)rewardedAdDidReceiveAd:(RewardedAdUnit *)rewardedAd { + if (rewardedAd.isReady) { + [rewardedAd showFrom:self]; + } +} + +- (void)rewardedAd:(RewardedAdUnit *)rewardedAd didFailToReceiveAdWithError:(NSError *)error { + PBMLogError(@"%@", error.localizedDescription); +} + +- (void)rewardedAdUserDidEarnReward:(RewardedAdUnit *)rewardedAd reward:(PrebidReward *)reward { + NSLog(@"User did earn reward - type: %@, count: %f", reward.type, [reward.count doubleValue]); +} + +@end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMVideoRewardedViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMVideoRewardedViewController.m index acf2fbcfc..e457dbdc9 100644 --- a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMVideoRewardedViewController.m +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/RenderingAPI/GAMVideoRewardedViewController.m @@ -15,7 +15,7 @@ #import "GAMVideoRewardedViewController.h" -NSString * const storedImpGAMVideoRewarded = @"prebid-demo-video-rewarded-320-480"; +NSString * const storedImpGAMVideoRewarded = @"prebid-demo-video-rewarded-endcard-time"; NSString * const gamAdUnitVideoRewardedRendering = @"/21808260008/prebid_oxb_rewarded_video_test"; @interface GAMVideoRewardedViewController () @@ -48,11 +48,17 @@ - (void)createAd { // MARK: - RewardedAdUnitDelegate - (void)rewardedAdDidReceiveAd:(RewardedAdUnit *)rewardedAd { - [self.rewardedAdUnit showFrom:self]; + if (rewardedAd.isReady) { + [rewardedAd showFrom:self]; + } } - (void)rewardedAd:(RewardedAdUnit *)rewardedAd didFailToReceiveAdWithError:(NSError *)error { PBMLogError(@"%@", error.localizedDescription); } +- (void)rewardedAdUserDidEarnReward:(RewardedAdUnit *)rewardedAd reward:(PrebidReward *)reward { + NSLog(@"User did earn reward - type: %@, count: %f", reward.type, [reward.count doubleValue]); +} + @end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayRewardedViewController.h b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayRewardedViewController.h new file mode 100644 index 000000000..1bf2222d3 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayRewardedViewController.h @@ -0,0 +1,26 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "InterstitialBaseViewController.h" + +@import PrebidMobile; + +NS_ASSUME_NONNULL_BEGIN + +@interface InAppDisplayRewardedViewController : InterstitialBaseViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayRewardedViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayRewardedViewController.m new file mode 100644 index 000000000..a2df4fadc --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayRewardedViewController.m @@ -0,0 +1,61 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "InAppDisplayRewardedViewController.h" +#import "PrebidDemoMacros.h" + +NSString * const storedImpBannerRewardedInApp = @"prebid-demo-banner-rewarded-time"; + +@interface InAppDisplayRewardedViewController () + +// Prebid +@property (nonatomic) RewardedAdUnit * rewardedAdUnit; + +@end + +@implementation InAppDisplayRewardedViewController + +- (void)loadView { + [super loadView]; + + [self createAd]; +} + +- (void)createAd { + // 1. Create a RewardedAdUnit + self.rewardedAdUnit = [[RewardedAdUnit alloc] initWithConfigID:storedImpBannerRewardedInApp]; + self.rewardedAdUnit.delegate = self; + + // 2. Load the rewarded ad + [self.rewardedAdUnit loadAd]; +} + +// MARK: - RewardedAdUnitDelegate + +- (void)rewardedAdDidReceiveAd:(RewardedAdUnit *)rewardedAd { + if (rewardedAd.isReady) { + [rewardedAd showFrom:self]; + } +} + +- (void)rewardedAd:(RewardedAdUnit *)rewardedAd didFailToReceiveAdWithError:(NSError *)error { + PBMLogError(@"%@", error.localizedDescription); +} + +- (void)rewardedAdUserDidEarnReward:(RewardedAdUnit *)rewardedAd reward:(PrebidReward *)reward { + NSLog(@"User did earn reward - type: %@, count: %f", reward.type, [reward.count doubleValue]); +} + +@end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppVideoRewardedViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppVideoRewardedViewController.m index 8d67ce90c..ab468ea73 100644 --- a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppVideoRewardedViewController.m +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppVideoRewardedViewController.m @@ -16,7 +16,7 @@ #import "InAppVideoRewardedViewController.h" #import "PrebidDemoMacros.h" -NSString * const storedImpVideoRewardedInApp = @"prebid-demo-video-rewarded-320-480"; +NSString * const storedImpVideoRewardedInApp = @"prebid-demo-video-rewarded-endcard-time"; @interface InAppVideoRewardedViewController () @@ -45,11 +45,17 @@ - (void)createAd { // MARK: - RewardedAdUnitDelegate - (void)rewardedAdDidReceiveAd:(RewardedAdUnit *)rewardedAd { - [self.rewardedAdUnit showFrom:self]; + if (rewardedAd.isReady) { + [rewardedAd showFrom:self]; + } } - (void)rewardedAd:(RewardedAdUnit *)rewardedAd didFailToReceiveAdWithError:(NSError *)error { PBMLogError(@"%@", error.localizedDescription); } +- (void)rewardedAdUserDidEarnReward:(RewardedAdUnit *)rewardedAd reward:(PrebidReward *)reward { + NSLog(@"User did earn reward - type: %@, count: %f", reward.type, [reward.count doubleValue]); +} + @end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXDisplayRewardedViewController.h b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXDisplayRewardedViewController.h new file mode 100644 index 000000000..48baf028f --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXDisplayRewardedViewController.h @@ -0,0 +1,28 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "InterstitialBaseViewController.h" + +@import AppLovinSDK; +@import PrebidMobile; +@import PrebidMobileMAXAdapters; + +NS_ASSUME_NONNULL_BEGIN + +@interface MAXDisplayRewardedViewController : InterstitialBaseViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXDisplayRewardedViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXDisplayRewardedViewController.m new file mode 100644 index 000000000..c43f765fa --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXDisplayRewardedViewController.m @@ -0,0 +1,93 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MAXDisplayRewardedViewController.h" +#import "PrebidDemoMacros.h" + +NSString * const storedImpDisplayRewardedMAX = @"prebid-demo-banner-rewarded-time"; +NSString * const maxAdUnitDisplayRewardedId = @"75edc39e22574a9d"; + +@interface MAXDisplayRewardedViewController () + +// Prebid +@property (nonatomic) MediationRewardedAdUnit * maxRewardedAdUnit; +@property (nonatomic) MAXMediationRewardedUtils * mediationDelegate; + +// MAX +@property (nonatomic) MARewardedAd * maxRewarded; + +@end + +@implementation MAXDisplayRewardedViewController + +- (void)loadView { + [super loadView]; + + [self createAd]; +} + +- (void)createAd { + // 1. Create a MARewardedAd + self.maxRewarded = [MARewardedAd sharedWithAdUnitIdentifier:maxAdUnitDisplayRewardedId]; + + // 2. Create a MAXMediationRewardedUtils + self.mediationDelegate = [[MAXMediationRewardedUtils alloc] initWithRewardedAd:self.maxRewarded]; + + // 3. Create a MediationRewardedAdUnit + self.maxRewardedAdUnit = [[MediationRewardedAdUnit alloc] initWithConfigId:storedImpDisplayRewardedMAX + mediationDelegate:self.mediationDelegate]; + + // 4. Make a bid request to Prebid Server + @weakify(self); + [self.maxRewardedAdUnit fetchDemandWithCompletion:^(enum ResultCode resultCode) { + @strongify(self); + if (!self) { return; } + + // 5. Load the rewarded ad + self.maxRewarded.delegate = self; + [self.maxRewarded loadAd]; + }]; +} + +// MARK: - MARewardedAdDelegate + +- (void)didLoadAd:(MAAd *)ad { + if (self.maxRewarded != nil && self.maxRewarded.isReady) { + [self.maxRewarded showAd]; + } +} + +- (void)didFailToDisplayAd:(MAAd *)ad withError:(MAError *)error { + PBMLogError(@"%@", error.message); +} + +- (void)didFailToLoadAdForAdUnitIdentifier:(NSString *)adUnitIdentifier withError:(MAError *)error { + PBMLogError(@"%@", error.message); +} + +- (void)didDisplayAd:(MAAd *)ad { +} + +- (void)didHideAd:(MAAd *)ad { +} + +- (void)didClickAd:(MAAd *)ad { +} + +- (void)didRewardUserForAd:(MAAd *)ad withReward:(MAReward *)reward { + NSLog(@"User did earn reward: label - %@, amount - %ld", reward.label, reward.amount); +} + +@end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXVideoRewardedViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXVideoRewardedViewController.m index 7391b9f4e..be2328b3d 100644 --- a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXVideoRewardedViewController.m +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/MAX/MAXVideoRewardedViewController.m @@ -16,7 +16,7 @@ #import "MAXVideoRewardedViewController.h" #import "PrebidDemoMacros.h" -NSString * const storedImpVideoRewardedMAX = @"prebid-demo-video-rewarded-320-480"; +NSString * const storedImpVideoRewardedMAX = @"prebid-demo-video-rewarded-endcard-time"; NSString * const maxAdUnitRewardedId = @"75edc39e22574a9d"; @interface MAXVideoRewardedViewController () @@ -86,6 +86,7 @@ - (void)didClickAd:(MAAd *)ad { } - (void)didRewardUserForAd:(MAAd *)ad withReward:(MAReward *)reward { + NSLog(@"User did earn reward: label - %@, amount - %ld", reward.label, reward.amount); } @end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/AdFormat.h b/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/AdFormat.h index e34d3d77a..bb8f5487d 100644 --- a/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/AdFormat.h +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/AdFormat.h @@ -21,6 +21,7 @@ typedef NS_ENUM(NSUInteger, AdFormat) { AdFormatNativeBanner, AdFormatDisplayInterstitial, AdFormatVideoInterstitial, + AdFormatDisplayRewarded, AdFormatVideoRewarded, AdFormatVideoInstream, AdFormatNative, diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/AdFormatDescriptor.m b/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/AdFormatDescriptor.m index c026f4a05..c61e45dfc 100644 --- a/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/AdFormatDescriptor.m +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/AdFormatDescriptor.m @@ -30,6 +30,8 @@ + (NSString *)getDescriptionForAdFormat:(AdFormat)adFormat { return @"Display Interstitial"; case AdFormatVideoInterstitial: return @"Video Interstitial"; + case AdFormatDisplayRewarded: + return @"Display Rewarded"; case AdFormatVideoRewarded: return @"Video Rewarded"; case AdFormatVideoInstream: diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/IntegrationCaseManager.m b/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/IntegrationCaseManager.m index 0aad45540..b2f5959f5 100644 --- a/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/IntegrationCaseManager.m +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/IntegrationCaseManager.m @@ -35,6 +35,7 @@ #import "InAppVideoInterstitialViewController.h" #import "InAppVideoInterstitialVerticalViewController.h" #import "InAppVideoInterstitialLandscapeViewController.h" +#import "InAppDisplayRewardedViewController.h" #import "InAppVideoRewardedViewController.h" #import "InAppNativeViewController.h" @@ -42,6 +43,7 @@ #import "GAMVideoBannerViewController.h" #import "GAMDisplayInterstitialViewController.h" #import "GAMVideoInterstitialViewController.h" +#import "GAMDisplayRewardedViewController.h" #import "GAMVideoRewardedViewController.h" #import "GAMNativeViewController.h" @@ -49,6 +51,7 @@ #import "AdMobVideoBannerViewController.h" #import "AdMobDisplayInterstitialViewController.h" #import "AdMobVideoInterstitialViewController.h" +#import "AdMobDisplayRewardedViewController.h" #import "AdMobVideoRewardedViewController.h" #import "AdMobNativeViewController.h" @@ -56,6 +59,7 @@ #import "MAXVideoBannerViewController.h" #import "MAXDisplayInterstitialViewController.h" #import "MAXVideoInterstitialViewController.h" +#import "MAXDisplayRewardedViewController.h" #import "MAXVideoRewardedViewController.h" #import "MAXNativeViewController.h" @@ -246,6 +250,16 @@ @implementation IntegrationCaseManager } ], + [ + [IntegrationCase alloc] + initWithTitle:@"In-App Banner Rewarded 320x480" + integrationKind:IntegrationKindInApp + adFormat:AdFormatVideoRewarded + configurationClosure:^UIViewController *{ + return [[InAppDisplayRewardedViewController alloc] init]; + } + ], + [ [IntegrationCase alloc] initWithTitle:@"In-App Video Rewarded 320x480" @@ -306,6 +320,16 @@ @implementation IntegrationCaseManager } ], + [ + [IntegrationCase alloc] + initWithTitle:@"GAM Display Rewarded 320x480" + integrationKind:IntegrationKindGAM + adFormat:AdFormatDisplayRewarded + configurationClosure:^UIViewController *{ + return [[GAMDisplayRewardedViewController alloc] init]; + } + ], + [ [IntegrationCase alloc] initWithTitle:@"GAM Video Rewarded 320x480" @@ -366,6 +390,16 @@ @implementation IntegrationCaseManager } ], + [ + [IntegrationCase alloc] + initWithTitle:@"AdMob Display Rewarded 320x480" + integrationKind:IntegrationKindAdMob + adFormat:AdFormatDisplayRewarded + configurationClosure:^UIViewController *{ + return [[AdMobDisplayRewardedViewController alloc] init]; + } + ], + [ [IntegrationCase alloc] initWithTitle:@"AdMob Video Rewarded 320x480" @@ -426,6 +460,16 @@ @implementation IntegrationCaseManager } ], + [ + [IntegrationCase alloc] + initWithTitle:@"MAX Display Rewarded 320x480" + integrationKind:IntegrationKindMAX + adFormat:AdFormatDisplayRewarded + configurationClosure:^UIViewController *{ + return [[MAXDisplayRewardedViewController alloc] init]; + } + ], + [ [IntegrationCase alloc] initWithTitle:@"MAX Video Rewarded 320x480" diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/AdMob/AdMobDisplayRewardedViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/AdMob/AdMobDisplayRewardedViewController.swift new file mode 100644 index 000000000..7094d5245 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/AdMob/AdMobDisplayRewardedViewController.swift @@ -0,0 +1,83 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import PrebidMobile +import GoogleMobileAds +import PrebidMobileAdMobAdapters + +fileprivate let storedImpDisplayRewarded = "prebid-demo-banner-rewarded-time" +fileprivate let adMobAdUnitRewardedId = "ca-app-pub-5922967660082475/5628505938" + +class AdMobDisplayRewardedViewController: InterstitialBaseViewController, GADFullScreenContentDelegate { + + // Prebid + private var mediationDelegate: AdMobMediationRewardedUtils! + private var admobRewardedAdUnit: MediationRewardedAdUnit! + + // AdMob + private var gadRewardedAd: GADRewardedAd? + + override func loadView() { + super.loadView() + + createAd() + } + + func createAd() { + // 1. Create a GADRequest + let request = GADRequest() + + // 2. Create an AdMobMediationRewardedUtils + mediationDelegate = AdMobMediationRewardedUtils(gadRequest: request) + + // 3. Create a MediationRewardedAdUnit + admobRewardedAdUnit = MediationRewardedAdUnit( + configId: storedImpDisplayRewarded, + mediationDelegate: mediationDelegate + ) + + // 4. Make a bid request to Prebid Server + admobRewardedAdUnit.fetchDemand { [weak self] result in + guard let self = self else { return } + PrebidDemoLogger.shared.info("Prebid demand fetch for AdMob \(result.name())") + + // 5. Load the rewarded ad + GADRewardedAd.load(withAdUnitID: adMobAdUnitRewardedId, request: request) { [weak self] ad, error in + guard let self = self else { return } + + if let error = error { + Log.error(error.localizedDescription) + return + } + + // 6. Present the rewarded ad + self.gadRewardedAd = ad + self.gadRewardedAd?.fullScreenContentDelegate = self + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) { + self.gadRewardedAd?.present(fromRootViewController: self, userDidEarnRewardHandler: { + print("User did earn reward.") + }) + } + } + } + } + + // MARK: - GADFullScreenContentDelegate + + func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) { + PrebidDemoLogger.shared.error("AdMob did fail to receive ad with error: \(error.localizedDescription)") + } +} diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/AdMob/AdMobVideoRewardedViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/AdMob/AdMobVideoRewardedViewController.swift index 009350dd4..9c513665e 100644 --- a/Example/PrebidDemo/PrebidDemoSwift/Examples/AdMob/AdMobVideoRewardedViewController.swift +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/AdMob/AdMobVideoRewardedViewController.swift @@ -18,8 +18,8 @@ import PrebidMobile import GoogleMobileAds import PrebidMobileAdMobAdapters -fileprivate let storedImpVideoRewarded = "prebid-demo-video-rewarded-320-480" -fileprivate let adMobAdUnitRewardedId = "ca-app-pub-5922967660082475/7397370641" +fileprivate let storedImpVideoRewarded = "prebid-demo-video-rewarded-endcard-time" +fileprivate let adMobAdUnitRewardedId = "ca-app-pub-5922967660082475/5628505938" class AdMobVideoRewardedViewController: InterstitialBaseViewController, GADFullScreenContentDelegate { @@ -64,7 +64,12 @@ class AdMobVideoRewardedViewController: InterstitialBaseViewController, GADFullS self.gadRewardedAd = ad self.gadRewardedAd?.fullScreenContentDelegate = self DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) { - self.gadRewardedAd?.present(fromRootViewController: self, userDidEarnRewardHandler: {}) + self.gadRewardedAd?.present( + fromRootViewController: self, + userDidEarnRewardHandler: { + print("User did earn reward.") + } + ) } } } diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.swift new file mode 100644 index 000000000..9d9f4022d --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/RenderingAPI/GAMDisplayRewardedViewController.swift @@ -0,0 +1,66 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import GoogleMobileAds +import PrebidMobile +import PrebidMobileGAMEventHandlers + +fileprivate let storedImpDisplayRewarded = "prebid-demo-banner-rewarded-time" +fileprivate let gamAdUnitBannerRewardedRendering = "/21808260008/prebid_oxb_rewarded_video_test" + +class GAMDisplayRewardedViewController: InterstitialBaseViewController, RewardedAdUnitDelegate { + + // Prebid + private var rewardedAdUnit: RewardedAdUnit! + + override func loadView() { + super.loadView() + + createAd() + } + + func createAd() { + // 1. Create a GAMRewardedAdEventHandler + let eventHandler = GAMRewardedAdEventHandler(adUnitID: gamAdUnitBannerRewardedRendering) + + // 2. Create a RewardedAdUnit + rewardedAdUnit = RewardedAdUnit( + configID: storedImpDisplayRewarded, + eventHandler: eventHandler + ) + + rewardedAdUnit.delegate = self + + // 3. Load the rewarded ad + rewardedAdUnit.loadAd() + } + + // MARK: - RewardedAdUnitDelegate + + func rewardedAdDidReceiveAd(_ rewardedAd: RewardedAdUnit) { + if rewardedAd.isReady { + rewardedAd.show(from: self) + } + } + + func rewardedAd(_ rewardedAd: RewardedAdUnit, didFailToReceiveAdWithError error: Error?) { + PrebidDemoLogger.shared.error("Rewarded ad unit failed to receive ad with error: \(error?.localizedDescription ?? "")") + } + + func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit, reward: PrebidReward) { + PrebidDemoLogger.shared.info("User did earn reward: type - \(reward.type ?? ""), count - \(reward.count ?? 0)") + } +} diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/RenderingAPI/GAMVideoRewardedViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/RenderingAPI/GAMVideoRewardedViewController.swift index 292f6c367..d65795608 100644 --- a/Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/RenderingAPI/GAMVideoRewardedViewController.swift +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/RenderingAPI/GAMVideoRewardedViewController.swift @@ -18,7 +18,7 @@ import GoogleMobileAds import PrebidMobile import PrebidMobileGAMEventHandlers -fileprivate let storedImpVideoRewarded = "prebid-demo-video-rewarded-320-480" +fileprivate let storedImpVideoRewarded = "prebid-demo-video-rewarded-endcard-time" fileprivate let gamAdUnitVideoRewardedRendering = "/21808260008/prebid_oxb_rewarded_video_test" class GAMVideoRewardedViewController: InterstitialBaseViewController, RewardedAdUnitDelegate { @@ -37,7 +37,11 @@ class GAMVideoRewardedViewController: InterstitialBaseViewController, RewardedAd let eventHandler = GAMRewardedAdEventHandler(adUnitID: gamAdUnitVideoRewardedRendering) // 2. Create a RewardedAdUnit - rewardedAdUnit = RewardedAdUnit(configID: storedImpVideoRewarded, eventHandler: eventHandler) + rewardedAdUnit = RewardedAdUnit( + configID: storedImpVideoRewarded, + eventHandler: eventHandler + ) + rewardedAdUnit.delegate = self // 3. Load the rewarded ad @@ -47,10 +51,16 @@ class GAMVideoRewardedViewController: InterstitialBaseViewController, RewardedAd // MARK: - RewardedAdUnitDelegate func rewardedAdDidReceiveAd(_ rewardedAd: RewardedAdUnit) { - rewardedAdUnit.show(from: self) + if rewardedAd.isReady { + rewardedAd.show(from: self) + } } func rewardedAd(_ rewardedAd: RewardedAdUnit, didFailToReceiveAdWithError error: Error?) { PrebidDemoLogger.shared.error("Rewarded ad unit failed to receive ad with error: \(error?.localizedDescription ?? "")") } + + func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit, reward: PrebidReward) { + PrebidDemoLogger.shared.info("User did earn reward: type - \(reward.type ?? ""), count - \(reward.count ?? 0)") + } } diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayRewardedViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayRewardedViewController.swift new file mode 100644 index 000000000..8b80d702e --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayRewardedViewController.swift @@ -0,0 +1,56 @@ +/* Copyright 2019-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import PrebidMobile + +fileprivate let storedImpDisplayRewarded = "prebid-demo-banner-rewarded-time" + +class InAppDisplayRewardedViewController: InterstitialBaseViewController, RewardedAdUnitDelegate { + + // Prebid + private var rewardedAdUnit: RewardedAdUnit! + + override func loadView() { + super.loadView() + + createAd() + } + + func createAd() { + // 1. Create a RewardedAdUnit + rewardedAdUnit = RewardedAdUnit(configID: storedImpDisplayRewarded) + rewardedAdUnit.delegate = self + + // 2. Load the rewarded ad + rewardedAdUnit.loadAd() + } + + // MARK: - RewardedAdUnitDelegate + + func rewardedAdDidReceiveAd(_ rewardedAd: RewardedAdUnit) { + if rewardedAd.isReady { + rewardedAd.show(from: self) + } + } + + func rewardedAd(_ rewardedAd: RewardedAdUnit, didFailToReceiveAdWithError error: Error?) { + PrebidDemoLogger.shared.error("Rewarded ad unit did fail to receive ad: \(error?.localizedDescription ?? "")") + } + + func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit, reward: PrebidReward) { + PrebidDemoLogger.shared.info("User did earn reward: type - \(reward.type ?? ""), count - \(reward.count ?? 0)") + } +} diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoInterstitialLandscapeViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoInterstitialLandscapeViewController.swift index c8e22a589..15a96c647 100644 --- a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoInterstitialLandscapeViewController.swift +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoInterstitialLandscapeViewController.swift @@ -18,7 +18,7 @@ import PrebidMobile fileprivate let storedImpVideoInterstitialLandscape = "prebid-demo-video-interstitial-landscape-with-end-card" -class InAppVideoInterstitialLandscapeViewController: UIViewController { +class InAppVideoInterstitialLandscapeViewController: UIViewController, InterstitialAdUnitDelegate { // Prebid private var renderingInterstitial: InterstitialRenderingAdUnit! diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoInterstitialVerticalViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoInterstitialVerticalViewController.swift index 86914d68f..6fa888fc2 100644 --- a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoInterstitialVerticalViewController.swift +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoInterstitialVerticalViewController.swift @@ -18,7 +18,7 @@ import PrebidMobile fileprivate let storedImpVideoInterstitialVertical = "prebid-demo-video-interstitial-vertical" -class InAppVideoInterstitialVerticalViewController: UIViewController { +class InAppVideoInterstitialVerticalViewController: UIViewController, InterstitialAdUnitDelegate { // Prebid private var renderingInterstitial: InterstitialRenderingAdUnit! diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoRewardedViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoRewardedViewController.swift index 35380e902..69d5b642a 100644 --- a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoRewardedViewController.swift +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppVideoRewardedViewController.swift @@ -16,7 +16,7 @@ import UIKit import PrebidMobile -fileprivate let storedImpVideoRewarded = "prebid-demo-video-rewarded-320-480" +fileprivate let storedImpVideoRewarded = "prebid-demo-video-rewarded-endcard-time" class InAppVideoRewardedViewController: InterstitialBaseViewController, RewardedAdUnitDelegate { @@ -41,10 +41,16 @@ class InAppVideoRewardedViewController: InterstitialBaseViewController, Rewarded // MARK: - RewardedAdUnitDelegate func rewardedAdDidReceiveAd(_ rewardedAd: RewardedAdUnit) { - rewardedAdUnit.show(from: self) + if rewardedAd.isReady { + rewardedAd.show(from: self) + } } func rewardedAd(_ rewardedAd: RewardedAdUnit, didFailToReceiveAdWithError error: Error?) { PrebidDemoLogger.shared.error("Rewarded ad unit did fail to receive ad: \(error?.localizedDescription ?? "")") } + + func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit, reward: PrebidReward) { + PrebidDemoLogger.shared.info("User did earn reward: type - \(reward.type ?? ""), count - \(reward.count ?? 0)") + } } diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/MAX/MAXDisplayRewardedViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/MAX/MAXDisplayRewardedViewController.swift new file mode 100644 index 000000000..3e0c4bcae --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/MAX/MAXDisplayRewardedViewController.swift @@ -0,0 +1,85 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import PrebidMobile +import PrebidMobileMAXAdapters +import AppLovinSDK + +fileprivate let storedImpDisplayRewarded = "prebid-demo-banner-rewarded-time" +fileprivate let maxAdUnitRewardedId = "f7a08e702c6bec54" + +class MAXDisplayRewardedViewController: InterstitialBaseViewController, MARewardedAdDelegate { + + // Prebid + private var maxRewardedAdUnit: MediationRewardedAdUnit! + private var mediationDelegate: MAXMediationRewardedUtils! + + // MAX + private var maxRewarded: MARewardedAd! + + override func loadView() { + super.loadView() + + createAd() + } + + func createAd() { + // 1. Create a MARewardedAd + maxRewarded = MARewardedAd.shared(withAdUnitIdentifier: maxAdUnitRewardedId) + + // 2. Create a MAXMediationRewardedUtils + mediationDelegate = MAXMediationRewardedUtils(rewardedAd: maxRewarded) + + // 3. Create a MediationRewardedAdUnit + maxRewardedAdUnit = MediationRewardedAdUnit( + configId: storedImpDisplayRewarded, + mediationDelegate: mediationDelegate + ) + + // 4. Make a bid request to Prebid Server + maxRewardedAdUnit.fetchDemand { [weak self] result in + // 5. Load the rewarded ad + self?.maxRewarded.delegate = self + self?.maxRewarded.load() + } + } + + // MARK: - MARewardedAdDelegate + + func didLoad(_ ad: MAAd) { + if let maxRewarded = maxRewarded, maxRewarded.isReady { + maxRewarded.show() + } + } + + func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) { + PrebidDemoLogger.shared.error("\(error.message)") + } + + func didFail(toDisplay ad: MAAd, withError error: MAError) { + PrebidDemoLogger.shared.error("\(error.message)") + } + + func didDisplay(_ ad: MAAd) {} + + func didHide(_ ad: MAAd) {} + + func didClick(_ ad: MAAd) {} + + func didRewardUser(for ad: MAAd, with reward: MAReward) { + print("User did earn reward: label - \(reward.label), amount - \(reward.amount)") + } +} diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/MAX/MAXVideoRewardedViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/MAX/MAXVideoRewardedViewController.swift index a2d65c0fd..57d8e2025 100644 --- a/Example/PrebidDemo/PrebidDemoSwift/Examples/MAX/MAXVideoRewardedViewController.swift +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/MAX/MAXVideoRewardedViewController.swift @@ -18,7 +18,7 @@ import PrebidMobile import PrebidMobileMAXAdapters import AppLovinSDK -fileprivate let storedImpVideoRewarded = "prebid-demo-video-rewarded-320-480" +fileprivate let storedImpVideoRewarded = "prebid-demo-video-rewarded-endcard-time" fileprivate let maxAdUnitRewardedId = "f7a08e702c6bec54" class MAXVideoRewardedViewController: InterstitialBaseViewController, MARewardedAdDelegate { @@ -44,7 +44,10 @@ class MAXVideoRewardedViewController: InterstitialBaseViewController, MARewarded mediationDelegate = MAXMediationRewardedUtils(rewardedAd: maxRewarded) // 3. Create a MediationRewardedAdUnit - maxRewardedAdUnit = MediationRewardedAdUnit(configId: storedImpVideoRewarded, mediationDelegate: mediationDelegate) + maxRewardedAdUnit = MediationRewardedAdUnit( + configId: storedImpVideoRewarded, + mediationDelegate: mediationDelegate + ) // 4. Make a bid request to Prebid Server maxRewardedAdUnit.fetchDemand { [weak self] result in @@ -76,5 +79,7 @@ class MAXVideoRewardedViewController: InterstitialBaseViewController, MARewarded func didClick(_ ad: MAAd) {} - func didRewardUser(for ad: MAAd, with reward: MAReward) {} + func didRewardUser(for ad: MAAd, with reward: MAReward) { + print("User did earn reward: label - \(reward.label), amount - \(reward.amount)") + } } diff --git a/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/AdFormat.swift b/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/AdFormat.swift index d297edd90..aaaa1e4aa 100644 --- a/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/AdFormat.swift +++ b/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/AdFormat.swift @@ -22,6 +22,7 @@ enum AdFormat: CustomStringConvertible, CaseIterable { case nativeBanner case displayInterstitial case videoInterstitial + case displayRewarded case videoRewarded case videoInstream case native @@ -39,6 +40,8 @@ enum AdFormat: CustomStringConvertible, CaseIterable { return "Display Interstitial" case .videoInterstitial: return "Video Interstitial" + case .displayRewarded: + return "Display Rewarded" case .videoRewarded: return "Video Rewarded" case .videoInstream: diff --git a/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/IntegrationCaseManager.swift b/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/IntegrationCaseManager.swift index 078a85845..1635cec7c 100644 --- a/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/IntegrationCaseManager.swift +++ b/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/IntegrationCaseManager.swift @@ -187,6 +187,15 @@ struct IntegrationCaseManager { } ), + IntegrationCase( + title: "In-App Display Rewarded 320x480", + integrationKind: .inApp, + adFormat: .displayRewarded, + configurationClosure: { + InAppDisplayRewardedViewController() + } + ), + IntegrationCase( title: "In-App Video Rewarded 320x480", integrationKind: .inApp, @@ -241,6 +250,15 @@ struct IntegrationCaseManager { } ), + IntegrationCase( + title: "GAM Display Rewarded 320x480", + integrationKind: .gam, + adFormat: .displayRewarded, + configurationClosure: { + GAMDisplayRewardedViewController() + } + ), + IntegrationCase( title: "GAM Video Rewarded 320x480", integrationKind: .gam, @@ -295,6 +313,15 @@ struct IntegrationCaseManager { } ), + IntegrationCase( + title: "AdMob Display Rewarded 320x480", + integrationKind: .adMob, + adFormat: .displayRewarded, + configurationClosure: { + AdMobDisplayRewardedViewController() + } + ), + IntegrationCase( title: "AdMob Video Rewarded 320x480", integrationKind: .adMob, @@ -350,9 +377,18 @@ struct IntegrationCaseManager { ), IntegrationCase( - title: "MAX Rewarded 320x480", + title: "MAX Display Rewarded 320x480", integrationKind: .max, - adFormat: .videoInterstitial, + adFormat: .videoRewarded, + configurationClosure: { + MAXDisplayRewardedViewController() + } + ), + + IntegrationCase( + title: "MAX Video Rewarded 320x480", + integrationKind: .max, + adFormat: .videoRewarded, configurationClosure: { MAXVideoRewardedViewController() } diff --git a/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj b/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj index d2cf1fe9b..9b7ee2550 100644 --- a/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj +++ b/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj @@ -640,13 +640,13 @@ 349302192473F2EF004A6086 /* Prebid */ = { isa = PBXGroup; children = ( + 92E92500276B2986002B57F3 /* AdMob */, 3CC4A3E72C11F93A00B97128 /* CustomRenderer */, + 3493021A2473F2FB004A6086 /* GAM */, + 9289873D28003BFE0062BFAA /* MAX */, 3461835425A6020500783A2C /* NativeAdViewBox.swift */, AC4F253825DFF6300095C601 /* NativeAdViewBoxLinks.swift */, AC4F254A25E0112C0095C601 /* NativeAdViewBoxProtocol.swift */, - 92E92500276B2986002B57F3 /* AdMob */, - 3493021A2473F2FB004A6086 /* GAM */, - 9289873D28003BFE0062BFAA /* MAX */, 3493022024740BD8004A6086 /* OpenX */, 5397BD122936180600ABDA22 /* OriginalAPI */, ); @@ -801,20 +801,20 @@ 5B3EEDB12101D1AF00BAA0C4 /* ViewControllers */ = { isa = PBXGroup; children = ( - 5B8EB5EC226768D1003CC15C /* TableCells */, 5B3EEDBE2101D1AF00BAA0C4 /* AboutViewController.swift */, - 5B39E0CA211455B000BBB68F /* Adapters */, 5B6EB3CC211336D300317ED9 /* AdapterViewController.swift */, + ACBBDF842397E10E006D5FE6 /* CommandArgsViewController.swift */, 5BCCA3D52136D2E800DA3B5A /* ConfigurableViewController.swift */, - 5B3EEDB32101D1AF00BAA0C4 /* Configuration */, + 34BAD457239564740042DB33 /* IABConsentViewController.swift */, + ACC41AC92444E50B00B9A3A7 /* SettingsViewController.swift */, D551B9702149207500FFF51D /* TabNavigationController.swift */, 5B3EEDDB2101DB7900BAA0C4 /* TabViewController.swift */, 5B3EEDE12101EDA200BAA0C4 /* TestCasesSectionsViewController.swift */, 5B3EEDDD2101EA9200BAA0C4 /* TestCasesViewController.swift */, 34E56989237DB96900B47B01 /* UtilitiesViewController.swift */, - 34BAD457239564740042DB33 /* IABConsentViewController.swift */, - ACBBDF842397E10E006D5FE6 /* CommandArgsViewController.swift */, - ACC41AC92444E50B00B9A3A7 /* SettingsViewController.swift */, + 5B39E0CA211455B000BBB68F /* Adapters */, + 5B3EEDB32101D1AF00BAA0C4 /* Configuration */, + 5B8EB5EC226768D1003CC15C /* TableCells */, ); path = ViewControllers; sourceTree = ""; @@ -884,27 +884,27 @@ 92700C66273C029F0006E9FA /* UITests */ = { isa = PBXGroup; children = ( + 92700C74273C029F0006E9FA /* AdsLoaderUITestCase.swift */, + 92700C78273C029F0006E9FA /* BaseUITestCase.swift */, + 92700C77273C029F0006E9FA /* ConsolidatedBannerUITest.swift */, + 92700C70273C029F0006E9FA /* PrebidBannerUITest.swift */, + 92700C7C273C029F0006E9FA /* PrebidExamplesUITest.swift */, + 92700C6D273C029F0006E9FA /* PrebidInterstitialUITest.swift */, + 92700C7A273C029F0006E9FA /* PrebidMRAID3MethodsUITest.swift */, 92700C67273C029F0006E9FA /* PrebidMRAID3UITest.swift */, - 92700C68273C029F0006E9FA /* XCTestCaseExt.swift */, - 92700C69273C029F0006E9FA /* PrebidVideoOutstreamUITest.swift */, 92700C6B273C029F0006E9FA /* PrebidMRAIDFullScreenUITest.swift */, - 92700C6C273C029F0006E9FA /* XCTestCase+Throwing.m */, - 92700C6D273C029F0006E9FA /* PrebidInterstitialUITest.swift */, + 92700C72273C029F0006E9FA /* PrebidMRAIDResizeExpandableUITest.swift */, + 92700C75273C029F0006E9FA /* PrebidMRAIDResizeUITest.swift */, + 92700C7B273C029F0006E9FA /* PrebidMRAIDResizeWithErrorsUITest.swift */, 92700C6E273C029F0006E9FA /* PrebidRewardedVideoUITest.swift */, 92700C6F273C029F0006E9FA /* PrebidServerUITests.swift */, - 92700C70273C029F0006E9FA /* PrebidBannerUITest.swift */, - 92700C71273C029F0006E9FA /* XCUIElementExt.swift */, - 92700C72273C029F0006E9FA /* PrebidMRAIDResizeExpandableUITest.swift */, 92700C73273C029F0006E9FA /* PrebidVideoInterstitialUITest.swift */, - 92700C74273C029F0006E9FA /* AdsLoaderUITestCase.swift */, - 92700C75273C029F0006E9FA /* PrebidMRAIDResizeUITest.swift */, - 92700C76273C029F0006E9FA /* XCTestCase+Throwing.h */, - 92700C77273C029F0006E9FA /* ConsolidatedBannerUITest.swift */, - 92700C78273C029F0006E9FA /* BaseUITestCase.swift */, + 92700C69273C029F0006E9FA /* PrebidVideoOutstreamUITest.swift */, 92700C79273C029F0006E9FA /* RepeatedUITestCase.swift */, - 92700C7A273C029F0006E9FA /* PrebidMRAID3MethodsUITest.swift */, - 92700C7B273C029F0006E9FA /* PrebidMRAIDResizeWithErrorsUITest.swift */, - 92700C7C273C029F0006E9FA /* PrebidExamplesUITest.swift */, + 92700C76273C029F0006E9FA /* XCTestCase+Throwing.h */, + 92700C6C273C029F0006E9FA /* XCTestCase+Throwing.m */, + 92700C68273C029F0006E9FA /* XCTestCaseExt.swift */, + 92700C71273C029F0006E9FA /* XCUIElementExt.swift */, ); path = UITests; sourceTree = ""; @@ -1182,7 +1182,6 @@ }; 93FED9C21A376EFE009A6E9F = { CreatedOnToolsVersion = 6.1.1; - DevelopmentTeam = 9TN4TUCG8N; LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; @@ -2115,7 +2114,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 9TN4TUCG8N; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -2147,7 +2146,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 9TN4TUCG8N; + DEVELOPMENT_TEAM = ""; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift b/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift index 72106a742..f05b106ae 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift @@ -1954,10 +1954,10 @@ struct TestCaseManager { ]; }), - // MARK: ---- Video Rewarded (In-App) ---- + // MARK: ---- Rewarded (In-App) ---- - TestCase(title: "Video Rewarded 320x480 (In-App)", - tags: [.video, .inapp, .server], + TestCase(title: "Banner Rewarded Default 320x480 (In-App)", + tags: [.interstitial, .inapp, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -1965,15 +1965,13 @@ struct TestCaseManager { } let rewardedAdController = PrebidRewardedController(rootController: adapterVC) - - rewardedAdController.prebidConfigId = "prebid-ita-video-rewarded-320-480" + rewardedAdController.prebidConfigId = "prebid-demo-banner-rewarded-default" adapterVC.setup(adapter: rewardedAdController) - setupCustomParams(for: rewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 (In-App) [SKAdN]", - tags: [.video, .inapp, .server], + TestCase(title: "Banner Rewarded Time 320x480 (In-App)", + tags: [.interstitial, .inapp, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -1981,31 +1979,27 @@ struct TestCaseManager { } let rewardedAdController = PrebidRewardedController(rootController: adapterVC) - Targeting.shared.sourceapp = "InternalTestApp" - - rewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-320-480-skadn" - + rewardedAdController.prebidConfigId = "prebid-demo-banner-rewarded-time" adapterVC.setup(adapter: rewardedAdController) - setupCustomParams(for: rewardedAdController.prebidConfigId) }), - - TestCase(title: "Video Rewarded 320x480 (In-App) [noBids]", - tags: [.video, .inapp, .server], - exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in + + TestCase(title: "Banner Rewarded Event 320x480 (In-App)", + tags: [.interstitial, .inapp, .server], + exampleVCStoryboardID: "AdapterViewController", + configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { return } let rewardedAdController = PrebidRewardedController(rootController: adapterVC) - rewardedAdController.prebidConfigId = "prebid-demo-no-bids" + rewardedAdController.prebidConfigId = "prebid-demo-banner-rewarded-event" adapterVC.setup(adapter: rewardedAdController) - setupCustomParams(for: rewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 without End Card (In-App)", - tags: [.video, .inapp, .server], + TestCase(title: "Video Rewarded Default 320x480 (In-App)", + tags: [.interstitial, .video, .inapp, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -2013,14 +2007,13 @@ struct TestCaseManager { } let rewardedAdController = PrebidRewardedController(rootController: adapterVC) - rewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-320-480-without-end-card" + rewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-default" adapterVC.setup(adapter: rewardedAdController) - setupCustomParams(for: rewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 480x320 (In-App)", - tags: [.video, .inapp, .server], + TestCase(title: "Video Rewarded Playbackevent 320x480 (In-App)", + tags: [.interstitial, .video, .inapp, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -2028,50 +2021,129 @@ struct TestCaseManager { } let rewardedAdController = PrebidRewardedController(rootController: adapterVC) - rewardedAdController.prebidConfigId = "prebid-ita-video-rewarded-320-480" + rewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-playbackevent" adapterVC.setup(adapter: rewardedAdController) - setupCustomParams(for: rewardedAdController.prebidConfigId) }), + TestCase(title: "Video Rewarded Time 320x480 (In-App)", + tags: [.interstitial, .video, .inapp, .server], + exampleVCStoryboardID: "AdapterViewController", + configurationClosure: { vc in + guard let adapterVC = vc as? AdapterViewController else { + return + } + + let rewardedAdController = PrebidRewardedController(rootController: adapterVC) + rewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-time" + adapterVC.setup(adapter: rewardedAdController) + setupCustomParams(for: rewardedAdController.prebidConfigId) + }), - TestCase(title: "Video Rewarded With Ad Configuration Server 320x480 (In-App)", - tags: [.video, .inapp, .server], + TestCase(title: "Video Rewarded Time 320x480 (In-App) [SKAdN]", + tags: [.interstitial, .video, .inapp, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { return } + + Targeting.shared.sourceapp = "InternalTestApp" let rewardedAdController = PrebidRewardedController(rootController: adapterVC) + rewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-time-skadn" + adapterVC.setup(adapter: rewardedAdController) + setupCustomParams(for: rewardedAdController.prebidConfigId) + }), + + TestCase(title: "Video Rewarded Time With Server Ad Configuration 320x480 (In-App)", + tags: [.interstitial, .video, .inapp, .server], + exampleVCStoryboardID: "AdapterViewController", + configurationClosure: { vc in + guard let adapterVC = vc as? AdapterViewController else { + return + } - rewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-320-480-with-ad-configuration" + let rewardedAdController = PrebidRewardedController(rootController: adapterVC) + rewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-time-ad-config" adapterVC.setup(adapter: rewardedAdController) - setupCustomParams(for: rewardedAdController.prebidConfigId) }), - // MARK: ---- Video Rewarded (GAM) ---- + TestCase(title: "Video Rewarded Endcard Default 320x480 (In-App)", + tags: [.interstitial, .video, .inapp, .server], + exampleVCStoryboardID: "AdapterViewController", + configurationClosure: { vc in + guard let adapterVC = vc as? AdapterViewController else { + return + } + + let rewardedAdController = PrebidRewardedController(rootController: adapterVC) + rewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-endcard-default" + adapterVC.setup(adapter: rewardedAdController) + setupCustomParams(for: rewardedAdController.prebidConfigId) + }), - TestCase(title: "Video Rewarded 320x480 (GAM) [OK, Metadata]", - tags: [.video, .gam, .server], + TestCase(title: "Video Rewarded Endcard Event 320x480 (In-App)", + tags: [.interstitial, .video, .inapp, .server], + exampleVCStoryboardID: "AdapterViewController", + configurationClosure: { vc in + guard let adapterVC = vc as? AdapterViewController else { + return + } + + let rewardedAdController = PrebidRewardedController(rootController: adapterVC) + rewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-endcard-event" + adapterVC.setup(adapter: rewardedAdController) + setupCustomParams(for: rewardedAdController.prebidConfigId) + }), + + TestCase(title: "Video Rewarded Endcard Time 320x480 (In-App)", + tags: [.interstitial, .video, .inapp, .server], + exampleVCStoryboardID: "AdapterViewController", + configurationClosure: { vc in + guard let adapterVC = vc as? AdapterViewController else { + return + } + + let rewardedAdController = PrebidRewardedController(rootController: adapterVC) + rewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-endcard-time" + adapterVC.setup(adapter: rewardedAdController) + setupCustomParams(for: rewardedAdController.prebidConfigId) + }), + + TestCase(title: "Video Rewarded 320x480 (In-App) [noBids]", + tags: [.interstitial, .video, .inapp, .server], + exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in + guard let adapterVC = vc as? AdapterViewController else { + return + } + + let rewardedAdController = PrebidRewardedController(rootController: adapterVC) + rewardedAdController.prebidConfigId = "prebid-demo-no-bids" + adapterVC.setup(adapter: rewardedAdController) + setupCustomParams(for: rewardedAdController.prebidConfigId) + }), + + // MARK: ---- Rewarded (GAM) ---- + + TestCase(title: "Banner Rewarded Time 320x480 (GAM) [OK, Metadata]", + tags: [.interstitial, .gam, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in + guard let adapterVC = vc as? AdapterViewController else { return } let gamRewardedAdController = PrebidGAMRewardedController(rootController: adapterVC) gamRewardedAdController.gamAdUnitId = "/21808260008/prebid_oxb_rewarded_video_test" - - gamRewardedAdController.prebidConfigId = "prebid-ita-video-rewarded-320-480" - + gamRewardedAdController.prebidConfigId = "prebid-demo-banner-rewarded-time" adapterVC.setup(adapter: gamRewardedAdController) - setupCustomParams(for: gamRewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 With Ad Configuration Server (GAM) [OK, Metadata]", - tags: [.video, .gam, .server], + TestCase(title: "Video Rewarded Time 320x480 (GAM) [OK, Metadata]", + tags: [.interstitial, .video, .gam, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -2079,16 +2151,14 @@ struct TestCaseManager { } let gamRewardedAdController = PrebidGAMRewardedController(rootController: adapterVC) + gamRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-time" gamRewardedAdController.gamAdUnitId = "/21808260008/prebid_oxb_rewarded_video_test" - gamRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-320-480-with-ad-configuration" - adapterVC.setup(adapter: gamRewardedAdController) - setupCustomParams(for: gamRewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 (GAM) [OK, Random]", - tags: [.video, .gam, .server], + TestCase(title: "Video Rewarded 320x480 Time With Server Ad Configuration (GAM) [OK, Metadata]", + tags: [.interstitial, .video, .gam, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -2096,31 +2166,30 @@ struct TestCaseManager { } let gamRewardedAdController = PrebidGAMRewardedController(rootController: adapterVC) - gamRewardedAdController.prebidConfigId = "prebid-ita-video-rewarded-320-480" - gamRewardedAdController.gamAdUnitId = "/21808260008/prebid_oxb_rewarded_video_random" + gamRewardedAdController.gamAdUnitId = "/21808260008/prebid_oxb_rewarded_video_test" + gamRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-time-ad-config" adapterVC.setup(adapter: gamRewardedAdController) - setupCustomParams(for: gamRewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 (GAM) [noBids, GAM Ad]", - tags: [.video, .gam, .server], + TestCase(title: "Video Rewarded Endcard Time 320x480 (GAM) [OK, Metadata]", + tags: [.interstitial, .video, .gam, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in + guard let adapterVC = vc as? AdapterViewController else { return } let gamRewardedAdController = PrebidGAMRewardedController(rootController: adapterVC) - gamRewardedAdController.prebidConfigId = "prebid-demo-no-bids" - gamRewardedAdController.gamAdUnitId = "/21808260008/prebid_oxb_rewarded_video_static" + gamRewardedAdController.gamAdUnitId = "/21808260008/prebid_oxb_rewarded_video_test" + gamRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-endcard-time" adapterVC.setup(adapter: gamRewardedAdController) - setupCustomParams(for: gamRewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 without End Card (GAM) [OK, Metadata]", - tags: [.video, .gam, .server], + TestCase(title: "Video Rewarded 320x480 (GAM) [noBids, GAM Ad]", + tags: [.interstitial, .video, .gam, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -2128,13 +2197,11 @@ struct TestCaseManager { } let gamRewardedAdController = PrebidGAMRewardedController(rootController: adapterVC) - gamRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-320-480-without-end-card" - gamRewardedAdController.gamAdUnitId = "/21808260008/prebid_oxb_rewarded_video_test" + gamRewardedAdController.prebidConfigId = "prebid-demo-no-bids" + gamRewardedAdController.gamAdUnitId = "/21808260008/prebid_oxb_rewarded_video_static" adapterVC.setup(adapter: gamRewardedAdController) - setupCustomParams(for: gamRewardedAdController.prebidConfigId) }), - // MARK: ---- MRAID (In-App) ---- @@ -2708,7 +2775,6 @@ struct TestCaseManager { admobInterstitialController.adFormats = [.video] admobInterstitialController.adMobAdUnitId = "ca-app-pub-5922967660082475/4527792002" adapterVC.setup(adapter: admobInterstitialController) - setupCustomParams(for: admobInterstitialController.prebidConfigId) }), @@ -2734,10 +2800,10 @@ struct TestCaseManager { setupCustomParams(for: interstitialController.prebidConfigId) }), - // MARK: ---- Video Rewarded (AdMob) ---- + // MARK: ---- Rewarded (AdMob) ---- - TestCase(title: "Video Rewarded 320x480 (AdMob) [OK, OXB Adapter]", - tags: [.video, .admob, .server], + TestCase(title: "Banner Rewarded Time 320x480 (AdMob) [OK, OXB Adapter]", + tags: [.interstitial, .video, .admob, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -2745,15 +2811,14 @@ struct TestCaseManager { } let admobRewardedAdController = PrebidAdMobRewardedViewController(rootController: adapterVC) - admobRewardedAdController.adMobAdUnitId = "ca-app-pub-5922967660082475/7397370641" - admobRewardedAdController.prebidConfigId = "prebid-ita-video-rewarded-320-480" + admobRewardedAdController.adMobAdUnitId = "ca-app-pub-5922967660082475/5628505938" + admobRewardedAdController.prebidConfigId = "prebid-demo-banner-rewarded-time" adapterVC.setup(adapter: admobRewardedAdController) - setupCustomParams(for: admobRewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded With Ad Configuration 320x480 Server (AdMob) [OK, OXB Adapter]", - tags: [.video, .admob, .server], + TestCase(title: "Video Rewarded Endcard Time 320x480 (AdMob) [OK, OXB Adapter]", + tags: [.interstitial, .video, .admob, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -2761,16 +2826,14 @@ struct TestCaseManager { } let admobRewardedAdController = PrebidAdMobRewardedViewController(rootController: adapterVC) - admobRewardedAdController.adMobAdUnitId = "ca-app-pub-5922967660082475/7397370641" - admobRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-320-480-with-ad-configuration" - + admobRewardedAdController.adMobAdUnitId = "ca-app-pub-5922967660082475/5628505938" + admobRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-endcard-time" adapterVC.setup(adapter: admobRewardedAdController) - setupCustomParams(for: admobRewardedAdController.prebidConfigId) }), - - TestCase(title: "Video Rewarded 320x480 (AdMob) [noBids, AdMob Ad]", - tags: [.video, .admob, .server], + + TestCase(title: "Video Rewarded Time 320x480 (AdMob) [OK, OXB Adapter]", + tags: [.interstitial, .video, .admob, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -2778,15 +2841,14 @@ struct TestCaseManager { } let admobRewardedAdController = PrebidAdMobRewardedViewController(rootController: adapterVC) - admobRewardedAdController.prebidConfigId = "prebid-demo-no-bids" - admobRewardedAdController.adMobAdUnitId = "ca-app-pub-5922967660082475/7397370641" + admobRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-time" + admobRewardedAdController.adMobAdUnitId = "ca-app-pub-5922967660082475/5628505938" adapterVC.setup(adapter: admobRewardedAdController) - setupCustomParams(for: admobRewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 (AdMob) [OK, Random]", - tags: [.video, .admob, .server], + TestCase(title: "Video Rewarded Time With Server Ad Configuration 320x480 (AdMob) [OK, OXB Adapter]", + tags: [.interstitial, .video, .admob, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -2794,15 +2856,14 @@ struct TestCaseManager { } let admobRewardedAdController = PrebidAdMobRewardedViewController(rootController: adapterVC) - admobRewardedAdController.prebidConfigId = "prebid-ita-video-rewarded-320-480" - admobRewardedAdController.adMobAdUnitId = "ca-app-pub-5922967660082475/7397370641" + admobRewardedAdController.adMobAdUnitId = "ca-app-pub-5922967660082475/5628505938" + admobRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-time-ad-config" adapterVC.setup(adapter: admobRewardedAdController) - setupCustomParams(for: admobRewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 without End Card (AdMob) [OK, OXB Adapter]", - tags: [.video, .admob, .server], + TestCase(title: "Video Rewarded 320x480 (AdMob) [noBids, AdMob Ad]", + tags: [.interstitial, .video, .admob, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -2810,10 +2871,9 @@ struct TestCaseManager { } let admobRewardedAdController = PrebidAdMobRewardedViewController(rootController: adapterVC) - admobRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-320-480-without-end-card" - admobRewardedAdController.adMobAdUnitId = "ca-app-pub-5922967660082475/7397370641" + admobRewardedAdController.prebidConfigId = "prebid-demo-no-bids" + admobRewardedAdController.adMobAdUnitId = "ca-app-pub-5922967660082475/5628505938" adapterVC.setup(adapter: admobRewardedAdController) - setupCustomParams(for: admobRewardedAdController.prebidConfigId) }), @@ -3017,6 +3077,7 @@ struct TestCaseManager { setupCustomParams(for: gamNativeAdController.prebidConfigId) }), + TestCase(title: "Native Ad (GAM) [OK, GADNativeCustomTemplateAd]", tags: [.native, .gam, .server], exampleVCStoryboardID: "AdapterViewController", @@ -3487,10 +3548,10 @@ struct TestCaseManager { setupCustomParams(for: interstitialController.prebidConfigId) }), - // MARK: ---- Video Rewarded (MAX) ---- + // MARK: ---- Rewarded (MAX) ---- - TestCase(title: "Video Rewarded 320x480 (MAX) [OK, OXB Adapter]", - tags: [.video, .max, .server], + TestCase(title: "Banner Rewarded Time 320x480 (MAX) [OK, OXB Adapter]", + tags: [.interstitial, .max, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -3499,14 +3560,13 @@ struct TestCaseManager { let maxRewardedAdController = PrebidMAXRewardedController(rootController: adapterVC) maxRewardedAdController.maxAdUnitId = "8c68716a67d5a5af" - maxRewardedAdController.prebidConfigId = "prebid-ita-video-rewarded-320-480" + maxRewardedAdController.prebidConfigId = "prebid-demo-banner-rewarded-time" adapterVC.setup(adapter: maxRewardedAdController) - setupCustomParams(for: maxRewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 With Ad Configuration Server (MAX) [OK, OXB Adapter]", - tags: [.video, .max, .server], + TestCase(title: "Video Rewarded Endcard Time 320x480 (MAX) [OK, OXB Adapter]", + tags: [.interstitial, .video, .max, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -3515,14 +3575,13 @@ struct TestCaseManager { let maxRewardedAdController = PrebidMAXRewardedController(rootController: adapterVC) maxRewardedAdController.maxAdUnitId = "8c68716a67d5a5af" - maxRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-320-480-with-ad-configuration" + maxRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-endcard-time" adapterVC.setup(adapter: maxRewardedAdController) - setupCustomParams(for: maxRewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 (MAX) [noBids, MAX Ad]", - tags: [.video, .max, .server], + TestCase(title: "Video Rewarded 320x480 Time (MAX) [OK, OXB Adapter]", + tags: [.interstitial, .video, .max, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -3531,14 +3590,14 @@ struct TestCaseManager { let maxRewardedAdController = PrebidMAXRewardedController(rootController: adapterVC) maxRewardedAdController.maxAdUnitId = "8c68716a67d5a5af" - maxRewardedAdController.prebidConfigId = "prebid-demo-no-bids" + maxRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-time" adapterVC.setup(adapter: maxRewardedAdController) setupCustomParams(for: maxRewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 (MAX) [OK, Random]", - tags: [.video, .max, .server], + TestCase(title: "Video Rewarded 320x480 Time With Server Ad Configuration (MAX) [OK, OXB Adapter]", + tags: [.interstitial, .video, .max, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -3547,14 +3606,14 @@ struct TestCaseManager { let maxRewardedAdController = PrebidMAXRewardedController(rootController: adapterVC) maxRewardedAdController.maxAdUnitId = "8c68716a67d5a5af" - maxRewardedAdController.prebidConfigId = "prebid-ita-video-rewarded-320-480" + maxRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-time-ad-config" adapterVC.setup(adapter: maxRewardedAdController) setupCustomParams(for: maxRewardedAdController.prebidConfigId) }), - TestCase(title: "Video Rewarded 320x480 without End Card (MAX) [OK, OXB Adapter]", - tags: [.video, .max, .server], + TestCase(title: "Video Rewarded 320x480 (MAX) [noBids, MAX Ad]", + tags: [.interstitial, .video, .max, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in guard let adapterVC = vc as? AdapterViewController else { @@ -3563,7 +3622,7 @@ struct TestCaseManager { let maxRewardedAdController = PrebidMAXRewardedController(rootController: adapterVC) maxRewardedAdController.maxAdUnitId = "8c68716a67d5a5af" - maxRewardedAdController.prebidConfigId = "prebid-demo-video-rewarded-320-480-without-end-card" + maxRewardedAdController.prebidConfigId = "prebid-demo-no-bids" adapterVC.setup(adapter: maxRewardedAdController) setupCustomParams(for: maxRewardedAdController.prebidConfigId) diff --git a/InternalTestApp/PrebidMobileDemoRendering/Resources/Info.plist b/InternalTestApp/PrebidMobileDemoRendering/Resources/Info.plist index 9da4dcdb2..7e4d40308 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/Resources/Info.plist +++ b/InternalTestApp/PrebidMobileDemoRendering/Resources/Info.plist @@ -2,8 +2,6 @@ - AppLovinSdkKey - 1tLUnP4cVQqpHuHH2yMtfdESvvUhTB05NdbCoDTceDDNVnhd_T8kwIzXDN9iwbdULTboByF-TtNaiTmsoVbxZw CFBundleDevelopmentRegion en CFBundleExecutable diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/GAM/PrebidGAMRewardedController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/GAM/PrebidGAMRewardedController.swift index 5561cf56b..377b95879 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/GAM/PrebidGAMRewardedController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/GAM/PrebidGAMRewardedController.swift @@ -128,7 +128,7 @@ class PrebidGAMRewardedController: NSObject, AdaptedController, PrebidConfigurab rewardedAdDidClickAdButton.isEnabled = true } - func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit) { + func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit, reward: PrebidReward) { rewardedAdUserDidEarnRewardButton.isEnabled = true } diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/PrebidInterstitialController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/PrebidInterstitialController.swift index f6d947a00..0669b1e79 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/PrebidInterstitialController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/PrebidInterstitialController.swift @@ -70,8 +70,11 @@ class PrebidInterstitialController: NSObject, AdaptedController, PrebidConfigura Prebid.shared.storedAuctionResponse = storedAuctionResponse } - interstitialController = InterstitialRenderingAdUnit(configID: prebidConfigId, - minSizePercentage: CGSize(width: 30, height: 30)) + interstitialController = InterstitialRenderingAdUnit( + configID: prebidConfigId, + minSizePercentage: CGSize(width: 30, height: 30) + ) + interstitialController?.delegate = self // Custom video configuarion diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/PrebidRewardedController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/PrebidRewardedController.swift index 12197916b..6220b9acd 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/PrebidRewardedController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/PrebidRewardedController.swift @@ -124,8 +124,9 @@ class PrebidRewardedController: NSObject, AdaptedController, RewardedAdUnitDeleg rewardedAdDidClickAdButton.isEnabled = true } - func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit) { + func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit, reward: PrebidReward) { rewardedAdUserDidEarnRewardButton.isEnabled = true + print("Did receive reward: type - \(reward.type ?? ""), count - \(reward.count ?? 0)") } // MARK: - Private Methods diff --git a/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/AdsLoaderUITestCase.swift b/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/AdsLoaderUITestCase.swift index 97d487840..6d0d27760 100644 --- a/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/AdsLoaderUITestCase.swift +++ b/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/AdsLoaderUITestCase.swift @@ -115,17 +115,20 @@ class AdsLoaderUITestCase: RepeatedUITestCase { } } - func checkRewardedLoadResult(exampleName: String, - callbacks: (Bool) -> (String) = defaultRewardedCallbacks, - expectFailure: Bool = false, - file: StaticString = #file, - line: UInt = #line) - { - checkInterstitialLoadResult(exampleName: exampleName, - callbacks: callbacks, - expectFailure: expectFailure, - file: file, - line: line) + func checkRewardedLoadResult( + exampleName: String, + callbacks: (Bool) -> (String) = defaultRewardedCallbacks, + expectFailure: Bool = false, + file: StaticString = #file, + line: UInt = #line + ) { + checkInterstitialLoadResult( + exampleName: exampleName, + callbacks: callbacks, + expectFailure: expectFailure, + file: file, + line: line + ) } private static func defaultBannerCallbacks(ok: Bool) -> String { diff --git a/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidExamplesUITest.swift b/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidExamplesUITest.swift index 4ba3480e6..876236b5b 100644 --- a/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidExamplesUITest.swift +++ b/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidExamplesUITest.swift @@ -17,11 +17,6 @@ import UIKit class PrebidExamplesUITest: AdsLoaderUITestCase { - override func setUp() { - super.setUp() - - } - // MARK: - Banners (In-App) func testInAppBanner_Small_OK() { @@ -204,41 +199,6 @@ class PrebidExamplesUITest: AdsLoaderUITestCase { checkBannerLoadResult(exampleName: "Video Outstream (GAM) [noBids, GAM Ad]", video: true) } - // MARK: - Rewarded Video - - func testInAppRewarded_320x480() { - checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 (In-App)") - } - - func testInAppRewarded_320x480_noBids() { - checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 (In-App) [noBids]", - expectFailure: true) - } - - func testInAppRewarded_320x480_noEndCard() { - checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 without End Card (In-App)") - } - - func testInAppRewarded_480x320() { - checkRewardedLoadResult(exampleName: "Video Rewarded 480x320 (In-App)") - } - - func testGAMRewarded_Ok_Metadata() { - checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 (GAM) [OK, Metadata]") - } - - func testGAMRewarded_Ok_Random() { - checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 (GAM) [OK, Random]") - } - - func testGAMRewarded_noBids_gamAd() { - checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 (GAM) [noBids, GAM Ad]") - } - - func testGAMRewarded_noEndCard_Ok_Metadata() { - checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 without End Card (GAM) [OK, Metadata]") - } - // MARK: - MRAID func testMRAID_Resize_InApp() { @@ -275,4 +235,76 @@ class PrebidExamplesUITest: AdsLoaderUITestCase { func testInAppNativeAd_Links() { checkNativeAdLoadResult(exampleName: "Native Ad Links (In-App)", successCallback: "getNativeAd success") } + + // MARK: - Rewarded + + func testRewardedBannerDefault() { + checkRewardedLoadResult(exampleName: "Banner Rewarded Default 320x480 (In-App)") + } + + func testRewardedBannerTime() { + checkRewardedLoadResult(exampleName: "Banner Rewarded Time 320x480 (In-App)") + } + + func testRewardedBannerEvent() { + checkRewardedLoadResult(exampleName: "Banner Rewarded Event 320x480 (In-App)") + } + + func testRewardedVideoDefault() { + checkRewardedLoadResult(exampleName: "Video Rewarded Default 320x480 (In-App)") + } + + func testRewardedVideoPlaybackevent() { + checkRewardedLoadResult(exampleName: "Video Rewarded Playbackevent 320x480 (In-App)") + } + + func testRewardedVideoTime() { + checkRewardedLoadResult(exampleName: "Video Rewarded Time 320x480 (In-App)") + } + + func testRewardedVideoTimeAdConfiguration() { + checkRewardedLoadResult(exampleName: "Video Rewarded Time With Server Ad Configuration 320x480 (In-App)") + } + + func testRewardedVideoEndcardDefault() { + checkRewardedLoadResult(exampleName: "Video Rewarded Endcard Default 320x480 (In-App)") + } + + func testRewardedVideoEndcardEvent() { + checkRewardedLoadResult(exampleName: "Video Rewarded Endcard Event 320x480 (In-App)") + } + + func testRewardedVideoEndcardTime() { + checkRewardedLoadResult(exampleName: "Video Rewarded Endcard Time 320x480 (In-App)") + } + + func testInAppRewarded_noBids() { + checkRewardedLoadResult( + exampleName: "Video Rewarded 320x480 (In-App) [noBids]", + expectFailure: true + ) + } + + func testGAMRewardedBannerTime() { + checkRewardedLoadResult(exampleName: "Banner Rewarded Time 320x480 (GAM) [OK, Metadata]") + } + + func testGAMRewardedVideoTime() { + checkRewardedLoadResult(exampleName: "Video Rewarded Time 320x480 (GAM) [OK, Metadata]") + } + + func testGAMRewardedVideoTimeAdConfiguration() { + checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 Time With Server Ad Configuration (GAM) [OK, Metadata]") + } + + func testGAMRewardedVideoEndcardTime() { + checkRewardedLoadResult(exampleName: "Video Rewarded Endcard Time 320x480 (GAM) [OK, Metadata]") + } + + func testGAMRewarded_noBids_gamAd() { + checkRewardedLoadResult( + exampleName: "Video Rewarded 320x480 (GAM) [noBids, GAM Ad]", + expectFailure: true + ) + } } diff --git a/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidRewardedVideoUITest.swift b/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidRewardedVideoUITest.swift index 1c0fbec13..3d07f17de 100644 --- a/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidRewardedVideoUITest.swift +++ b/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidRewardedVideoUITest.swift @@ -20,12 +20,8 @@ class PrebidRewardedVideoUITest: RepeatedUITestCase { private let waitingTimeout = 15.0 private let videoDuration = TimeInterval(17) - let videoRewardedTitle = "Video Rewarded 320x480 without End Card (In-App)" - let videoRewardedEndCardTitle = "Video Rewarded 320x480 (In-App)" - - override func setUp() { - super.setUp() - } + let videoRewardedTitle = "Video Rewarded Time 320x480 (In-App)" + let videoRewardedEndCardTitle = "Video Rewarded Endcard Time 320x480 (In-App)" func testTapEndCardThenClose() { repeatTesting(times: 7) { @@ -33,26 +29,33 @@ class PrebidRewardedVideoUITest: RepeatedUITestCase { // Tap on End card let endCardLink = app.links.firstMatch - waitForExists(element: endCardLink, waitSeconds: 5) + waitForExists(element: endCardLink, waitSeconds: 20) endCardLink.tap() //The video should still be visible. Close it. - closeAndVerifyPostEvents(expectClick: true) + let done = app.descendants(matching: .button)["Done"] + waitForExists(element: done, waitSeconds: waitingTimeout) + done.tapFrameCenter() + + // Wait for auto close action + sleep(10) + + verifyPostEvents(expectClick: true) } } - func testNoClickthrough () { + func testNoClickthrough() { repeatTesting(times: 7) { openVideoAndWait(title: videoRewardedEndCardTitle) - - closeAndVerifyPostEvents(expectClick: false) + // Wait for auto close action + sleep(10) + verifyPostEvents(expectClick: false) } } func testAutoClose() { repeatTesting(times: 7) { openVideoAndWait(title: videoRewardedTitle) - verifyPostEvents(expectClick: false) } } @@ -92,7 +95,7 @@ class PrebidRewardedVideoUITest: RepeatedUITestCase { done.tapFrameCenter() } else { let closeBtn = app.buttons["PBMCloseButton"] - waitForExists(element: closeBtn, waitSeconds: 5) + waitForExists(element: closeBtn, waitSeconds: 10) closeBtn.tap() } diff --git a/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidServerUITests.swift b/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidServerUITests.swift index 677070eb6..88600e32b 100644 --- a/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidServerUITests.swift +++ b/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/PrebidServerUITests.swift @@ -127,23 +127,4 @@ class PrebidServerUITests: AdsLoaderUITestCase { func testGAMInterstitial_Video_VanillaPrebidOrder() { checkInterstitialLoadResult(exampleName: "Video Interstitial 320x480 (GAM) [Vanilla Prebid Order]") } - - // MARK: - Rewarded Video - - func testRewardedVideo() { - checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 (In-App)") - } - - func testInAppRewarded_noBids() { - checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 (In-App) [noBids]", - expectFailure: true) - } - - func testGAMRewarded() { - checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 (GAM) [OK, Metadata]") - } - - func testGAMRewarded_noBids_gamAd() { - checkRewardedLoadResult(exampleName: "Video Rewarded 320x480 (GAM) [noBids, GAM Ad]") - } } diff --git a/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/XCTestCaseExt.swift b/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/XCTestCaseExt.swift index dfe039c15..d1cc56f65 100644 --- a/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/XCTestCaseExt.swift +++ b/InternalTestApp/PrebidMobileDemoRenderingUITests/UITests/XCTestCaseExt.swift @@ -113,6 +113,9 @@ extension XCTestCase { // MARK: - Helper methods (navigation) fileprivate func navigateToExample(app: XCUIApplication, title: String, file: StaticString = #file, line: UInt = #line) { + app.searchFields.element.tap() + app.searchFields.element.typeText(title) + let listItem = app.tables.staticTexts[title] waitForExists(element: listItem, waitSeconds: 5, file: file, line: line) listItem.tap() @@ -211,8 +214,6 @@ extension BaseUITestCase { } func navigateToExample(_ title: String, file: StaticString = #file, line: UInt = #line) { - applyFilter(adServer: detect(from: title)) - applyFilter(tag: detect(from: title)) navigateToExample(app: app, title: title, file: file, line: line) } diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index 7e5fcc3e5..1fe7c0b11 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -78,6 +78,28 @@ 533E0FB929100076000CC24F /* MockGADMobileAds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 533E0FB829100076000CC24F /* MockGADMobileAds.swift */; }; 533FDF852A12030C0066ED5A /* MockPrebidJSLibraryManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 533FDF842A12030C0066ED5A /* MockPrebidJSLibraryManager.swift */; }; 5347473B2BFB4D3D00966658 /* UserAgentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5347473A2BFB4D3D00966658 /* UserAgentService.swift */; }; + 534C611F2CB5252C0026119A /* PBMORTBRewardedConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 534C611D2CB5252C0026119A /* PBMORTBRewardedConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 534C61202CB5252C0026119A /* PBMORTBRewardedConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C611E2CB5252C0026119A /* PBMORTBRewardedConfiguration.m */; }; + 534C61232CB526CE0026119A /* PBMORTBRewardedReward.h in Headers */ = {isa = PBXBuildFile; fileRef = 534C61212CB526CE0026119A /* PBMORTBRewardedReward.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 534C61242CB526CE0026119A /* PBMORTBRewardedReward.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C61222CB526CE0026119A /* PBMORTBRewardedReward.m */; }; + 534C61272CB5279C0026119A /* PBMORTBRewardedCompletionBanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 534C61252CB5279C0026119A /* PBMORTBRewardedCompletionBanner.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 534C61282CB5279C0026119A /* PBMORTBRewardedCompletionBanner.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C61262CB5279C0026119A /* PBMORTBRewardedCompletionBanner.m */; }; + 534C612D2CB527F80026119A /* PBMORTBRewardedCompletionVideo.h in Headers */ = {isa = PBXBuildFile; fileRef = 534C612B2CB527F80026119A /* PBMORTBRewardedCompletionVideo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 534C612E2CB527F80026119A /* PBMORTBRewardedCompletionVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C612C2CB527F80026119A /* PBMORTBRewardedCompletionVideo.m */; }; + 534C61312CB5282F0026119A /* PBMORTBRewardedCompletionVideoEndcard.h in Headers */ = {isa = PBXBuildFile; fileRef = 534C612F2CB5282F0026119A /* PBMORTBRewardedCompletionVideoEndcard.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 534C61322CB5282F0026119A /* PBMORTBRewardedCompletionVideoEndcard.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C61302CB5282F0026119A /* PBMORTBRewardedCompletionVideoEndcard.m */; }; + 534C61352CB529240026119A /* PBMORTBRewardedClose.h in Headers */ = {isa = PBXBuildFile; fileRef = 534C61332CB529240026119A /* PBMORTBRewardedClose.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 534C61362CB529240026119A /* PBMORTBRewardedClose.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C61342CB529240026119A /* PBMORTBRewardedClose.m */; }; + 534C61392CB52A4E0026119A /* PBMORTBRewardedCompletion.h in Headers */ = {isa = PBXBuildFile; fileRef = 534C61372CB52A4E0026119A /* PBMORTBRewardedCompletion.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 534C613A2CB52A4E0026119A /* PBMORTBRewardedCompletion.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C61382CB52A4E0026119A /* PBMORTBRewardedCompletion.m */; }; + 534C613C2CB536F00026119A /* RewardedConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534C613B2CB536F00026119A /* RewardedConfig.swift */; }; + 534C613F2CB5504D0026119A /* PBMCloseActionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 534C613D2CB5504D0026119A /* PBMCloseActionManager.h */; }; + 534C61402CB5504D0026119A /* PBMCloseActionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C613E2CB5504D0026119A /* PBMCloseActionManager.m */; }; + 534C61422CB55D980026119A /* BackgroundAwareTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534C61412CB55D980026119A /* BackgroundAwareTimer.swift */; }; + 534C61442CB5619B0026119A /* MockVideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534C61432CB5619B0026119A /* MockVideoView.swift */; }; + 534C61462CB562E40026119A /* CloseActionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534C61452CB562E40026119A /* CloseActionManagerTests.swift */; }; + 535145E02CCB758800D40B19 /* NSObject+PBMExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 535145DF2CCB758800D40B19 /* NSObject+PBMExtensions.m */; }; + 535145E12CCB758800D40B19 /* NSObject+PBMExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 535145DE2CCB758800D40B19 /* NSObject+PBMExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5355ACA929C454070014F16E /* VAST_with_empty_companion.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5355ACA829C454070014F16E /* VAST_with_empty_companion.xml */; }; 5355ACAB29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355ACAA29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift */; }; 536A39262A84C50F00B1CCEA /* StringExtensionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536A39252A84C50F00B1CCEA /* StringExtensionsTest.swift */; }; @@ -100,6 +122,9 @@ 53A368E92AB2E38200A03B3E /* BidInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A368E82AB2E38200A03B3E /* BidInfo.swift */; }; 53A368EB2AB2E95E00A03B3E /* NativeParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A368EA2AB2E95E00A03B3E /* NativeParameters.swift */; }; 53A368F12AB83C9D00A03B3E /* PrebidRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A368F02AB83C9D00A03B3E /* PrebidRequestTests.swift */; }; + 53A447CD2CAE9F8E008DE6C0 /* PrebidReward.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A447CC2CAE9F8E008DE6C0 /* PrebidReward.swift */; }; + 53A447CF2CAEA65A008DE6C0 /* BaseInterstitialAdUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A447CE2CAEA65A008DE6C0 /* BaseInterstitialAdUnit.swift */; }; + 53A447D12CAECA3C008DE6C0 /* BaseRewardedAdUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A447D02CAECA3C008DE6C0 /* BaseRewardedAdUnit.swift */; }; 53A6579E2A860F5800AE0B4F /* CacheExpiryDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6579D2A860F5800AE0B4F /* CacheExpiryDelegate.swift */; }; 53A657A02A860F7200AE0B4F /* CacheExpiryDelegateWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A6579F2A860F7200AE0B4F /* CacheExpiryDelegateWrapper.swift */; }; 53A657B02A8B64C200AE0B4F /* PBMORTBSDKConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 53A657AF2A8B64C200AE0B4F /* PBMORTBSDKConfiguration.m */; }; @@ -479,10 +504,8 @@ 5BC37A8C271F1D0000444D5E /* BaseInterstitialAdUnitProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC37849271F1CFF00444D5E /* BaseInterstitialAdUnitProtocol.swift */; }; 5BC37A8D271F1D0000444D5E /* InterstitialRenderingAdUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3784A271F1CFF00444D5E /* InterstitialRenderingAdUnit.swift */; }; 5BC37A8E271F1D0000444D5E /* RewardedAdUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3784B271F1CFF00444D5E /* RewardedAdUnit.swift */; }; - 5BC37A8F271F1D0000444D5E /* BaseInterstitialAdUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3784C271F1CFF00444D5E /* BaseInterstitialAdUnit.swift */; }; 5BC37A90271F1D0000444D5E /* InterstitialAdUnitDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3784D271F1CFF00444D5E /* InterstitialAdUnitDelegate.swift */; }; 5BC37A91271F1D0000444D5E /* InterstitialEventLoadingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3784E271F1CFF00444D5E /* InterstitialEventLoadingDelegate.swift */; }; - 5BC37A92271F1D0000444D5E /* RewardedEventLoadingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3784F271F1CFF00444D5E /* RewardedEventLoadingDelegate.swift */; }; 5BC37A93271F1D0000444D5E /* PBMInterstitialEventHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BC37850271F1CFF00444D5E /* PBMInterstitialEventHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5BC37A94271F1D0000444D5E /* BannerEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC37851271F1CFF00444D5E /* BannerEventHandler.swift */; }; 5BC37A95271F1D0000444D5E /* PBMPrimaryAdRequesterProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BC37852271F1CFF00444D5E /* PBMPrimaryAdRequesterProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -940,6 +963,28 @@ 533E0FB829100076000CC24F /* MockGADMobileAds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGADMobileAds.swift; sourceTree = ""; }; 533FDF842A12030C0066ED5A /* MockPrebidJSLibraryManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPrebidJSLibraryManager.swift; sourceTree = ""; }; 5347473A2BFB4D3D00966658 /* UserAgentService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentService.swift; sourceTree = ""; }; + 534C611D2CB5252C0026119A /* PBMORTBRewardedConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMORTBRewardedConfiguration.h; sourceTree = ""; }; + 534C611E2CB5252C0026119A /* PBMORTBRewardedConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PBMORTBRewardedConfiguration.m; sourceTree = ""; }; + 534C61212CB526CE0026119A /* PBMORTBRewardedReward.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMORTBRewardedReward.h; sourceTree = ""; }; + 534C61222CB526CE0026119A /* PBMORTBRewardedReward.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PBMORTBRewardedReward.m; sourceTree = ""; }; + 534C61252CB5279C0026119A /* PBMORTBRewardedCompletionBanner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMORTBRewardedCompletionBanner.h; sourceTree = ""; }; + 534C61262CB5279C0026119A /* PBMORTBRewardedCompletionBanner.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PBMORTBRewardedCompletionBanner.m; sourceTree = ""; }; + 534C612B2CB527F80026119A /* PBMORTBRewardedCompletionVideo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMORTBRewardedCompletionVideo.h; sourceTree = ""; }; + 534C612C2CB527F80026119A /* PBMORTBRewardedCompletionVideo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PBMORTBRewardedCompletionVideo.m; sourceTree = ""; }; + 534C612F2CB5282F0026119A /* PBMORTBRewardedCompletionVideoEndcard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMORTBRewardedCompletionVideoEndcard.h; sourceTree = ""; }; + 534C61302CB5282F0026119A /* PBMORTBRewardedCompletionVideoEndcard.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PBMORTBRewardedCompletionVideoEndcard.m; sourceTree = ""; }; + 534C61332CB529240026119A /* PBMORTBRewardedClose.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMORTBRewardedClose.h; sourceTree = ""; }; + 534C61342CB529240026119A /* PBMORTBRewardedClose.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PBMORTBRewardedClose.m; sourceTree = ""; }; + 534C61372CB52A4E0026119A /* PBMORTBRewardedCompletion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMORTBRewardedCompletion.h; sourceTree = ""; }; + 534C61382CB52A4E0026119A /* PBMORTBRewardedCompletion.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PBMORTBRewardedCompletion.m; sourceTree = ""; }; + 534C613B2CB536F00026119A /* RewardedConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RewardedConfig.swift; sourceTree = ""; }; + 534C613D2CB5504D0026119A /* PBMCloseActionManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMCloseActionManager.h; sourceTree = ""; }; + 534C613E2CB5504D0026119A /* PBMCloseActionManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PBMCloseActionManager.m; sourceTree = ""; }; + 534C61412CB55D980026119A /* BackgroundAwareTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundAwareTimer.swift; sourceTree = ""; }; + 534C61432CB5619B0026119A /* MockVideoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVideoView.swift; sourceTree = ""; }; + 534C61452CB562E40026119A /* CloseActionManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseActionManagerTests.swift; sourceTree = ""; }; + 535145DE2CCB758800D40B19 /* NSObject+PBMExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject+PBMExtensions.h"; sourceTree = ""; }; + 535145DF2CCB758800D40B19 /* NSObject+PBMExtensions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+PBMExtensions.m"; sourceTree = ""; }; 5355ACA829C454070014F16E /* VAST_with_empty_companion.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = VAST_with_empty_companion.xml; sourceTree = ""; }; 5355ACAA29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreativeModelCollectionMakerVASTTests.swift; sourceTree = ""; }; 536A39252A84C50F00B1CCEA /* StringExtensionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensionsTest.swift; sourceTree = ""; }; @@ -962,6 +1007,9 @@ 53A368E82AB2E38200A03B3E /* BidInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BidInfo.swift; sourceTree = ""; }; 53A368EA2AB2E95E00A03B3E /* NativeParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeParameters.swift; sourceTree = ""; }; 53A368F02AB83C9D00A03B3E /* PrebidRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidRequestTests.swift; sourceTree = ""; }; + 53A447CC2CAE9F8E008DE6C0 /* PrebidReward.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidReward.swift; sourceTree = ""; }; + 53A447CE2CAEA65A008DE6C0 /* BaseInterstitialAdUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseInterstitialAdUnit.swift; sourceTree = ""; }; + 53A447D02CAECA3C008DE6C0 /* BaseRewardedAdUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseRewardedAdUnit.swift; sourceTree = ""; }; 53A6579D2A860F5800AE0B4F /* CacheExpiryDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheExpiryDelegate.swift; sourceTree = ""; }; 53A6579F2A860F7200AE0B4F /* CacheExpiryDelegateWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheExpiryDelegateWrapper.swift; sourceTree = ""; }; 53A657AE2A8B64B300AE0B4F /* PBMORTBSDKConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMORTBSDKConfiguration.h; sourceTree = ""; }; @@ -1345,10 +1393,8 @@ 5BC37849271F1CFF00444D5E /* BaseInterstitialAdUnitProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseInterstitialAdUnitProtocol.swift; sourceTree = ""; }; 5BC3784A271F1CFF00444D5E /* InterstitialRenderingAdUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterstitialRenderingAdUnit.swift; sourceTree = ""; }; 5BC3784B271F1CFF00444D5E /* RewardedAdUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RewardedAdUnit.swift; sourceTree = ""; }; - 5BC3784C271F1CFF00444D5E /* BaseInterstitialAdUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseInterstitialAdUnit.swift; sourceTree = ""; }; 5BC3784D271F1CFF00444D5E /* InterstitialAdUnitDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterstitialAdUnitDelegate.swift; sourceTree = ""; }; 5BC3784E271F1CFF00444D5E /* InterstitialEventLoadingDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterstitialEventLoadingDelegate.swift; sourceTree = ""; }; - 5BC3784F271F1CFF00444D5E /* RewardedEventLoadingDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RewardedEventLoadingDelegate.swift; sourceTree = ""; }; 5BC37850271F1CFF00444D5E /* PBMInterstitialEventHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBMInterstitialEventHandler.h; sourceTree = ""; }; 5BC37851271F1CFF00444D5E /* BannerEventHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BannerEventHandler.swift; sourceTree = ""; }; 5BC37852271F1CFF00444D5E /* PBMPrimaryAdRequesterProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBMPrimaryAdRequesterProtocol.h; sourceTree = ""; }; @@ -1774,6 +1820,7 @@ 539D4C7129B5E23600B6B30E /* MockPBMAbstractCreative.swift */, 533FDF842A12030C0066ED5A /* MockPrebidJSLibraryManager.swift */, 53CE09022AD67ECE0018CB75 /* MockPBMBidRequester.swift */, + 534C61432CB5619B0026119A /* MockVideoView.swift */, ); path = Mocks; sourceTree = ""; @@ -1987,6 +2034,8 @@ 53C925012990FB30009E6F94 /* String+Extensions.swift */, 53D3C38B2C2BEF9B0074D99B /* NSURL+PBMExtensions.h */, 53D3C38C2C2BEF9B0074D99B /* NSURL+PBMExtensions.m */, + 535145DE2CCB758800D40B19 /* NSObject+PBMExtensions.h */, + 535145DF2CCB758800D40B19 /* NSObject+PBMExtensions.m */, ); path = ExtensionsAndWrappers; sourceTree = ""; @@ -2021,10 +2070,8 @@ 5BC37672271F1CFD00444D5E /* ORTB */ = { isa = PBXGroup; children = ( - 3CADBD222BFDDE01007B6913 /* PBMORTBRendererConfig.h */, - 3CADBD232BFDDE01007B6913 /* PBMORTBRendererConfig.m */, - 5BC3768C271F1CFD00444D5E /* PBMORTB_NotImplemented.h */, 5BC37682271F1CFD00444D5E /* PBMORTB.h */, + 5BC3768C271F1CFD00444D5E /* PBMORTB_NotImplemented.h */, 5BC37693271F1CFD00444D5E /* PBMORTBAbstract.h */, 5BC37674271F1CFD00444D5E /* PBMORTBAbstract.m */, 5BC37691271F1CFD00444D5E /* PBMORTBAbstract+Protected.h */, @@ -2032,6 +2079,8 @@ 5BC37680271F1CFD00444D5E /* PBMORTBApp.m */, 92AFE57C274FCD8200B0430C /* PBMORTBAppContent.h */, 92AFE57E274FCD9900B0430C /* PBMORTBAppContent.m */, + 799109FC28F99C7D004837B4 /* PBMORTBAppExt.h */, + 799109FA28F99C2B004837B4 /* PBMORTBAppExt.m */, 5BC37679271F1CFD00444D5E /* PBMORTBAppExtPrebid.h */, 5BC3768E271F1CFD00444D5E /* PBMORTBAppExtPrebid.m */, 5BC37673271F1CFD00444D5E /* PBMORTBBanner.h */, @@ -2074,6 +2123,8 @@ 5BC3769A271F1CFD00444D5E /* PBMORTBPublisher.m */, 5BC376A5271F1CFD00444D5E /* PBMORTBRegs.h */, 5BC3768A271F1CFD00444D5E /* PBMORTBRegs.m */, + 3CADBD222BFDDE01007B6913 /* PBMORTBRendererConfig.h */, + 3CADBD232BFDDE01007B6913 /* PBMORTBRendererConfig.m */, 5BC3767B271F1CFD00444D5E /* PBMORTBSource.h */, 5BC3769C271F1CFD00444D5E /* PBMORTBSource.m */, 5BC3768D271F1CFD00444D5E /* PBMORTBSourceExtOMID.h */, @@ -2082,8 +2133,6 @@ 5BC37678271F1CFD00444D5E /* PBMORTBUser.m */, 5BC3768B271F1CFD00444D5E /* PBMORTBVideo.h */, 5BC376A4271F1CFD00444D5E /* PBMORTBVideo.m */, - 799109FA28F99C2B004837B4 /* PBMORTBAppExt.m */, - 799109FC28F99C7D004837B4 /* PBMORTBAppExt.h */, ); path = ORTB; sourceTree = ""; @@ -2134,6 +2183,7 @@ 537B651D2833A3DA008AE9D1 /* Reachability.swift */, 537B653F2833C091008AE9D1 /* NetworkType.swift */, 5347473A2BFB4D3D00966658 /* UserAgentService.swift */, + 534C61412CB55D980026119A /* BackgroundAwareTimer.swift */, ); path = Utilities; sourceTree = ""; @@ -2191,14 +2241,14 @@ 5BC37701271F1CFE00444D5E /* AdTypes */ = { isa = PBXGroup; children = ( - 5BC37702271F1CFE00444D5E /* Video */, - 5BC37727271F1CFE00444D5E /* PBMHTMLFormatter.m */, - 5BC37728271F1CFE00444D5E /* PBMHTMLCreative.m */, - 5BC37729271F1CFE00444D5E /* PBMMRAIDCommand.h */, + 5BC3772D271F1CFE00444D5E /* AdView */, 5BC3772A271F1CFE00444D5E /* PBMHTMLCreative.h */, + 5BC37728271F1CFE00444D5E /* PBMHTMLCreative.m */, 5BC3772B271F1CFE00444D5E /* PBMHTMLFormatter.h */, + 5BC37727271F1CFE00444D5E /* PBMHTMLFormatter.m */, + 5BC37729271F1CFE00444D5E /* PBMMRAIDCommand.h */, 5BC3772C271F1CFE00444D5E /* PBMMRAIDCommand.m */, - 5BC3772D271F1CFE00444D5E /* AdView */, + 5BC37702271F1CFE00444D5E /* Video */, ); path = AdTypes; sourceTree = ""; @@ -2275,6 +2325,8 @@ 5BC3773F271F1CFE00444D5E /* PBMAdViewManagerDelegate.h */, 5BC3775D271F1CFE00444D5E /* PBMAutoRefreshManager.h */, 5BC37775271F1CFE00444D5E /* PBMAutoRefreshManager.m */, + 534C613D2CB5504D0026119A /* PBMCloseActionManager.h */, + 534C613E2CB5504D0026119A /* PBMCloseActionManager.m */, 5BC37767271F1CFE00444D5E /* PBMCreativeModel.h */, 5BC3773A271F1CFE00444D5E /* PBMCreativeModel.m */, 5BC37733271F1CFE00444D5E /* PBMCreativeModelCollectionMakerVAST.h */, @@ -2306,8 +2358,8 @@ 5BC37782271F1CFE00444D5E /* PBMWKNavigationActionCompatible.h */, 5BC3776F271F1CFE00444D5E /* PBMWKWebViewCompatible.h */, 92EE5A0C27F9D292003D7691 /* Position.swift */, - 5BC3777F271F1CFE00444D5E /* WKWebView+PBMWKWebViewCompatible.h */, 9247FEFD2808175E00ACD84A /* VideoControlsConfiguration.swift */, + 5BC3777F271F1CFE00444D5E /* WKWebView+PBMWKWebViewCompatible.h */, ); path = AdView; sourceTree = ""; @@ -2315,20 +2367,20 @@ 5BC37745271F1CFE00444D5E /* Modals */ = { isa = PBXGroup; children = ( - 5BC37746271F1CFE00444D5E /* PBMModalViewController.h */, - 5BC37747271F1CFE00444D5E /* PBMDeferredModalState.h */, - 5BC37748271F1CFE00444D5E /* PBMModalViewController+Private.h */, 5BC37749271F1CFE00444D5E /* ModalAnimator */, - 5BC37750271F1CFE00444D5E /* PBMModalManagerDelegate.h */, - 5BC37751271F1CFE00444D5E /* PBMNonModalViewController.m */, + 5BC37747271F1CFE00444D5E /* PBMDeferredModalState.h */, + 5BC37754271F1CFE00444D5E /* PBMDeferredModalState.m */, 5BC37752271F1CFE00444D5E /* PBMModalManager.h */, + 5BC37759271F1CFE00444D5E /* PBMModalManager.m */, + 5BC37750271F1CFE00444D5E /* PBMModalManagerDelegate.h */, + 5BC37758271F1CFE00444D5E /* PBMModalState.h */, 5BC37753271F1CFE00444D5E /* PBMModalState.m */, - 5BC37754271F1CFE00444D5E /* PBMDeferredModalState.m */, + 5BC37746271F1CFE00444D5E /* PBMModalViewController.h */, 5BC37755271F1CFE00444D5E /* PBMModalViewController.m */, - 5BC37756271F1CFE00444D5E /* PBMNonModalViewController.h */, + 5BC37748271F1CFE00444D5E /* PBMModalViewController+Private.h */, 5BC37757271F1CFE00444D5E /* PBMModalViewControllerDelegate.h */, - 5BC37758271F1CFE00444D5E /* PBMModalState.h */, - 5BC37759271F1CFE00444D5E /* PBMModalManager.m */, + 5BC37756271F1CFE00444D5E /* PBMNonModalViewController.h */, + 5BC37751271F1CFE00444D5E /* PBMNonModalViewController.m */, ); path = Modals; sourceTree = ""; @@ -2499,6 +2551,20 @@ 924F661727FDB79C00C8DAF7 /* PBMORTBExtPrebidPassthrough.h */, 924F661D27FDBC3100C8DAF7 /* PBMORTBExtPrebidPassthrough.m */, 5BC3781C271F1CFF00444D5E /* PBMORTBPrebid.h */, + 534C61332CB529240026119A /* PBMORTBRewardedClose.h */, + 534C61342CB529240026119A /* PBMORTBRewardedClose.m */, + 534C61372CB52A4E0026119A /* PBMORTBRewardedCompletion.h */, + 534C61382CB52A4E0026119A /* PBMORTBRewardedCompletion.m */, + 534C61252CB5279C0026119A /* PBMORTBRewardedCompletionBanner.h */, + 534C61262CB5279C0026119A /* PBMORTBRewardedCompletionBanner.m */, + 534C612B2CB527F80026119A /* PBMORTBRewardedCompletionVideo.h */, + 534C612C2CB527F80026119A /* PBMORTBRewardedCompletionVideo.m */, + 534C612F2CB5282F0026119A /* PBMORTBRewardedCompletionVideoEndcard.h */, + 534C61302CB5282F0026119A /* PBMORTBRewardedCompletionVideoEndcard.m */, + 534C611D2CB5252C0026119A /* PBMORTBRewardedConfiguration.h */, + 534C611E2CB5252C0026119A /* PBMORTBRewardedConfiguration.m */, + 534C61212CB526CE0026119A /* PBMORTBRewardedReward.h */, + 534C61222CB526CE0026119A /* PBMORTBRewardedReward.m */, 53A657AE2A8B64B300AE0B4F /* PBMORTBSDKConfiguration.h */, 53A657AF2A8B64C200AE0B4F /* PBMORTBSDKConfiguration.m */, 92664564272AB5BB0064F7BD /* PBMORTBSkadnFidelity.h */, @@ -2534,26 +2600,28 @@ 5BC37841271F1CFF00444D5E /* GAM */ = { isa = PBXGroup; children = ( + 5BC37855271F1CFF00444D5E /* AdLoading */, + 5BC37851271F1CFF00444D5E /* BannerEventHandler.swift */, + 5BC37848271F1CFF00444D5E /* BannerEventInteractionDelegate.swift */, 5BC37842271F1CFF00444D5E /* BannerEventLoadingDelegate.swift */, 5BC37843271F1CFF00444D5E /* BannerView.swift */, - 5BC37844271F1CFF00444D5E /* InterstitialEventInteractionDelegate.swift */, - 5BC37845271F1CFF00444D5E /* RewardedAdUnitDelegate.swift */, - 5BC37846271F1CFF00444D5E /* RewardedEventInteractionDelegate.swift */, 5BC37847271F1CFF00444D5E /* BannerViewDelegate.swift */, - 5BC37848271F1CFF00444D5E /* BannerEventInteractionDelegate.swift */, + 53A447CE2CAEA65A008DE6C0 /* BaseInterstitialAdUnit.swift */, 5BC37849271F1CFF00444D5E /* BaseInterstitialAdUnitProtocol.swift */, - 5BC3784A271F1CFF00444D5E /* InterstitialRenderingAdUnit.swift */, - 5BC3784B271F1CFF00444D5E /* RewardedAdUnit.swift */, - 5BC3784C271F1CFF00444D5E /* BaseInterstitialAdUnit.swift */, + 53A447D02CAECA3C008DE6C0 /* BaseRewardedAdUnit.swift */, 5BC3784D271F1CFF00444D5E /* InterstitialAdUnitDelegate.swift */, + 5BC37854271F1CFF00444D5E /* InterstitialEventHandlerProtocol.swift */, + 5BC37844271F1CFF00444D5E /* InterstitialEventInteractionDelegate.swift */, 5BC3784E271F1CFF00444D5E /* InterstitialEventLoadingDelegate.swift */, - 5BC3784F271F1CFF00444D5E /* RewardedEventLoadingDelegate.swift */, + 5BC3784A271F1CFF00444D5E /* InterstitialRenderingAdUnit.swift */, 5BC37850271F1CFF00444D5E /* PBMInterstitialEventHandler.h */, - 5BC37851271F1CFF00444D5E /* BannerEventHandler.swift */, 5BC37852271F1CFF00444D5E /* PBMPrimaryAdRequesterProtocol.h */, + 53A447CC2CAE9F8E008DE6C0 /* PrebidReward.swift */, + 5BC3784B271F1CFF00444D5E /* RewardedAdUnit.swift */, + 5BC37845271F1CFF00444D5E /* RewardedAdUnitDelegate.swift */, 5BC37853271F1CFF00444D5E /* RewardedEventHandlerProtocol.swift */, - 5BC37854271F1CFF00444D5E /* InterstitialEventHandlerProtocol.swift */, - 5BC37855271F1CFF00444D5E /* AdLoading */, + 5BC37846271F1CFF00444D5E /* RewardedEventInteractionDelegate.swift */, + 534C613B2CB536F00026119A /* RewardedConfig.swift */, ); path = GAM; sourceTree = ""; @@ -2561,19 +2629,19 @@ 5BC37855271F1CFF00444D5E /* AdLoading */ = { isa = PBXGroup; children = ( - 5BC37856271F1CFF00444D5E /* PBMBannerAdLoader.m */, - 5BC37857271F1CFF00444D5E /* PBMInterstitialAdLoader.m */, + 5BC37861271F1CFF00444D5E /* AdLoadFlowControllerDelegate.swift */, 5BC37858271F1CFF00444D5E /* BannerAdLoaderDelegate.swift */, - 5BC37859271F1CFF00444D5E /* PBMAdLoadFlowController+PrivateState.h */, + 5BC3785E271F1CFF00444D5E /* PBMAdLoaderFlowDelegate.h */, 5BC3785A271F1CFF00444D5E /* PBMAdLoaderProtocol.h */, 5BC3785B271F1CFF00444D5E /* PBMAdLoadFlowController.h */, + 5BC37862271F1CFF00444D5E /* PBMAdLoadFlowController.m */, + 5BC37859271F1CFF00444D5E /* PBMAdLoadFlowController+PrivateState.h */, 5BC3785C271F1CFF00444D5E /* PBMAdLoadFlowState.h */, - 5BC3785D271F1CFF00444D5E /* PBMInterstitialAdLoaderDelegate.h */, - 5BC3785E271F1CFF00444D5E /* PBMAdLoaderFlowDelegate.h */, - 5BC3785F271F1CFF00444D5E /* PBMInterstitialAdLoader.h */, 5BC37860271F1CFF00444D5E /* PBMBannerAdLoader.h */, - 5BC37861271F1CFF00444D5E /* AdLoadFlowControllerDelegate.swift */, - 5BC37862271F1CFF00444D5E /* PBMAdLoadFlowController.m */, + 5BC37856271F1CFF00444D5E /* PBMBannerAdLoader.m */, + 5BC3785F271F1CFF00444D5E /* PBMInterstitialAdLoader.h */, + 5BC37857271F1CFF00444D5E /* PBMInterstitialAdLoader.m */, + 5BC3785D271F1CFF00444D5E /* PBMInterstitialAdLoaderDelegate.h */, ); path = AdLoading; sourceTree = ""; @@ -2877,6 +2945,7 @@ 539D4C6F29B5DDFD00B6B30E /* SafariOpenerTests.swift */, 53B221D22A0E3DA900C91CCB /* PrebidJSLibraryManagerTests.swift */, 53138B2D2A713A7800B18B5C /* PrebidGAMVersionCheckerTest.swift */, + 534C61452CB562E40026119A /* CloseActionManagerTests.swift */, ); path = Tests; sourceTree = ""; @@ -3394,10 +3463,12 @@ 5BC3796F271F1D0000444D5E /* PBMVastCreativeCompanionAdsCompanion.h in Headers */, 5BC378CA271F1CFF00444D5E /* PBMWeakTimerTargetBox.h in Headers */, 5BC37AC0271F1D0100444D5E /* PBMURLComponents.h in Headers */, + 535145E12CCB758800D40B19 /* NSObject+PBMExtensions.h in Headers */, 5BC37A08271F1D0000444D5E /* PBMExternalURLOpenCallbacks.h in Headers */, 5BC37ACC271F1D0100444D5E /* PBMORTBParameterBuilder.h in Headers */, 5BC37A70271F1D0000444D5E /* PBMORTBAbstractResponse+Protected.h in Headers */, 5BC379AA271F1D0000444D5E /* PBMModalViewControllerDelegate.h in Headers */, + 534C61272CB5279C0026119A /* PBMORTBRewardedCompletionBanner.h in Headers */, 5BC3791F271F1CFF00444D5E /* PBMDeviceAccessManagerKeys.h in Headers */, 5BC378D2271F1CFF00444D5E /* PBMORTBBanner.h in Headers */, 5BC37987271F1D0000444D5E /* PBMCreativeModelMakerResult.h in Headers */, @@ -3416,6 +3487,7 @@ 5BC3790F271F1CFF00444D5E /* PBMDeviceAccessManager.h in Headers */, 53A657B12A8B650900AE0B4F /* PBMORTBSDKConfiguration.h in Headers */, 5BC379CA271F1D0000444D5E /* PBMTransaction.h in Headers */, + 534C613F2CB5504D0026119A /* PBMCloseActionManager.h in Headers */, 5BC379F5271F1D0000444D5E /* PBMTrackingURLVisitors.h in Headers */, 5BC378E3271F1CFF00444D5E /* PBMORTBImp.h in Headers */, 5BC37A18271F1D0000444D5E /* PBMViewControllerProvider.h in Headers */, @@ -3456,6 +3528,7 @@ 5BC379B8271F1D0000444D5E /* PBMAdViewManager.h in Headers */, 5BC379B3271F1D0000444D5E /* PBMVideoViewDelegate.h in Headers */, 5BC378F2271F1CFF00444D5E /* PBMORTBAbstract.h in Headers */, + 534C611F2CB5252C0026119A /* PBMORTBRewardedConfiguration.h in Headers */, 5BC37AA0271F1D0000444D5E /* PBMAdLoaderFlowDelegate.h in Headers */, 5BC3794F271F1D0000444D5E /* PBMErrorType.h in Headers */, 5BC37928271F1CFF00444D5E /* PBMDownloadDataHelper.h in Headers */, @@ -3466,6 +3539,7 @@ 5BC379D8271F1D0000444D5E /* PBMDisplayView+InternalState.h in Headers */, 53BDBF8A293F605B004B6DE8 /* InternalUserConsentDataManager.h in Headers */, 5BC37A6B271F1D0000444D5E /* PBMORTBNoBidReason.h in Headers */, + 534C61392CB52A4E0026119A /* PBMORTBRewardedCompletion.h in Headers */, 5BC3799A271F1D0000444D5E /* PBMModalViewController.h in Headers */, 5BC37A9C271F1D0000444D5E /* PBMAdLoaderProtocol.h in Headers */, 5BC37AA1271F1D0000444D5E /* PBMInterstitialAdLoader.h in Headers */, @@ -3482,6 +3556,7 @@ 5BC37A9E271F1D0000444D5E /* PBMAdLoadFlowState.h in Headers */, 5BC379BD271F1D0000444D5E /* PBMAdRefreshOptions.h in Headers */, 5BC378F8271F1CFF00444D5E /* PBMORTBPmp.h in Headers */, + 534C612D2CB527F80026119A /* PBMORTBRewardedCompletionVideo.h in Headers */, 92664565272AB5BB0064F7BD /* PBMORTBSkadnFidelity.h in Headers */, 53F096CA27E917D40058C94C /* Log+Extensions.h in Headers */, 5BC3797F271F1D0000444D5E /* PBMMRAIDCommand.h in Headers */, @@ -3493,6 +3568,7 @@ 5BC37A5B271F1D0000444D5E /* PBMORTBAbstractResponse.h in Headers */, 5BC37909271F1CFF00444D5E /* PBMDeepLinkPlusHelper+Testing.h in Headers */, 5BC3799B271F1D0000444D5E /* PBMDeferredModalState.h in Headers */, + 534C61352CB529240026119A /* PBMORTBRewardedClose.h in Headers */, 5BC37912271F1CFF00444D5E /* PBMConstants.h in Headers */, 5BC37A64271F1D0000444D5E /* PBMORTBBidResponseExt.h in Headers */, 5BC3794D271F1D0000444D5E /* Prebid+TestExtension.h in Headers */, @@ -3507,6 +3583,7 @@ 5BC3795A271F1D0000444D5E /* PBMVastResourceContainerProtocol.h in Headers */, 5BC37925271F1CFF00444D5E /* PBMNSThreadProtocol.h in Headers */, 5BC37ABC271F1D0000444D5E /* PBMOpenMeasurementSession.h in Headers */, + 534C61312CB5282F0026119A /* PBMORTBRewardedCompletionVideoEndcard.h in Headers */, 5BC379A1271F1D0000444D5E /* PBMModalPresentationController.h in Headers */, 5BC378B5271F1CFF00444D5E /* PBMViewExposureChecker.h in Headers */, 5BC37A52271F1D0000444D5E /* PBMAdMarkupStringHandler.h in Headers */, @@ -3571,6 +3648,7 @@ 5BC3799D271F1D0000444D5E /* PBMTouchForwardingView.h in Headers */, 5BC37AD4271F1D0100444D5E /* PBMParameterBuilderProtocol.h in Headers */, 5BC37A19271F1D0000444D5E /* PBMBidRequesterFactoryBlock.h in Headers */, + 534C61232CB526CE0026119A /* PBMORTBRewardedReward.h in Headers */, 5BC379B0271F1D0000444D5E /* PBMAutoRefreshManager.h in Headers */, 5BC37AD3271F1D0100444D5E /* PBMBasicParameterBuilder.h in Headers */, 5BC3798A271F1D0000444D5E /* PBMInterstitialDisplayProperties.h in Headers */, @@ -3807,6 +3885,7 @@ 925D5E662737F47500A8A2B5 /* PBMVastLoaderCheckForAds.swift in Sources */, 929638BD27AB20FB00D30F3D /* NativeDataTests.swift in Sources */, 925D5D762737B85400A8A2B5 /* PBMVastCreativeCompanionAdsTest.swift in Sources */, + 534C61462CB562E40026119A /* CloseActionManagerTests.swift in Sources */, 925D5E222737EA1E00A8A2B5 /* MediationBannerAdUnitTest.swift in Sources */, 925D5E6C2737F4DF00A8A2B5 /* PBMVastLoaderTestOMVerificationOneInline.swift in Sources */, 925D5D912737C0A300A8A2B5 /* PBMUIViewTests.swift in Sources */, @@ -3857,6 +3936,7 @@ 606FAC4A2201F248008EAE5E /* AdUnitTests.swift in Sources */, 922AFD712737335C00732C53 /* PBMRewardedVideoCreativeTestCloseDelay.swift in Sources */, FAEBF2A2237ECFEF006BA972 /* StorageUtilsTests.swift in Sources */, + 534C61442CB5619B0026119A /* MockVideoView.swift in Sources */, 922AFD65273730FB00732C53 /* PBMMRAIDControllerTest_Base.swift in Sources */, 922AFD1A27371BCF00732C53 /* PBMCircularProgressBarLayerTests.swift in Sources */, 925D5DAB2737C4F600A8A2B5 /* PBMAdLoadFlowControllerTest.swift in Sources */, @@ -4058,6 +4138,7 @@ 9236A64E272FEAFE00ABBEF4 /* ImpressionTasksExecutor.swift in Sources */, FAEE4D26262DC2B200AD9966 /* ExternalUserId.swift in Sources */, 5BC379A0271F1D0000444D5E /* PBMTouchForwardingView.m in Sources */, + 534C61202CB5252C0026119A /* PBMORTBRewardedConfiguration.m in Sources */, 5BC378D4271F1CFF00444D5E /* PBMORTBDeviceExtAtts.m in Sources */, 92C85D6E27A983150080BAC5 /* NativeAdMarkup.swift in Sources */, 53138B2C2A7132CD00B18B5C /* PrebidGAMVersionChecker.swift in Sources */, @@ -4073,6 +4154,8 @@ 537B651E2833A3DA008AE9D1 /* Reachability.swift in Sources */, 5BC37A5D271F1D0000444D5E /* PBMORTBBidExtPrebidCache.m in Sources */, 5BC379DB271F1D0000444D5E /* InterstitialControllerLoadingDelegate.swift in Sources */, + 534C61242CB526CE0026119A /* PBMORTBRewardedReward.m in Sources */, + 534C61322CB5282F0026119A /* PBMORTBRewardedCompletionVideoEndcard.m in Sources */, 5BC3794A271F1D0000444D5E /* PBMAdDetails.m in Sources */, 92C85D7027A987260080BAC5 /* NativeEventTrackerResponse.swift in Sources */, 5BC379C7271F1D0000444D5E /* PBMVideoView.m in Sources */, @@ -4098,14 +4181,18 @@ 5BC3796D271F1D0000444D5E /* PBMVastCreativeNonLinearAds.m in Sources */, 5BC379C9271F1D0000444D5E /* PBMAdRequestResponseVAST.m in Sources */, 5BC378F9271F1CFF00444D5E /* PBMORTBPublisher.m in Sources */, + 534C61362CB529240026119A /* PBMORTBRewardedClose.m in Sources */, 5BC379D6271F1D0000444D5E /* PBMWebView.m in Sources */, + 535145E02CCB758800D40B19 /* NSObject+PBMExtensions.m in Sources */, 92AFE57F274FCD9900B0430C /* PBMORTBAppContent.m in Sources */, 92664569272AB9120064F7BD /* SkadnEventTracker.swift in Sources */, 5BC37A62271F1D0000444D5E /* PBMORTBBidExtPrebidCacheBids.m in Sources */, 929865392806CCC4007A2F34 /* UIView+Extensions.swift in Sources */, 5BC37997271F1D0000444D5E /* PBMAbstractCreative.m in Sources */, + 53A447D12CAECA3C008DE6C0 /* BaseRewardedAdUnit.swift in Sources */, 92EE5A0D27F9D292003D7691 /* Position.swift in Sources */, 92C3A83627F989E200DC05E9 /* AutoRefreshCountConfig.swift in Sources */, + 534C61402CB5504D0026119A /* PBMCloseActionManager.m in Sources */, 5BC37A72271F1D0000444D5E /* BidResponse.swift in Sources */, 5BC379C8271F1D0000444D5E /* PBMAutoRefreshManager.m in Sources */, 5BC37950271F1D0000444D5E /* PBMCreativeFactory.m in Sources */, @@ -4127,12 +4214,13 @@ 5BC378AC271F1CFF00444D5E /* Gender.swift in Sources */, 5BC37966271F1D0000444D5E /* PBMVastCreativeCompanionAds.m in Sources */, 5BC379D3271F1D0000444D5E /* PBMMRAIDJavascriptCommands.m in Sources */, + 534C613C2CB536F00026119A /* RewardedConfig.swift in Sources */, 5BC378B7271F1CFF00444D5E /* UIView+PBMViewExposure.m in Sources */, 53322AA8282E29910049229D /* EventManager.swift in Sources */, 531CF21927E8FC1B005E5ABE /* LogLevel.swift in Sources */, FAEE4D1E262DC2B200AD9966 /* VideoInterstitialAdUnit.swift in Sources */, 5BC378FF271F1CFF00444D5E /* PBMORTBImpExtPrebid.m in Sources */, - 5BC37A8F271F1D0000444D5E /* BaseInterstitialAdUnit.swift in Sources */, + 534C612E2CB527F80026119A /* PBMORTBRewardedCompletionVideo.m in Sources */, 5BC37A14271F1D0000444D5E /* PBMError_Extension.swift in Sources */, 34C9CD602850CE6300FB5451 /* OMSDKVersionProvider.m in Sources */, 5BC378B3271F1CFF00444D5E /* NSException+PBMExtensions.m in Sources */, @@ -4141,6 +4229,7 @@ 92028EA627F0D89E00783470 /* NativeAdConfiguration.swift in Sources */, 5BC379C5271F1D0000444D5E /* PBMAdRequesterVAST.m in Sources */, 5BC378E0271F1CFF00444D5E /* PBMORTBFormat.m in Sources */, + 53A447CF2CAEA65A008DE6C0 /* BaseInterstitialAdUnit.swift in Sources */, 5BC37953271F1D0000444D5E /* PBMCreativeFactoryJob.m in Sources */, 5BC378B8271F1CFF00444D5E /* PBMViewExposureChecker.m in Sources */, 5BC37978271F1D0000444D5E /* PBMVastCreativeNonLinearAdsNonLinear.m in Sources */, @@ -4152,7 +4241,6 @@ 5BC37969271F1D0000444D5E /* PBMVastIcon.m in Sources */, FAEE4D12262DC2B200AD9966 /* RewardedVideoAdUnit.swift in Sources */, 5BC3795B271F1D0000444D5E /* PBMVastCreativeCompanionAdsCompanion.m in Sources */, - 5BC37A92271F1D0000444D5E /* RewardedEventLoadingDelegate.swift in Sources */, 53A657B02A8B64C200AE0B4F /* PBMORTBSDKConfiguration.m in Sources */, 92C85D5C27A96C680080BAC5 /* NativeImage.swift in Sources */, 5BC37A86271F1D0000444D5E /* BannerView.swift in Sources */, @@ -4180,8 +4268,10 @@ 53C925052990FBFB009E6F94 /* PrebidServerStatusRequester.swift in Sources */, 5BC378DE271F1CFF00444D5E /* PBMORTBDeviceExtPrebidInterstitial.m in Sources */, 5BC37A5F271F1D0000444D5E /* PBMORTBBidExtSkadn.m in Sources */, + 534C61282CB5279C0026119A /* PBMORTBRewardedCompletionBanner.m in Sources */, 5BC3793A271F1D0000444D5E /* PBMLocationManager.m in Sources */, 5BC37AB5271F1D0000444D5E /* PBMOpenMeasurementSession.m in Sources */, + 534C613A2CB52A4E0026119A /* PBMORTBRewardedCompletion.m in Sources */, 9298964B275FD3C300D3C174 /* JSONConvertible.swift in Sources */, 53A368E42AB2E0AA00A03B3E /* PrebidAdUnit.swift in Sources */, 5BC37A58271F1D0000444D5E /* PBMORTBNoBidReason.m in Sources */, @@ -4253,6 +4343,7 @@ 5BC37A91271F1D0000444D5E /* InterstitialEventLoadingDelegate.swift in Sources */, 530382F2283197E100B2B111 /* PrebidServerResponse.swift in Sources */, 53D3C3882C2BEE1E0074D99B /* URL+Extensions.swift in Sources */, + 53A447CD2CAE9F8E008DE6C0 /* PrebidReward.swift in Sources */, 5BC3792F271F1D0000444D5E /* PBMDeviceAccessManager.m in Sources */, 5BC37A6E271F1D0000444D5E /* PBMORTBBidResponse.m in Sources */, 5BC37ACE271F1D0100444D5E /* PBMGeoLocationParameterBuilder.m in Sources */, @@ -4271,6 +4362,7 @@ 5BC37913271F1CFF00444D5E /* PBMDownloadDataHelper.m in Sources */, 5BC378BE271F1CFF00444D5E /* NSMutableDictionary+PBMExtensions.m in Sources */, FAEE4D17262DC2B200AD9966 /* NativeRequest.swift in Sources */, + 534C61422CB55D980026119A /* BackgroundAwareTimer.swift in Sources */, 5BC37ACA271F1D0100444D5E /* PBMNetworkParameterBuilder.m in Sources */, 5BC378F1271F1CFF00444D5E /* PBMORTBDeviceExtPrebid.m in Sources */, 5BC37AA6271F1D0000444D5E /* MediationBidInfoWrapper.swift in Sources */, diff --git a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift index 97c9c1b0b..cddc6f131 100644 --- a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift +++ b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift @@ -125,7 +125,7 @@ public class PrebidAdUnit: NSObject { } adUnit.adUnitConfig.adConfiguration.isInterstitialAd = request.isInterstitial - adUnit.adUnitConfig.adConfiguration.isOptIn = request.isRewarded + adUnit.adUnitConfig.adConfiguration.isRewarded = request.isRewarded if request.isInterstitial || request.isRewarded { adUnit.adUnitConfig.adPosition = .fullScreen diff --git a/PrebidMobile/BuildFiles/PrebidMobile.modulemap b/PrebidMobile/BuildFiles/PrebidMobile.modulemap index f6849bb33..24d74803c 100644 --- a/PrebidMobile/BuildFiles/PrebidMobile.modulemap +++ b/PrebidMobile/BuildFiles/PrebidMobile.modulemap @@ -157,6 +157,15 @@ framework module PrebidMobile { header "PBMORTBContentSegment.h" header "PBMORTBNative.h" + // Rewarded + header "PBMORTBRewardedClose.h" + header "PBMORTBRewardedCompletion.h" + header "PBMORTBRewardedCompletionBanner.h" + header "PBMORTBRewardedCompletionVideo.h" + header "PBMORTBRewardedCompletionVideoEndcard.h" + header "PBMORTBRewardedConfiguration.h" + header "PBMORTBRewardedReward.h" + header "NSDictionary+PBMExtensions.h" header "NSMutableDictionary+PBMExtensions.h" header "PBMLocationManager.h" @@ -165,4 +174,5 @@ framework module PrebidMobile { header "PBMInterstitialAdLoaderDelegate.h" header "OMSDKVersionProvider.h" + header "NSObject+PBMExtensions.h" } diff --git a/PrebidMobile/BuildFiles/PrebidMobileSwiftHeaders.h b/PrebidMobile/BuildFiles/PrebidMobileSwiftHeaders.h index 2c2b9c7a3..be5a45fcb 100644 --- a/PrebidMobile/BuildFiles/PrebidMobileSwiftHeaders.h +++ b/PrebidMobile/BuildFiles/PrebidMobileSwiftHeaders.h @@ -34,6 +34,15 @@ #import "PBMORTBExtPrebidEvents.h" #import "PBMORTBAdConfiguration.h" #import "PBMRawBidResponse.h" + +#import "PBMORTBRewardedClose.h" +#import "PBMORTBRewardedCompletion.h" +#import "PBMORTBRewardedCompletionBanner.h" +#import "PBMORTBRewardedCompletionVideo.h" +#import "PBMORTBRewardedCompletionVideoEndcard.h" +#import "PBMORTBRewardedConfiguration.h" +#import "PBMORTBRewardedReward.h" + #import "PBMLocationManager.h" #import "Log+Extensions.h" diff --git a/PrebidMobile/Dispatcher.swift b/PrebidMobile/Dispatcher.swift index f3d9711c4..80b17a1d2 100644 --- a/PrebidMobile/Dispatcher.swift +++ b/PrebidMobile/Dispatcher.swift @@ -27,7 +27,7 @@ class Dispatcher: NSObject { case stopped } - private (set) var state = State.notStarted + private(set) var state = State.notStarted var timer: Timer? diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/AdConfiguration.swift b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/AdConfiguration.swift index 5ec69b4e7..ef3b688e2 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/AdConfiguration.swift +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/AdConfiguration.swift @@ -73,9 +73,9 @@ public class AdConfiguration: AutoRefreshCountConfig { public var size = CGSize.zero /** - Sets a video interstitial ad unit as an opt-in video + Sets an ad unit as an rewarded */ - public var isOptIn = false + public var isRewarded = false /** Indicates whether the ad is built-in video e.g. 300x250. @@ -94,6 +94,9 @@ public class AdConfiguration: AutoRefreshCountConfig { */ public lazy var videoControlsConfig = VideoControlsConfiguration() + /// Server-side configuration for rewarded ads (bid.ext.rwdd) + public var rewardedConfig: RewardedConfig? + // MARK: - Impression Tracking public var pollFrequency: TimeInterval = 0.2 @@ -141,5 +144,4 @@ public class AdConfiguration: AutoRefreshCountConfig { } return [:] } - } diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Modals/PBMModalViewController.h b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Modals/PBMModalViewController.h index 1729da0b2..aed0f987f 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Modals/PBMModalViewController.h +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Modals/PBMModalViewController.h @@ -22,6 +22,7 @@ @class PBMModalManager; @class PBMOpenMeasurementSession; @class PBMInterstitialDisplayProperties; +@class PBMCloseActionManager; @interface PBMModalViewController : UIViewController diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Modals/PBMModalViewController.m b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Modals/PBMModalViewController.m index a89bd89fd..9b5cde61b 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Modals/PBMModalViewController.m +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/Modals/PBMModalViewController.m @@ -20,9 +20,9 @@ #import "PBMOpenMeasurementSession.h" #import "PBMFunctions+Private.h" #import "UIView+PBMExtensions.h" -#import "PBMMacros.h" #import "PBMModalManager.h" #import "PBMWebView.h" +#import "PBMCloseActionManager.h" #import "PrebidMobileSwiftHeaders.h" #if __has_include("PrebidMobile-Swift.h") @@ -242,7 +242,7 @@ - (void)configureCloseButton { - (void)setupCloseButtonVisibility { // Set the close button view visibilty based on th view context (i.e. normal, clickthrough browser, rewarded video) [self.closeButtonDecorator bringButtonToFront]; - if (self.modalState.adConfiguration.isOptIn) { + if (self.modalState.adConfiguration.isRewarded) { return; // Must be hidden } else if (self.displayProperties && self.displayProperties.closeDelay > 0) { @@ -260,8 +260,25 @@ - (void)setupCloseButtonVisibility { } - (void)creativeDisplayCompleted:(PBMAbstractCreative *)creative { - if (self.modalState.adConfiguration.isOptIn) { - self.closeButtonDecorator.button.hidden = NO; + if (self.modalState.adConfiguration.isRewarded) { + PBMRewardedConfig * rewardedConfig = creative.creativeModel.adConfiguration.rewardedConfig; + + NSString * ortbAction = rewardedConfig.closeAction ?: @""; + + PBMCloseAction action = [PBMCloseActionManager getActionWithDescription:ortbAction]; + + switch(action) { + case PBMCloseActionCloseButton: + self.closeButtonDecorator.button.hidden = NO; + break; + case PBMCloseActionAutoClose: + [self.modalViewControllerDelegate modalViewControllerCloseButtonTapped:self]; + break; + case PBMCloseActionUnknown: + // By default SDK should show close button + self.closeButtonDecorator.button.hidden = NO; + PBMLogWarn(@"SDK met unknown close action.") + } } } diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMAdViewManager.m b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMAdViewManager.m index 90bd7ee4f..69533b650 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMAdViewManager.m +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMAdViewManager.m @@ -209,13 +209,7 @@ - (void)creativeWasClicked:(PBMAbstractCreative *)creative { } - (void)creativeInterstitialDidClose:(PBMAbstractCreative *) creative { - if (self.adConfiguration.isOptIn) { - // In Rewarded Video, the Video remains on the screen with the last frame showing. - // Cleaning up here when the Interstial is closed.; - if (self.currentCreative.view && self.currentCreative.view.superview) { - [self.currentCreative.view removeFromSuperview]; - } - } else if (self.adConfiguration.winningBidAdFormat == AdFormat.video) { + if (self.adConfiguration.winningBidAdFormat == AdFormat.video) { self.videoInterstitialDidClose = YES; } @@ -282,6 +276,13 @@ - (void)creativeFullScreenDidFinish:(PBMAbstractCreative *)creative { [self.adViewManagerDelegate adDidClose]; } +/// NOTE: Rewarded API only +- (void)creativeDidSendRewardedEvent:(PBMAbstractCreative *)creative { + if (self.isInterstitial && self.isRewarded) { + [self.adViewManagerDelegate adDidSendRewardedEvent]; + } +} + #pragma mark - Utility Functions - (PBMTransaction *)currentTransaction { @@ -292,6 +293,10 @@ - (BOOL)isInterstitial { return self.adConfiguration.presentAsInterstitial; } +- (BOOL)isRewarded { + return self.adConfiguration.isRewarded; +} + //Do not load an ad if the current one is "opened" //Is the current creative an PBMHTMLCreative? If so, is a clickthrough browser visible/MRAID in Expanded mode? - (BOOL)isCreativeOpened { diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMAdViewManagerDelegate.h b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMAdViewManagerDelegate.h index e15262cd0..faf438aa6 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMAdViewManagerDelegate.h +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMAdViewManagerDelegate.h @@ -62,6 +62,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)videoAdWasMuted; - (void)videoAdWasUnmuted; +// Used only for rewarded API +- (void)adDidSendRewardedEvent; + @end NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCloseActionManager.h b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCloseActionManager.h new file mode 100644 index 000000000..5145a89c4 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCloseActionManager.h @@ -0,0 +1,32 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSInteger, PBMCloseAction) { + PBMCloseActionCloseButton = 0, + PBMCloseActionAutoClose, + PBMCloseActionUnknown, +}; + +@interface PBMCloseActionManager : NSObject + ++ (PBMCloseAction)getActionWithDescription:(NSString * _Nonnull)description; + +@end + +NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCloseActionManager.m b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCloseActionManager.m new file mode 100644 index 000000000..35c99c721 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCloseActionManager.m @@ -0,0 +1,30 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMCloseActionManager.h" + +@implementation PBMCloseActionManager + ++ (PBMCloseAction)getActionWithDescription:(NSString * _Nonnull)description { + if ([description isEqualToString:@"closebutton"]) { + return PBMCloseActionCloseButton; + } else if ([description isEqualToString:@"autoclose"]) { + return PBMCloseActionAutoClose; + } else { + return PBMCloseActionUnknown; + } +} + +@end diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeModel.h b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeModel.h index 98240296a..289edb18d 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeModel.h +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeModel.h @@ -55,6 +55,13 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) BOOL isCompanionAd; @property (atomic, assign) bool hasCompanionAd; +// NOTE: for rewarded ads only +@property (nonatomic, assign) BOOL userHasEarnedReward; +@property (nonatomic, assign) BOOL userPostRewardEventSent; + +@property (nonatomic, strong, nullable) NSNumber * rewardTime; +@property (nonatomic, strong, nullable) NSNumber * postRewardTime; + - (instancetype)initWithAdConfiguration:(nonnull PBMAdConfiguration *)adConfiguration; @end diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeModel.m b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeModel.m index c3ef15e68..b547de476 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeModel.m +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeModel.m @@ -35,6 +35,9 @@ -(instancetype)initWithAdConfiguration:(PBMAdConfiguration *)adConfiguration { self.trackingURLs = [NSDictionary new]; self.adConfiguration = adConfiguration; + + self.userHasEarnedReward = NO; + self.userPostRewardEventSent = NO; } return self; diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeViewDelegate.h b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeViewDelegate.h index 1f3bb6909..db8930c23 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeViewDelegate.h +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMCreativeViewDelegate.h @@ -26,6 +26,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)creativeInterstitialDidLeaveApp:(PBMAbstractCreative *)creative; - (void)creativeFullScreenDidFinish:(PBMAbstractCreative *)creative; +// Rewarded Ad Only +- (void)creativeDidSendRewardedEvent:(PBMAbstractCreative *)creative; + // MRAID Only - (void)creativeReadyToReimplant:(PBMAbstractCreative *)creative; - (void)creativeMraidDidCollapse:(PBMAbstractCreative *)creative; diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMTrackingEvent.h b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMTrackingEvent.h index 0c995ab79..6ab0f3c4b 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMTrackingEvent.h +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMTrackingEvent.h @@ -53,7 +53,9 @@ typedef NS_ENUM(NSInteger, PBMTrackingEvent) { PBMTrackingEventLoaded, - PBMTrackingEventPrebidWin + PBMTrackingEventPrebidWin, + + PBMTrackingEventUnknown }; @@ -61,6 +63,7 @@ NS_ASSUME_NONNULL_BEGIN @interface PBMTrackingEventDescription : NSObject + (NSString *)getDescription:(PBMTrackingEvent)event; ++ (PBMTrackingEvent)getEventWith:(NSString *)description; @end NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMTrackingEvent.m b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMTrackingEvent.m index 2f7027fdb..b44e6ad4e 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMTrackingEvent.m +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMTrackingEvent.m @@ -48,7 +48,76 @@ + (NSString *)getDescription:(PBMTrackingEvent)event { case PBMTrackingEventAcceptInvitation : return @"acceptInvitation"; case PBMTrackingEventLoaded : return @"loaded"; case PBMTrackingEventPrebidWin : return @"prebid_Win"; + case PBMTrackingEventUnknown : return @"unknown"; } } ++ (PBMTrackingEvent)getEventWith:(NSString *)description { + PBMTrackingEvent event; + + if ([description isEqualToString:@"creativeModelTrackingKey_Request"]) { + event = PBMTrackingEventRequest; + } else if ([description isEqualToString:@"creativeModelTrackingKey_Impression"]) { + event = PBMTrackingEventImpression; + } else if ([description isEqualToString:@"creativeModelTrackingKey_Click"]) { + event = PBMTrackingEventClick; + } else if ([description isEqualToString:@"creativeModelTrackingKey_OverlayClick"]) { + event = PBMTrackingEventOverlayClick; + } else if ([description isEqualToString:@"creativeModelTrackingKey_CompanionClick"]) { + event = PBMTrackingEventCompanionClick; + } else if ([description isEqualToString:@"creativeModelTrackingKey_Play"]) { + event = PBMTrackingEventPlay; + } else if ([description isEqualToString:@"pause"]) { + event = PBMTrackingEventPause; + } else if ([description isEqualToString:@"rewind"]) { + event = PBMTrackingEventRewind; + } else if ([description isEqualToString:@"resume"]) { + event = PBMTrackingEventResume; + } else if ([description isEqualToString:@"creativeModelTrackingKey_Skip"]) { + event = PBMTrackingEventSkip; + } else if ([description isEqualToString:@"creativeView"]) { + event = PBMTrackingEventCreativeView; + } else if ([description isEqualToString:@"start"]) { + event = PBMTrackingEventStart; + } else if ([description isEqualToString:@"firstquartile"]) { + event = PBMTrackingEventFirstQuartile; + } else if ([description isEqualToString:@"midpoint"]) { + event = PBMTrackingEventMidpoint; + } else if ([description isEqualToString:@"thirdquartile"]) { + event = PBMTrackingEventThirdQuartile; + } else if ([description isEqualToString:@"complete"]) { + event = PBMTrackingEventComplete; + } else if ([description isEqualToString:@"mute"]) { + event = PBMTrackingEventMute; + } else if ([description isEqualToString:@"unmute"]) { + event = PBMTrackingEventUnmute; + } else if ([description isEqualToString:@"fullscreen"]) { + event = PBMTrackingEventFullscreen; + } else if ([description isEqualToString:@"creativeModelTrackingKey_ExitFullscreen"]) { + event = PBMTrackingEventExitFullscreen; + } else if ([description isEqualToString:@"normal"]) { + event = PBMTrackingEventNormal; + } else if ([description isEqualToString:@"expand"]) { + event = PBMTrackingEventExpand; + } else if ([description isEqualToString:@"collapse"]) { + event = PBMTrackingEventCollapse; + } else if ([description isEqualToString:@"close"]) { + event = PBMTrackingEventCloseLinear; + } else if ([description isEqualToString:@"creativeModelTrackingKey_CloseOverlay"]) { + event = PBMTrackingEventCloseOverlay; + } else if ([description isEqualToString:@"creativeModelTrackingKey_Error"]) { + event = PBMTrackingEventError; + } else if ([description isEqualToString:@"acceptInvitation"]) { + event = PBMTrackingEventAcceptInvitation; + } else if ([description isEqualToString:@"loaded"]) { + event = PBMTrackingEventLoaded; + } else if ([description isEqualToString:@"prebid_Win"]) { + event = PBMTrackingEventPrebidWin; + } else { + event = PBMTrackingEventUnknown; + } + + return event; +} + @end diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoCreative.h b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoCreative.h index 295fb421d..6bbd9cacf 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoCreative.h +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoCreative.h @@ -17,6 +17,8 @@ #import "PBMAbstractCreative.h" #import "PBMVideoViewDelegate.h" +@class PBMRewardedConfig; + @interface PBMVideoCreative : PBMAbstractCreative @property (class, readonly) NSInteger maxSizeForPreRenderContent; diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoCreative.m b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoCreative.m index 33798fc32..b770e161b 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoCreative.m +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoCreative.m @@ -48,6 +48,7 @@ @interface PBMVideoCreative () @property (nonatomic, strong) PBMVideoView *videoView; @property (nonatomic, strong) NSData *data; +@property (nonatomic, strong) PBMRewardedConfig *rewardedConfig; @end @@ -76,6 +77,10 @@ - (instancetype)initWithCreativeModel:(PBMCreativeModel *)creativeModel self.videoView = [[PBMVideoView alloc] initWithCreative:self]; self.videoView.videoViewDelegate = self; self.view = self.videoView; + + self.rewardedConfig = self.creativeModel.adConfiguration.rewardedConfig; + self.creativeModel.rewardTime = [self calculateRewardTime]; + self.creativeModel.postRewardTime = [self calculatePostRewardTimeWith:self.creativeModel.rewardTime]; } return self; @@ -220,6 +225,42 @@ - (void)learnMoreWasClicked { }]; } +- (void)videoViewCurrentPlayingTime:(NSNumber *)currentPlayingTime { + // NOTE: Rewarded API only + // Signal to the application that the user has earned the reward after + // the certain period of time that the ad is on the screen. + if (!self.creativeModel.adConfiguration.isRewarded) { + return; + } + + // NOTE: This logic apllicable only to the creatives without endcard. + if (self.creativeModel.hasCompanionAd) { + return; + } + + // Try track reward event + if (!self.creativeModel.userHasEarnedReward && + [self.creativeModel.rewardTime doubleValue] >= 0 && + [currentPlayingTime doubleValue] >= [self.creativeModel.rewardTime doubleValue]) { + + self.creativeModel.userHasEarnedReward = YES; + [self.creativeViewDelegate creativeDidSendRewardedEvent:self]; + + // The SDK shows the Learn More button only when the reward clause is met. + self.videoView.showLearnMore = YES; + [self.videoView updateLearnMoreButton]; + } + + // Try track post-reward event + if(!self.creativeModel.userPostRewardEventSent && + [self.creativeModel.postRewardTime doubleValue] >= 0 && + [currentPlayingTime doubleValue] >= [self.creativeModel.postRewardTime doubleValue]) { + + self.creativeModel.userPostRewardEventSent = YES; + [self.modalManager creativeDisplayCompleted:self]; + } +} + - (void)videoViewWasTapped { if (self.creativeModel.adConfiguration.clickHandlerOverride != nil) { [self.eventManager trackEvent:PBMTrackingEventClick]; @@ -288,7 +329,7 @@ - (PBMInterstitialDisplayProperties *)createInterstitialPropertiesForCurrentVide // TODO: - Clarify the requirements and fix calculation logic - (NSTimeInterval)calculateCloseDelayForPubCloseDelay:(NSTimeInterval)pubCloseDelay { - if (self.creativeModel.adConfiguration.isOptIn || self.creativeModel.hasCompanionAd) { + if (self.creativeModel.hasCompanionAd) { return [self.creativeModel.displayDurationInSeconds doubleValue]; } else if (self.creativeModel.adConfiguration.videoControlsConfig.skipDelay && self.creativeModel.adConfiguration.videoControlsConfig.skipDelay <= self.creativeModel.displayDurationInSeconds.doubleValue) { return self.creativeModel.adConfiguration.videoControlsConfig.skipDelay; @@ -308,4 +349,63 @@ - (NSTimeInterval)calculateCloseDelayForPubCloseDelay:(NSTimeInterval)pubCloseDe } } +- (NSNumber *)calculateRewardTime { + if (!self.creativeModel.adConfiguration.isRewarded) { + return @-1; + } + + NSString * ortbPlaybackevent = self.rewardedConfig.videoPlaybackevent; + NSNumber * ortbVideoTime = self.rewardedConfig.videoTime; + + // If both complition criteria are missing (playbackevent and time) - use default configuration + if (!ortbPlaybackevent && !ortbVideoTime) { + ortbPlaybackevent = self.rewardedConfig.defaultVideoPlaybackEvent; + } + + CGFloat videoDuration = [self.creativeModel.displayDurationInSeconds doubleValue]; + + // If completion criteria is playback event + if (ortbPlaybackevent) { + PBMTrackingEvent event = [PBMTrackingEventDescription getEventWith:ortbPlaybackevent]; + + switch(event) { + case PBMTrackingEventStart: + return @0; + case PBMTrackingEventFirstQuartile: + return @(0.25 * videoDuration); + case PBMTrackingEventMidpoint: + return @(0.5 * videoDuration); + case PBMTrackingEventThirdQuartile: + return @(0.75 * videoDuration); + case PBMTrackingEventComplete: + return @(videoDuration); + default: + return @(videoDuration); + } + } + + // If completion criteria is time and if 0 <= completion.video.time <= videoDuration + if (ortbVideoTime && [ortbVideoTime doubleValue] >= 0 && [ortbVideoTime doubleValue] <= videoDuration) { + double rewardTime = [ortbVideoTime doubleValue]; + return [NSNumber numberWithDouble:rewardTime]; + } + + // Return video duration by default + return @(videoDuration); +} + +- (NSNumber *)calculatePostRewardTimeWith:(NSNumber *)calculatedRewardTime { + NSNumber * checkedRewardTime = (!calculatedRewardTime || [calculatedRewardTime doubleValue] < 0.0) + ? @0.0 : calculatedRewardTime; + + NSNumber * ortbPostrewardedTime = self.rewardedConfig.postRewardTime; + NSNumber * checkedPostrewardedTime = (!ortbPostrewardedTime || [ortbPostrewardedTime doubleValue] < 0.0) + ? @0.0 : ortbPostrewardedTime; + + NSNumber * postRewardTime = [NSNumber numberWithDouble: + [checkedRewardTime doubleValue] + [checkedPostrewardedTime doubleValue]]; + + return postRewardTime; +} + @end diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoView.h b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoView.h index d6426e47d..54952af5d 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoView.h +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoView.h @@ -34,6 +34,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, nullable) PBMCircularProgressBarView *progressBar; @property (nonatomic, assign, getter=isMuted) BOOL muted; +@property (nonatomic, assign) BOOL showLearnMore; // Indicates that video reached the VAST Duration // We must use this flag instead of player’s state to prevent double-stopping of the video due to async work of observers. @@ -45,6 +46,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithCreative:(PBMVideoCreative *)creative; +- (void)updateLearnMoreButton; + - (void)showMediaFileURL:(NSURL *)mediaFileURL preloadedData:(NSData *)preloadedData; - (void)startPlayback; diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoView.m b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoView.m index fb875ce08..90076ee04 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoView.m +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoView.m @@ -54,7 +54,6 @@ @interface PBMVideoView () @property (nonatomic, weak) PBMVideoCreative *creative; @property (nonatomic, strong) PBMEventManager *eventManager; @property (nonatomic, strong) PBMTouchDownRecognizer *tapdownGestureRecognizer; -@property (nonatomic, assign) BOOL showLearnMore; #pragma mark UI @@ -85,6 +84,10 @@ @interface PBMVideoView () @property (nonatomic, assign) BOOL isPlaybackStarted; @property (nonatomic, assign) BOOL isPlaybackFinished; +@property (nonatomic, strong, nonnull) NSNumber * progressBarDuration; + +- (NSNumber *)calculateProgressBarDuration; + @end #pragma mark - Implementation @@ -107,12 +110,17 @@ - (void)setAvPlayer:(AVPlayer *)avPlayer { layer.player = avPlayer; } +- (PBMAdConfiguration *)adConfiguration { + return self.creative.creativeModel.adConfiguration; +} + #pragma mark - Initialization - (instancetype)initWithEventManager:(PBMEventManager *)eventManager { self = [super init]; if (self) { [self setupWithEventManager:eventManager]; + self.progressBarDuration = [self calculateProgressBarDuration]; } return self; @@ -125,6 +133,7 @@ - (instancetype)initWithCreative:(PBMVideoCreative *)creative { if (self) { self.creative = creative; [self setupWithEventManager:creative.eventManager]; + self.progressBarDuration = [self calculateProgressBarDuration]; } return self; @@ -157,7 +166,7 @@ - (void)setupWithEventManager:(PBMEventManager *)eventManager { self.accessibilityIdentifier = @"PBMVideoView"; if (!self.creative.creativeModel.adConfiguration.isInterstitialAd || - (self.creative.creativeModel.adConfiguration.isOptIn && !self.creative.creativeModel.hasCompanionAd)) { + (!self.creative.creativeModel.adConfiguration.isRewarded)) { [self setupTapRecognizer]; } @@ -197,6 +206,7 @@ - (void)showMediaFileURL:(NSURL *)mediaFileURL preloadedData:(NSData *)preloaded } - (void)updateControls { + [self updateLearnMoreButtonVisibility]; [self updateLearnMoreButton]; [self resetMuteControls]; @@ -220,7 +230,7 @@ - (void)updateLearnMoreButtonVisibility { } // For rewarded ad learn more should be hidden - if (self.creative.creativeModel.adConfiguration.isOptIn) { + if (self.creative.creativeModel.adConfiguration.isRewarded) { self.showLearnMore = NO; return; } @@ -339,7 +349,6 @@ - (void)updateLearnMoreButton { [self.btnLearnMore removeFromSuperview]; } - [self updateLearnMoreButtonVisibility]; if (!self.showLearnMore) { return; } @@ -355,7 +364,7 @@ - (void)updateProgressBar { [self.progressBar removeFromSuperview]; } - if (!self.creative.creativeModel.adConfiguration.isOptIn) { + if (!self.creative.creativeModel.adConfiguration.isRewarded) { return; } @@ -493,7 +502,9 @@ - (void)handleSkipDelay:(NSTimeInterval)skipDelay videoDuration:(NSTimeInterval) return; } - if (!self.creative.creativeModel.hasCompanionAd || self.creative.creativeModel.adConfiguration.isOptIn || self.creative.creativeModel.adConfiguration.isBuiltInVideo) { + if (!self.creative.creativeModel.hasCompanionAd || + self.creative.creativeModel.adConfiguration.isRewarded || + self.creative.creativeModel.adConfiguration.isBuiltInVideo) { return; } @@ -616,7 +627,8 @@ - (void)startPlayback { [self.avPlayer play]; - [self handleSkipDelay:self.creative.creativeModel.adConfiguration.videoControlsConfig.skipDelay videoDuration:self.creative.creativeModel.displayDurationInSeconds.doubleValue]; + [self handleSkipDelay:self.adConfiguration.videoControlsConfig.skipDelay + videoDuration:self.creative.creativeModel.displayDurationInSeconds.doubleValue]; if (!self.isPlaybackStarted) { self.isPlaybackStarted = YES; @@ -638,8 +650,8 @@ - (void)initTimeObserver { }]; } - if (self.creative.creativeModel.adConfiguration.isOptIn) { - self.progressBar.duration = [self requiredVideoDuration]; + if (self.adConfiguration.isRewarded) { + self.progressBar.duration = [self.progressBarDuration doubleValue]; } } @@ -759,13 +771,13 @@ - (void)completeVideoViewDisplayWith:(PBMTrackingEvent)trackingEvent { [self.videoViewDelegate videoViewCompletedDisplay]; - if (self.creative.creativeModel.adConfiguration.isOptIn) { + if (self.adConfiguration.isRewarded) { self.progressBar.hidden = YES; } - if (self.creative.creativeModel.adConfiguration.isBuiltInVideo && !self.creative.creativeModel.hasCompanionAd) { + if (self.adConfiguration.isBuiltInVideo && !self.creative.creativeModel.hasCompanionAd) { // UI: need to give some time to hide the interstitial before showing the Watch Again - if (self.creative.creativeModel.adConfiguration.presentAsInterstitial) { + if (self.adConfiguration.presentAsInterstitial) { @weakify(self); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ @strongify(self); @@ -827,12 +839,22 @@ - (CGFloat)handlePeriodicTimeEvent { } CGFloat playingTime = CMTimeGetSeconds(currentTime); - CGFloat remainingTime = [self requiredVideoDuration] - playingTime; + CGFloat remainingTime = [self.progressBarDuration doubleValue] - playingTime; - if (self.creative.creativeModel.adConfiguration.isOptIn) { - [self.progressBar updateProgress:remainingTime]; + if (self.creative.creativeModel.adConfiguration.isRewarded) { + + // Update progress bar + if(remainingTime >= 0) { + [self.progressBar updateProgress:remainingTime]; + } else { + if (self.progressBar.superview) { + [self.progressBar removeFromSuperview]; + } + } } + [self.videoViewDelegate videoViewCurrentPlayingTime:[NSNumber numberWithDouble:playingTime]]; + [self stopAdIfNeeded]; return remainingTime; @@ -1002,4 +1024,69 @@ - (void)recordTapEvent:(UITapGestureRecognizer *)tap { } } +- (NSNumber *)calculateProgressBarDuration { + // Get video duration + CGFloat videoDuration = [self requiredVideoDuration]; + + // If creative is not rewarded or has companion ad - return video duration. + if (!self.creative.creativeModel.adConfiguration.isRewarded || + self.creative.creativeModel.hasCompanionAd) { + return [NSNumber numberWithDouble:videoDuration]; + } + + PBMRewardedConfig * rewardedConfig = self.creative.creativeModel.adConfiguration.rewardedConfig; + + NSString * ortbPlaybackevent = rewardedConfig.videoPlaybackevent; + NSNumber * ortbVideoTime = rewardedConfig.videoTime; + NSNumber * ortbPostrewardedTime = rewardedConfig.postRewardTime ?: 0; + + // If both completion criteria are missing (playbackevent and time) - use default configuration + if (!ortbPlaybackevent && !ortbVideoTime) { + ortbPlaybackevent = rewardedConfig.defaultVideoPlaybackEvent; + } + + double progressBarDuration = ([ortbPostrewardedTime doubleValue] >= 0) ? [ortbPostrewardedTime doubleValue] : 0.0; + + // If completion criteria is playback event + if (ortbPlaybackevent) { + PBMTrackingEvent event = [PBMTrackingEventDescription getEventWith:ortbPlaybackevent]; + + switch(event) { + case PBMTrackingEventStart: + break; + case PBMTrackingEventFirstQuartile: + progressBarDuration += 0.25 * videoDuration; + break; + case PBMTrackingEventMidpoint: + progressBarDuration += 0.5 * videoDuration; + break; + case PBMTrackingEventThirdQuartile: + progressBarDuration += 0.75 * videoDuration; + break; + case PBMTrackingEventComplete: + progressBarDuration = videoDuration; + break; + default: + break; + } + + // if calculated value is bigger than video duration => return video duration + return (progressBarDuration > videoDuration) ? [NSNumber numberWithDouble:videoDuration] : + [NSNumber numberWithDouble:progressBarDuration]; + } + + // If completion criteria is time + if (ortbVideoTime && [ortbVideoTime doubleValue] >= 0.0) { + progressBarDuration += [ortbVideoTime doubleValue]; + + // if calculated value is bigger than video duration => return video duration + return (progressBarDuration > videoDuration) ? [NSNumber numberWithDouble:videoDuration] : + [NSNumber numberWithDouble:progressBarDuration]; + } + + // Return video duration by default + return [NSNumber numberWithDouble:[self requiredVideoDuration]]; +} + + @end diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoViewDelegate.h b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoViewDelegate.h index ee67f0edd..9a621b495 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoViewDelegate.h +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMVideoViewDelegate.h @@ -25,6 +25,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)videoViewCompletedDisplay; - (void)videoViewWasTapped; +- (void)videoViewCurrentPlayingTime:(NSNumber *)currentPlayingTime; + - (void)learnMoreWasClicked; @end diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebView.h b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebView.h index b32ecb0b9..c57266ba9 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebView.h +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebView.h @@ -61,6 +61,8 @@ typedef NS_ENUM(NSInteger, PBMWebViewState) { @property (nonatomic, assign) BOOL isMRAID; @property (nonatomic, assign, getter=isRotationEnabled) BOOL rotationEnabled; +@property (nonatomic, strong, nullable) NSString *rewardedAdURL; + #pragma mark - Initialization - (instancetype)initWithFrame:(CGRect)frame; diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebView.m b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebView.m index 1f260c9f3..b5be2fd19 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebView.m +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebView.m @@ -287,7 +287,8 @@ - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfigurat #pragma mark - WKNavigationDelegate -- (void) webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction + decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { //If there's no URL, bail NSURL *url = navigationAction.request.URL; @@ -309,6 +310,21 @@ - (void) webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigat decisionHandler(WKNavigationActionPolicyCancel); return; } + + // Identify and process rewarded events + if (self.rewardedAdURL) { + if ([url.absoluteString isEqualToString:self.rewardedAdURL]) { + @weakify(self); + dispatch_async(dispatch_get_main_queue(), ^{ + @strongify(self); + if (!self) { return; } + [self.delegate webView:self receivedRewardedEventLink:url]; + }); + + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + } //If this is the first URL, allow it. if (self.state == PBMWebViewStateLoading) { diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebViewDelegate.h b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebViewDelegate.h index b7cf33d44..65ae0a72c 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebViewDelegate.h +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/PBMWebViewDelegate.h @@ -23,6 +23,7 @@ - (void)webView:(nonnull PBMWebView *)webView failedToLoadWithError:(nonnull NSError *)error; - (void)webView:(nonnull PBMWebView *)webView receivedClickthroughLink:(nonnull NSURL *)url; - (void)webView:(nonnull PBMWebView *)webView receivedMRAIDLink:(nonnull NSURL *)url; +- (void)webView:(nonnull PBMWebView *)webView receivedRewardedEventLink:(nonnull NSURL *)url; @end diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/PBMHTMLCreative.h b/PrebidMobile/PrebidMobileRendering/AdTypes/PBMHTMLCreative.h index be1fce218..af493988d 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/PBMHTMLCreative.h +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/PBMHTMLCreative.h @@ -19,6 +19,7 @@ #import "PBMCreativeFactory.h" @class PBMMRAIDResizeProperties; +@class PBMBackgroundAwareTimer; @interface PBMHTMLCreative : PBMAbstractCreative diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/PBMHTMLCreative.m b/PrebidMobile/PrebidMobileRendering/AdTypes/PBMHTMLCreative.m index afc316c40..a5cc9da7d 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/PBMHTMLCreative.m +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/PBMHTMLCreative.m @@ -63,6 +63,9 @@ @interface PBMHTMLCreative() @property (nonatomic, strong) Prebid *sdkConfiguration; @property (nonatomic, strong) PBMMRAIDController *MRAIDController; +@property (nonatomic, strong) PBMRewardedConfig *rewardedConfig; +@property (nonatomic, strong, nullable) PBMBackgroundAwareTimer *backgroundAwareTimer; + @end #pragma mark - Implementation @@ -96,6 +99,8 @@ - (nonnull instancetype)initWithCreativeModel:(PBMCreativeModel *)creativeModel if (webView) { self.prebidWebView = webView; } + + self.rewardedConfig = self.creativeModel.adConfiguration.rewardedConfig; } return self; @@ -135,6 +140,12 @@ - (void)setupView { self.prebidWebView.frame = rect; } + if (self.creativeModel.isCompanionAd) { + self.prebidWebView.rewardedAdURL = self.rewardedConfig.endcardEvent; + } else { + self.prebidWebView.rewardedAdURL = self.rewardedConfig.bannerEvent; + } + self.prebidWebView.delegate = self; self.view = self.prebidWebView; @@ -163,8 +174,11 @@ - (void)displayWithRootViewController:(UIViewController*)viewController { if (self.creativeModel.isCompanionAd == YES) { [self.eventManager trackEvent:PBMTrackingEventCreativeView]; - // FIXME: extremly ugly. It makes creative highly coupled with modal manager. Need to split responsibilities more carefully. - [self.modalManager creativeDisplayCompleted:self]; + // For rewarded we have different logic for display completion. + // See `setupRewardTimerIfNeeded` for more details + if (!self.creativeModel.adConfiguration.isRewarded) { + [self.modalManager creativeDisplayCompleted:self]; + } } [self.viewabilityTracker start]; @@ -173,9 +187,10 @@ - (void)displayWithRootViewController:(UIViewController*)viewController { - (void)onAdDisplayed { [super onAdDisplayed]; [self setupDisplayTimer]; + [self setupRewardTimerIfNeeded]; } -- (void) setupDisplayTimer { +- (void)setupDisplayTimer { //Banners display for a set amount of time and then signal creativeDidComplete. //Interstitials display for as long as the user is enjoying their presence. if (self.creativeModel.adConfiguration.presentAsInterstitial) { @@ -203,6 +218,86 @@ - (void) setupDisplayTimer { }); } +- (void)setupRewardTimerIfNeeded { + // NOTE: Rewarded API only + // Signal to the application that the user has earned the reward after + // the certain period of time that the ad is on the screen. + if (!self.creativeModel.adConfiguration.isRewarded) { + return; + } + + if (!self.rewardedConfig) { + return; + } + + NSTimeInterval rewardNotificationInterval = 0.0; + + if (self.creativeModel.isCompanionAd) { + NSNumber * videoEndcardTime = self.rewardedConfig.endcardTime; + NSNumber * defaultEndcardTime = self.rewardedConfig.defaultCompletionTime; + rewardNotificationInterval = (videoEndcardTime) ? [videoEndcardTime intValue] : [defaultEndcardTime intValue]; + } else { + NSNumber * bannerEndcardTime = self.rewardedConfig.bannerTime; + NSNumber * defaultBannerTime = self.rewardedConfig.defaultCompletionTime; + rewardNotificationInterval = (bannerEndcardTime) ? [bannerEndcardTime intValue] : [defaultBannerTime intValue]; + } + + self.backgroundAwareTimer = [PBMBackgroundAwareTimer new]; + + // Track user did earn reward + @weakify(self); + [self.backgroundAwareTimer startTimerWith:rewardNotificationInterval + completion:^{ + @strongify(self); + + if (!self) { return; } + + if (!self.creativeModel.userHasEarnedReward) { + self.creativeModel.userHasEarnedReward = YES; + [self.creativeViewDelegate creativeDidSendRewardedEvent:self]; + } + + // Track post reward event + [self setupPostRewardTimer]; + }]; +} + +- (void)setupPostRewardTimer { + // NOTE: Rewarded API only + // Signal to the SDK about the post reward event in order to execute close ad logic. + if (!self.creativeModel.adConfiguration.isRewarded) { + return; + } + + if (!self.rewardedConfig) { + return; + } + + NSTimeInterval postRewardTime = [self.rewardedConfig.postRewardTime doubleValue] ?: 0; + + if (postRewardTime < 0.0 || !self.creativeModel.userHasEarnedReward || + self.creativeModel.userPostRewardEventSent) { + return; + } + + self.backgroundAwareTimer = [PBMBackgroundAwareTimer new]; + + // Track user did earn reward + @weakify(self); + [self.backgroundAwareTimer startTimerWith:postRewardTime + completion:^{ + @strongify(self); + + if (!self) { return; } + + if (!self.creativeModel.userPostRewardEventSent) { + self.creativeModel.userPostRewardEventSent = YES; + [self.modalManager creativeDisplayCompleted:self]; + } + }]; +} + + // The session must be created only after WebView finishes loading - (void)createOpenMeasurementSession { @@ -276,6 +371,14 @@ - (void)webView:(PBMWebView *)webView receivedMRAIDLink:(NSURL *)url { } } +- (void)webView:(PBMWebView *)webView receivedRewardedEventLink:(NSURL *)url { + if (!self.creativeModel.userHasEarnedReward) { + [self.creativeViewDelegate creativeDidSendRewardedEvent:self]; + self.creativeModel.userHasEarnedReward = YES; + + [self setupPostRewardTimer]; + } +} #pragma mark - PBMModalManagerDelegate @@ -291,13 +394,6 @@ - (void)modalManagerDidFinishPop:(PBMModalState *)state { [self.creativeViewDelegate creativeClickthroughDidClose:self]; self.clickthroughVisible = NO; - //Pop to root after clickthroughs - if (self.creativeModel.adConfiguration.presentAsInterstitial) { - if (self.dismissInterstitialModalState) { - self.dismissInterstitialModalState(); - } - } - return; } diff --git a/PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/NSObject+PBMExtensions.h b/PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/NSObject+PBMExtensions.h new file mode 100644 index 000000000..61a37639d --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/NSObject+PBMExtensions.h @@ -0,0 +1,22 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +#import + +@interface NSObject (Prebid) + +- (id)nullToNil; + +@end diff --git a/PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/NSObject+PBMExtensions.m b/PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/NSObject+PBMExtensions.m new file mode 100644 index 000000000..16f78bb4e --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/NSObject+PBMExtensions.m @@ -0,0 +1,24 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +#import "NSObject+PBMExtensions.h" + +@implementation NSObject (Prebid) + +- (id)nullToNil { + return [self isKindOfClass:[NSNull class]] ? nil : self; +} + +@end diff --git a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBAbstract+Protected.h b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBAbstract+Protected.h index 59716f0f8..cb945dd86 100644 --- a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBAbstract+Protected.h +++ b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBAbstract+Protected.h @@ -19,6 +19,7 @@ #import "NSMutableDictionary+PBMExtensions.h" #import "PBMConstants.h" #import "PBMFunctions.h" +#import "NSObject+PBMExtensions.h" @interface PBMORTBAbstract () diff --git a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBImp.h b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBImp.h index 92cccb132..82a5f064f 100644 --- a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBImp.h +++ b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBImp.h @@ -97,6 +97,9 @@ NS_ASSUME_NONNULL_BEGIN //support can be assumed. @property (nonatomic, strong) NSNumber *secure; +//Indicates whether the ad is rewarded +@property (nonatomic, strong, nullable) NSNumber *rewarded; + //Array of exchange-specific names of supported iframe busters. //Note: iframebuster is not supported. diff --git a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBImp.m b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBImp.m index 6c6b06010..a0c2f9c9b 100644 --- a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBImp.m +++ b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBImp.m @@ -55,6 +55,7 @@ - (nonnull PBMJsonDictionary *)toJsonDictionary { ret[@"tagid"] = self.tagid; ret[@"clickbrowser"] = self.clickbrowser; ret[@"secure"] = self.secure; + ret[@"rwdd"] = self.rewarded; ret[@"ext"] = [[self extDictionary] nullIfEmpty]; @@ -91,6 +92,7 @@ - (instancetype)initWithJsonDictionary:(nonnull PBMJsonDictionary *)jsonDictiona _tagid = jsonDictionary[@"tagid"]; _clickbrowser = jsonDictionary[@"clickbrowser"]; _secure = jsonDictionary[@"secure"]; + _rewarded = jsonDictionary[@"rwdd"]; _extPrebid = [[PBMORTBImpExtPrebid alloc] initWithJsonDictionary:jsonDictionary[@"ext"][@"prebid"]]; _extSkadn = [[PBMORTBImpExtSkadn alloc] initWithJsonDictionary:jsonDictionary[@"ext"][@"skadn"]]; diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/AdLoadFlowControllerDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/AdLoadFlowControllerDelegate.swift index 3935fa9e8..227077c05 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/AdLoadFlowControllerDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/AdLoadFlowControllerDelegate.swift @@ -17,8 +17,6 @@ import Foundation @objc public protocol AdLoadFlowControllerDelegate: NSObjectProtocol { - var adUnitConfig:AdUnitConfig { get } - // Loading callbacks @objc func adLoadFlowController(_ adLoadFlowController: PBMAdLoadFlowController, failedWithError error: Error?) diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMAdLoadFlowController.h b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMAdLoadFlowController.h index b9bdea502..39c2ee040 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMAdLoadFlowController.h +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMAdLoadFlowController.h @@ -44,6 +44,7 @@ typedef BOOL(^PBMAdUnitConfigValidationBlock)(AdUnitConfig *adUnitConfig, BOOL r - (instancetype)initWithBidRequesterFactory:(id (^)(AdUnitConfig *))bidRequesterFactory adLoader:(id)adLoader + adUnitConfig:(AdUnitConfig *)adUnitConfig delegate:(id)delegate configValidationBlock:(PBMAdUnitConfigValidationBlock)configValidationBlock; diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMAdLoadFlowController.m b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMAdLoadFlowController.m index 786e66943..92bbcbb4e 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMAdLoadFlowController.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMAdLoadFlowController.m @@ -38,6 +38,7 @@ @implementation PBMAdLoadFlowController - (instancetype)initWithBidRequesterFactory:(id (^)(AdUnitConfig *))bidRequesterFactory adLoader:(id)adLoader + adUnitConfig:(AdUnitConfig *)adUnitConfig delegate:(id)delegate configValidationBlock:(PBMAdUnitConfigValidationBlock)configValidationBlock { @@ -49,6 +50,7 @@ - (instancetype)initWithBidRequesterFactory:(id (^)(AdU _adLoader = adLoader; _delegate = delegate; _configValidationBlock = [configValidationBlock copy]; + _savedAdUnitConfig = adUnitConfig; NSString * const uuid = [[NSUUID UUID] UUIDString]; const char * const queueName = [[NSString stringWithFormat:@"PBMAdLoadFlowController_%@", uuid] UTF8String]; @@ -195,14 +197,13 @@ - (void)moveToNextLoadingStep { } - (void)tryLaunchingAdRequestFlow { - AdUnitConfig * const configClone = [self.delegate.adUnitConfig copy]; - const BOOL configIsValid = self.configValidationBlock(configClone, NO); + const BOOL configIsValid = self.configValidationBlock(self.savedAdUnitConfig, NO); if (!configIsValid) { - [self reportLoadingFailedWithError:[PBMError errorWithMessage:@"AdUnitConfig is not valid." type:PBMErrorTypeInternalError]]; + [self reportLoadingFailedWithError:[PBMError errorWithMessage:@"AdUnitConfig is not valid." + type:PBMErrorTypeInternalError]]; return; } - self.savedAdUnitConfig = configClone; [self.delegate adLoadFlowControllerWillSendBidRequest:self]; [self sendBidRequest]; @@ -261,7 +262,8 @@ - (void)loadPrebidDisplayView { const BOOL configIsValid = self.configValidationBlock(self.savedAdUnitConfig, YES); if (!configIsValid) { - [self reportLoadingFailedWithError:[PBMError errorWithMessage:@"AdUnitConfig is not valid." type:PBMErrorTypeInternalError]]; + [self reportLoadingFailedWithError:[PBMError errorWithMessage:@"AdUnitConfig is not valid." + type:PBMErrorTypeInternalError]]; return; } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.h b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.h index cca15584b..761d76898 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.h +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.h @@ -23,9 +23,9 @@ NS_ASSUME_NONNULL_BEGIN @interface PBMInterstitialAdLoader : NSObject -- (instancetype)initWithDelegate:(id)delegate; +- (instancetype)initWithDelegate:(id)delegate + eventHandler:(id)eventHandler; -@property (nonatomic, strong) NSObject *reward; @end NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.m b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.m index 784755f68..730ce78f3 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.m @@ -28,11 +28,12 @@ #import #endif -@interface PBMInterstitialAdLoader () -@property (nonatomic, weak, nullable, readonly) id delegate; -@end +@interface PBMInterstitialAdLoader () +@property (nonatomic, weak, nullable, readonly) id delegate; +@property (nonatomic, weak, nullable, readonly) id eventHandler; +@end @implementation PBMInterstitialAdLoader @@ -40,18 +41,23 @@ @implementation PBMInterstitialAdLoader // MARK: - Lifecycle -- (instancetype)initWithDelegate:(id)delegate { +- (instancetype)initWithDelegate:(id)delegate + eventHandler:(nonnull id)eventHandler { + if (!(self = [super init])) { return nil; } + _delegate = delegate; + _eventHandler = eventHandler; + return self; } // MARK: - PBMAdLoaderProtocol - (id)primaryAdRequester { - return self.delegate.eventHandler; + return self.eventHandler; } - (void)createPrebidAdWithBid:(Bid *)bid @@ -59,8 +65,8 @@ - (void)createPrebidAdWithBid:(Bid *)bid adObjectSaver:(void (^)(id))adObjectSaver loadMethodInvoker:(void (^)(dispatch_block_t))loadMethodInvoker { - InterstitialController * const controller = [[InterstitialController alloc] initWithBid:bid - adConfiguration:adUnitConfig]; + InterstitialController * const controller = [[InterstitialController alloc] initWithBid:bid + adConfiguration:adUnitConfig]; adObjectSaver(controller); @weakify(self); loadMethodInvoker(^{ @@ -119,13 +125,11 @@ - (void)prebidDidWin { } - (void)adServerDidWin { - [self.flowDelegate adLoader:self loadedPrimaryAd:self.delegate.eventHandler adSize:nil]; + [self.flowDelegate adLoader:self loadedPrimaryAd:self.eventHandler adSize:nil]; } - (void)failedWithError:(nullable NSError *)error { [self.flowDelegate adLoader:self failedWithPrimarySDKError:error]; } -@synthesize reward; - @end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoaderDelegate.h b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoaderDelegate.h index ea96bcb33..84008eb65 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoaderDelegate.h +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoaderDelegate.h @@ -26,21 +26,15 @@ NS_ASSUME_NONNULL_BEGIN @required -@property (nonatomic, strong, nullable, readonly) id eventHandler; - // Loading callbacks - (void)interstitialAdLoader:(PBMInterstitialAdLoader *)interstitialAdLoader loadedAd:(void (^)(UIViewController * _Nullable))showBlock isReadyBlock:(BOOL (^)(void))isReadyBlock; // Hook to insert interaction delegate -- (void) interstitialAdLoader:(PBMInterstitialAdLoader *)interstitialAdLoader +- (void)interstitialAdLoader:(PBMInterstitialAdLoader *)interstitialAdLoader createdInterstitialController:(InterstitialController *)interstitialController; -@optional - -@property (nonatomic, strong, nullable) NSObject *reward; - @end NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift index d2db18d6d..de71986a5 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift @@ -156,6 +156,7 @@ public class BannerView: UIView, adUnitConfiguration: adUnitConfig) }, adLoader: bannerAdLoader, + adUnitConfig: adUnitConfig, delegate: self, configValidationBlock: { adUnitConfig, renderWithPrebid in true diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift index 3d4938b09..d56b10cfd 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift @@ -1,66 +1,54 @@ /*   Copyright 2018-2021 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation import UIKit -/// A base class for interstitial ad units. -public class BaseInterstitialAdUnit : +@objcMembers +class BaseInterstitialAdUnit: NSObject, PBMInterstitialAdLoaderDelegate, AdLoadFlowControllerDelegate, InterstitialControllerInteractionDelegate, - InterstitialEventInteractionDelegate, - BaseInterstitialAdUnitProtocol { + InterstitialEventInteractionDelegate { - // MARK: - Public Properties + // MARK: - Internal Properties - /// The banner parameters used for configuring ad unit. - @objc public var bannerParameters: BannerParameters { - get { adUnitConfig.adConfiguration.bannerParameters } - } + let adUnitConfig: AdUnitConfig + let eventHandler: PBMPrimaryAdRequesterProtocol - /// The video parameters used for configuring ad unit. - @objc public var videoParameters: VideoParameters { - get { adUnitConfig.adConfiguration.videoParameters } + weak var delegate: BaseInterstitialAdUnitProtocol? { + didSet { + if let adLoader { + delegate?.callEventHandler_setLoadingDelegate(adLoader) + } + } } - /// The last bid response received for the ad unit. - @objc public var lastBidResponse: BidResponse? { - return adLoadFlowController?.bidResponse + var bannerParameters: BannerParameters { + get { adUnitConfig.adConfiguration.bannerParameters } } - /// The configuration ID for the ad unit. - @objc public var configID: String { - adUnitConfig.configId + var videoParameters: VideoParameters { + get { adUnitConfig.adConfiguration.videoParameters } } - /// The set of ad formats supported by this ad unit. - @objc public var adFormats: Set { - get { adUnitConfig.adFormats } - set { adUnitConfig.adFormats = newValue } + var lastBidResponse: BidResponse? { + adLoadFlowController?.bidResponse } - /// The ORTB (OpenRTB) configuration string for the ad unit. - @objc public var ortbConfig: String? { - get { adUnitConfig.ortbConfig } - set { adUnitConfig.ortbConfig = newValue } - } - - /// A Boolean value indicating whether the ad unit is ready to be displayed. - @objc public var isReady: Bool { + var isReady: Bool { objc_sync_enter(blocksLockToken) if let block = isReadyBlock { let res = block() @@ -71,333 +59,97 @@ public class BaseInterstitialAdUnit : objc_sync_exit(blocksLockToken) return false } - - /// A Boolean value indicating whether the video controls are muted. - @objc public var isMuted: Bool { - get { adUnitConfig.adConfiguration.videoControlsConfig.isMuted } - set { adUnitConfig.adConfiguration.videoControlsConfig.isMuted = newValue } - } - - /// A Boolean value indicating whether the sound button is visible in the video controls. - @objc public var isSoundButtonVisible: Bool { - get { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible } - set { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible = newValue } - } - - /// The area of the close button in the video controls as a percentage. - @objc public var closeButtonArea: Double { - get { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonArea } - set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonArea = newValue } - } - - /// The position of the close button in the video controls. - @objc public var closeButtonPosition: Position { - get { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition } - set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition = newValue } - } - - /// A delegate for handling interactions with the ad unit. - @objc public weak var delegate: AnyObject? - - /// The configuration object for the ad unit. - public let adUnitConfig: AdUnitConfig // MARK: - Private Properties private var adLoadFlowController: PBMAdLoadFlowController! private let blocksLockToken: NSObject - private var showBlock: ((UIViewController?) -> Void)? private var currentAdBlock: ((UIViewController?) -> Void)? private var isReadyBlock: (() -> Bool)? - - private weak var targetController: UIViewController? + private var adLoader: PBMInterstitialAdLoader? - // MARK: - Public Methods + private weak var targetController: UIViewController? - /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID, minimum size percentage, and event handler. - /// - Parameters: - /// - configID: The unique identifier for the ad unit configuration. - /// - minSizePerc: The minimum size percentage for the ad unit. - /// - eventHandler: An optional event handler object for handling ad events. - required public init(configID: String, - minSizePerc: NSValue?, - eventHandler: AnyObject?) { - + init( + configID: String, + minSizePerc: NSValue?, + eventHandler: PBMPrimaryAdRequesterProtocol + ) { adUnitConfig = AdUnitConfig(configId: configID) - adUnitConfig.adConfiguration.isInterstitialAd = true - adUnitConfig.minSizePerc = minSizePerc - adUnitConfig.adPosition = .fullScreen - adUnitConfig.adConfiguration.adFormats = [.banner, .video] - adUnitConfig.adConfiguration.bannerParameters.api = PrebidConstants.supportedRenderingBannerAPISignals blocksLockToken = NSObject() - + self.eventHandler = eventHandler - + super.init() - videoParameters.placement = .Interstitial + let adLoader = PBMInterstitialAdLoader( + delegate: self, + eventHandler: eventHandler + ) - let adLoader = PBMInterstitialAdLoader(delegate: self) - callEventHandler_setLoadingDelegate(adLoader) + self.adLoader = adLoader - adLoadFlowController = PBMAdLoadFlowController(bidRequesterFactory: { adUnitConfig in - return PBMBidRequester(connection: PrebidServerConnection.shared, - sdkConfiguration: Prebid.shared, - targeting: Targeting.shared, - adUnitConfiguration: adUnitConfig) - }, - adLoader: adLoader, - delegate: self, - configValidationBlock: { _,_ in true } ) - } - - /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID, minimum size percentage, and event handler. - /// - Parameters: - /// - configID: The unique identifier for the ad unit configuration. - /// - minSizePercentage: The minimum size percentage for the ad unit. - /// - eventHandler: An optional event handler object for handling ad events. - public convenience init(configID: String, - minSizePercentage: CGSize, - eventHandler:AnyObject?) - { - self.init(configID: configID, - minSizePerc:NSValue(cgSize: minSizePercentage), - eventHandler: eventHandler) - } - - /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID and event handler. - /// - Parameters: - /// - configID: The unique identifier for the ad unit configuration. - /// - eventHandler: An optional event handler object for handling ad events. - public convenience init(configID: String, - eventHandler:AnyObject?) { - self.init(configID: configID, - minSizePerc:nil, - eventHandler: eventHandler) + adLoadFlowController = PBMAdLoadFlowController( + bidRequesterFactory: { adUnitConfig in + return PBMBidRequester( + connection: PrebidServerConnection.shared, + sdkConfiguration: Prebid.shared, + targeting: Targeting.shared, + adUnitConfiguration: adUnitConfig + ) + }, + adLoader: adLoader, + adUnitConfig: adUnitConfig, + delegate: self, + configValidationBlock: { _, _ in true } + ) + // Set default values + adUnitConfig.adConfiguration.isInterstitialAd = true + adUnitConfig.minSizePerc = minSizePerc + adUnitConfig.adPosition = .fullScreen + adUnitConfig.adConfiguration.adFormats = [.banner, .video] + adUnitConfig.adConfiguration.bannerParameters.api = PrebidConstants.supportedRenderingBannerAPISignals + videoParameters.placement = .Interstitial } - - /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID and minimum size percentage. - /// - Parameters: - /// - configID: The unique identifier for the ad unit configuration. - /// - minSizePercentage: The minimum size percentage for the ad unit. - public convenience init(configID: String, - minSizePercentage:CGSize) { - - self.init(configID: configID, - minSizePerc:NSValue(cgSize: minSizePercentage), - eventHandler: nil) - } - - /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID. - /// - Parameter configID: The unique identifier for the ad unit configuration. - public convenience init(configID: String) { - self.init(configID: configID, - minSizePerc:nil, - eventHandler: nil) } // MARK: - Public Methods - /// Loads a new ad. - @objc public func loadAd() { + func loadAd() { adLoadFlowController.refresh() } - /// Shows the ad from a specified view controller. - /// - Parameter controller: The view controller from which the ad will be presented. - /// - Note: This method must be called on the main thread. - @objc public func show(from controller: UIViewController) { + func show(from controller: UIViewController) { // It is expected from the user to call this method on main thread assert(Thread.isMainThread, "Expected to only be called on the main thread"); - + objc_sync_enter(blocksLockToken) - - guard self.showBlock != nil, - self.currentAdBlock == nil else { - objc_sync_exit(blocksLockToken) - return; - } - isReadyBlock = nil; - currentAdBlock = showBlock; - showBlock = nil; - callDelegate_willPresentAd() - targetController = controller; - currentAdBlock?(controller); + guard self.showBlock != nil, + self.currentAdBlock == nil else { objc_sync_exit(blocksLockToken) - - } - - // MARK: - Ext Data (imp[].ext.data) - - /// Adds context data for a specified key. - /// - Parameters: - /// - data: The data to add. - /// - key: The key associated with the data. - @available(*, deprecated, message: "This method is deprecated. Please, use addExtData method instead.") - @objc public func addContextData(_ data: String, forKey key: String) { - addExtData(key: key, value: data) - } - - /// Updates context data for a specified key. - /// - Parameters: - /// - data: A set of data to update. - /// - key: The key associated with the data. - @available(*, deprecated, message: "This method is deprecated. Please, use updateExtData method instead.") - @objc public func updateContextData(_ data: Set, forKey key: String) { - updateExtData(key: key, value: data) - } - - /// Removes context data for a specified key. - /// - Parameter key: The key associated with the data to remove. - @available(*, deprecated, message: "This method is deprecated. Please, use removeExtData method instead.") - @objc public func removeContextDate(forKey key: String) { - removeExtData(forKey: key) - } - - /// Clears all context data. - @available(*, deprecated, message: "This method is deprecated. Please, use clearExtData method instead.") - @objc public func clearContextData() { - clearExtData() - } - - /// Adds ext data. - /// - Parameters: - /// - key: The key for the data. - /// - value: The value for the data. - @objc public func addExtData(key: String, value: String) { - adUnitConfig.addExtData(key: key, value: value) - } - - /// Updates ext data. - /// - Parameters: - /// - key: The key for the data. - /// - value: The value for the data. - @objc public func updateExtData(key: String, value: Set) { - adUnitConfig.updateExtData(key: key, value: value) - } - - /// Removes ext data. - /// - Parameters: - /// - key: The key for the data. - @objc public func removeExtData(forKey: String) { - adUnitConfig.removeExtData(for: forKey) - } - - /// Clears ext data. - @objc public func clearExtData() { - adUnitConfig.clearExtData() - } - - // MARK: - Ext keywords (imp[].ext.keywords) - - /// Adds a context keyword. - /// - Parameter newElement: The keyword to add. - @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeyword method instead.") - @objc public func addContextKeyword(_ newElement: String) { - addExtKeyword(newElement) - } - - /// Adds a set of context keywords. - /// - Parameter newElements: A set of keywords to add. - @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeywords method instead.") - @objc public func addContextKeywords(_ newElements: Set) { - addExtKeywords(newElements) - } - - /// Removes a context keyword. - /// - Parameter element: The keyword to remove. - @available(*, deprecated, message: "This method is deprecated. Please, use removeExtKeyword method instead.") - @objc public func removeContextKeyword(_ element: String) { - removeExtKeyword(element) - } - - /// Clears all context keywords. - @available(*, deprecated, message: "This method is deprecated. Please, use clearExtKeywords method instead.") - @objc public func clearContextKeywords() { - clearExtKeywords() - } - - /// Adds an extended keyword. - /// - Parameter newElement: The keyword to be added. - @objc public func addExtKeyword(_ newElement: String) { - adUnitConfig.addExtKeyword(newElement) - } - - /// Adds multiple extended keywords. - /// - Parameter newElements: A set of keywords to be added. - @objc public func addExtKeywords(_ newElements: Set) { - adUnitConfig.addExtKeywords(newElements) - } - - /// Removes an extended keyword. - /// - Parameter element: The keyword to be removed. - @objc public func removeExtKeyword(_ element: String) { - adUnitConfig.removeExtKeyword(element) - } - - /// Clears all extended keywords. - @objc public func clearExtKeywords() { - adUnitConfig.clearExtKeywords() - } - - // MARK: - App Content (app.content.data) - - /// Sets the app content data. - /// - Parameter appContent: The app content data. - @objc public func setAppContent(_ appContent: PBMORTBAppContent) { - adUnitConfig.setAppContent(appContent) - } - - /// Clears the app content data. - @objc public func clearAppContent() { - adUnitConfig.clearAppContent() - } - - /// Adds app content data objects. - /// - Parameter dataObjects: The data objects to be added. - @objc public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { - adUnitConfig.addAppContentData(dataObjects) - } - - /// Removes an app content data object. - /// - Parameter dataObject: The data object to be removed. - @objc public func removeAppContentDataObject(_ dataObject: PBMORTBContentData) { - adUnitConfig.removeAppContentData(dataObject) - } - - /// Clears all app content data objects. - @objc public func clearAppContentDataObjects() { - adUnitConfig.clearAppContentData() - } - - // MARK: - User Data (user.data) - - /// Adds user data objects. - /// - Parameter userDataObjects: The user data objects to be added. - @objc public func addUserData(_ userDataObjects: [PBMORTBContentData]) { - adUnitConfig.addUserData(userDataObjects) - } - - /// Removes a user data object. - /// - Parameter userDataObject: The user data object to be removed. - @objc public func removeUserData(_ userDataObject: PBMORTBContentData) { - adUnitConfig.removeUserData(userDataObject) - } - - /// Clears all user data objects. - @objc public func clearUserData() { - adUnitConfig.clearUserData() + return; + } + + isReadyBlock = nil + currentAdBlock = showBlock + showBlock = nil + + delegate?.callDelegate_willPresentAd() + targetController = controller + currentAdBlock?(controller) + objc_sync_exit(blocksLockToken) } // MARK: - PBMInterstitialAdLoaderDelegate - /// Internal delegate method. - public func interstitialAdLoader(_ interstitialAdLoader: PBMInterstitialAdLoader, - loadedAd showBlock: @escaping (UIViewController?) -> Void, - isReadyBlock: @escaping () -> Bool) { + public func interstitialAdLoader( + _ interstitialAdLoader: PBMInterstitialAdLoader, + loadedAd showBlock: @escaping (UIViewController?) -> Void, + isReadyBlock: @escaping () -> Bool + ) { objc_sync_enter(blocksLockToken) self.showBlock = showBlock self.isReadyBlock = isReadyBlock @@ -406,26 +158,20 @@ public class BaseInterstitialAdUnit : reportLoadingSuccess() } - /// Internal delegate method. - public func interstitialAdLoader(_ interstitialAdLoader: PBMInterstitialAdLoader, - createdInterstitialController interstitialController: InterstitialController) { + public func interstitialAdLoader( + _ interstitialAdLoader: PBMInterstitialAdLoader, + createdInterstitialController interstitialController: InterstitialController + ) { interstitialController.interactionDelegate = self } - - /// The event handler for the interstitial events. - public var eventHandler: Any? - // MARK: - AdLoadFlowControllerDelegate - /// Called when the ad load flow controller is about to send a bid request. - public func adLoadFlowControllerWillSendBidRequest(_ adLoadFlowController: PBMAdLoadFlowController) { - // nop - } + public func adLoadFlowControllerWillSendBidRequest(_ adLoadFlowController: PBMAdLoadFlowController) {} /// Called when the ad load flow controller is about to request the primary ad. public func adLoadFlowControllerWillRequestPrimaryAd(_ adLoadFlowController: PBMAdLoadFlowController) { - callEventHandler_setInteractionDelegate() + delegate?.callEventHandler_setInteractionDelegate() } /// Called to determine if the ad load flow controller should continue with the current flow. @@ -433,8 +179,10 @@ public class BaseInterstitialAdUnit : true } - /// Called when the ad load flow controller fails with an error. - public func adLoadFlowController(_ adLoadFlowController: PBMAdLoadFlowController, failedWithError error: Error?) { + public func adLoadFlowController( + _ adLoadFlowController: PBMAdLoadFlowController, + failedWithError error: Error? + ) { reportLoadingFailed(with: error) } @@ -443,63 +191,44 @@ public class BaseInterstitialAdUnit : /// Tracks an impression for the given interstitial controller. public func trackImpression(forInterstitialController: InterstitialController) { DispatchQueue.main.async { - self.callEventHandler_trackImpression() + self.delegate?.callEventHandler_trackImpression() } } /// Called when the ad in the interstitial controller is clicked. public func interstitialControllerDidClickAd(_ interstitialController: InterstitialController) { assert(Thread.isMainThread, "Expected to only be called on the main thread") - callDelegate_didClickAd() + delegate?.callDelegate_didClickAd() } /// Called when the ad in the interstitial controller is closed. public func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { assert(Thread.isMainThread, "Expected to only be called on the main thread") - callDelegate_didDismissAd() + delegate?.callDelegate_didDismissAd() } /// Called when the ad in the interstitial controller causes the app to leave. public func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) { assert(Thread.isMainThread, "Expected to only be called on the main thread") - callDelegate_willLeaveApplication() + delegate?.callDelegate_willLeaveApplication() } - /// Called when the interstitial controller displays an ad. - public func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) { - - } - - /// Called when the interstitial controller completes the ad display. - public func interstitialControllerDidComplete(_ interstitialController: InterstitialController) { - - } + public func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) {} + public func interstitialControllerDidComplete(_ interstitialController: InterstitialController) {} + public func trackUserReward(_ interstitialController: InterstitialController, _ reward: PrebidReward) {} - /// Provides the view controller to use for modal presentation. - public func viewControllerForModalPresentation(fromInterstitialController: InterstitialController) -> UIViewController? { + public func viewControllerForModalPresentation( + fromInterstitialController: InterstitialController + ) -> UIViewController? { return targetController } - // MARK: - Private methods - - private func reportLoadingSuccess() { - DispatchQueue.main.async { - self.callDelegate_didReceiveAd() - } - } - - private func reportLoadingFailed(with error: Error?) { - DispatchQueue.main.async { - self.callDelegate_didFailToReceiveAd(with: error) - } - } - // MARK: - InterstitialEventInteractionDelegate /// Called when an ad is about to be presented. public func willPresentAd() { DispatchQueue.main.async { - self.callDelegate_willPresentAd() + self.delegate?.callDelegate_willPresentAd() } } @@ -510,90 +239,35 @@ public class BaseInterstitialAdUnit : objc_sync_exit(blocksLockToken) DispatchQueue.main.async { - self.callDelegate_didDismissAd() + self.delegate?.callDelegate_didDismissAd() } } /// Called when the ad causes the app to leave. public func willLeaveApp() { DispatchQueue.main.async { - self.callDelegate_willLeaveApplication() + self.delegate?.callDelegate_willLeaveApplication() } } /// Called when an ad is clicked. public func didClickAd() { DispatchQueue.main.async { - self.callDelegate_didClickAd() + self.delegate?.callDelegate_didClickAd() } } - - // MARK: - BaseInterstitialAdUnitProtocol - /// Requests an ad using the provided bid response. - public func callEventHandler_requestAd(with bidResponse: BidResponse?) { - - } + // MARK: - Private methods - /// Displays the ad using the provided view controller. - public func callEventHandler_show(from controller: UIViewController?) { - + private func reportLoadingSuccess() { + DispatchQueue.main.async { + self.delegate?.callDelegate_didReceiveAd() + } } - - // MARK: - Abstract Methods - /// Checks if the ad unit is ready to show an ad. - public func callEventHandler_isReady() -> Bool { - return false // to be overridden in subclass - } - - /// Notifies the delegate that an ad has been successfully received. - public func callDelegate_didReceiveAd() { - // to be overridden in subclass - } - - /// Notifies the delegate that the ad failed to load. - public func callDelegate_didFailToReceiveAd(with: Error?) { - // to be overridden in subclass - } - - /// Notifies the delegate that an ad is about to be presented. - public func callDelegate_willPresentAd() { - // to be overridden in subclass - } - - /// Notifies the delegate that an ad has been dismissed. - public func callDelegate_didDismissAd() { - // to be overridden in subclass - } - - /// Notifies the delegate that the app is about to leave due to an ad. - public func callDelegate_willLeaveApplication() { - // to be overridden in subclass - } - - /// Notifies the delegate that an ad has been clicked. - public func callDelegate_didClickAd() { - // to be overridden in subclass - } - - /// Sets the loading delegate for the event handler. - public func callEventHandler_setLoadingDelegate(_ loadingDelegate: NSObject?) { - // to be overridden in subclass - } - - /// Sets the interaction delegate for the event handler. - public func callEventHandler_setInteractionDelegate() { - // to be overridden in subclass - } - - /// Shows the ad from the provided view controller. - public func callEventHandler_showFromViewController(controller: UIViewController?) { - // to be overridden in subclass - } - - /// Tracks an impression for the ad. - public func callEventHandler_trackImpression() { - // to be overridden in subclass + private func reportLoadingFailed(with error: Error?) { + DispatchQueue.main.async { + self.delegate?.callDelegate_didFailToReceiveAd(with: error) + } } } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnitProtocol.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnitProtocol.swift index 9b6877df8..308bdc29f 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnitProtocol.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnitProtocol.swift @@ -16,7 +16,7 @@ import Foundation import UIKit -@objc public protocol BaseInterstitialAdUnitProtocol: NSObjectProtocol { +@objc protocol BaseInterstitialAdUnitProtocol: NSObjectProtocol { @objc func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) @@ -33,4 +33,6 @@ import UIKit @objc func callEventHandler_requestAd(with bidResponse: BidResponse?) @objc func callEventHandler_show(from controller: UIViewController?) @objc func callEventHandler_trackImpression() + + @objc optional func callDelegate_rewardedAdUserDidEarnReward(reward: PrebidReward) } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseRewardedAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseRewardedAdUnit.swift new file mode 100644 index 000000000..4d7f833db --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseRewardedAdUnit.swift @@ -0,0 +1,53 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import Foundation + +@objcMembers +class BaseRewardedAdUnit: BaseInterstitialAdUnit, RewardedEventInteractionDelegate { + + override init( + configID: String, + minSizePerc: NSValue?, + eventHandler: PBMPrimaryAdRequesterProtocol + ) { + super.init( + configID: configID, + minSizePerc: minSizePerc, + eventHandler: eventHandler + ) + + // Setup default values + adUnitConfig.adConfiguration.isRewarded = true + } + + // MARK: - InterstitialControllerInteractionDelegate + + override func trackUserReward(_ interstitialController: InterstitialController, _ reward: PrebidReward) { + DispatchQueue.main.async { + self.delegate?.callDelegate_rewardedAdUserDidEarnReward?(reward: reward) + } + } + + // MARK: - RewardedEventInteractionDelegate + + func userDidEarnReward(_ reward: PrebidReward?) { + if let reward { + DispatchQueue.main.async { + self.delegate?.callDelegate_rewardedAdUserDidEarnReward?(reward: reward) + } + } + } +} diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialEventInteractionDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialEventInteractionDelegate.swift index 62b91fa16..4e9eb2373 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialEventInteractionDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialEventInteractionDelegate.swift @@ -17,23 +17,15 @@ import Foundation @objc public protocol InterstitialEventInteractionDelegate: NSObjectProtocol { - /*! - @abstract Call this when the ad server SDK is about to present a modal - */ + /// Call this when the ad server SDK is about to present a modal @objc func willPresentAd() - /*! - @abstract Call this when the ad server SDK dissmisses a modal - */ + /// Call this when the ad server SDK dissmisses a modal @objc func didDismissAd() - /*! - @abstract Call this when the ad server SDK informs about app leave event as a result of user interaction. - */ + /// Call this when the ad server SDK informs about app leave event as a result of user interaction. @objc func willLeaveApp() - /*! - @abstract Call this when the ad server SDK informs about click event as a result of user interaction. - */ + /// Call this when the ad server SDK informs about click event as a result of user interaction. @objc func didClickAd() } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift index e0d9c5488..f4e69cbb7 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift @@ -1,157 +1,415 @@ /*   Copyright 2018-2021 Prebid.org, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ import UIKit /// Represents an interstitial ad unit. Built for rendering type of integration. @objcMembers -public class InterstitialRenderingAdUnit: BaseInterstitialAdUnit { +public class InterstitialRenderingAdUnit: NSObject, BaseInterstitialAdUnitProtocol { + + /// A delegate for handling interactions with the ad unit. + public weak var delegate: InterstitialAdUnitDelegate? + + /// A Boolean value indicating whether the ad unit is ready to be displayed. + public var isReady: Bool { + baseAdUnit.isReady + } + + /// The set of ad formats supported by this ad unit. + public var adFormats: Set { + get { adUnitConfig.adFormats } + set { adUnitConfig.adFormats = newValue } + } + + /// The ORTB (OpenRTB) configuration string for the ad unit. + public var ortbConfig: String? { + get { adUnitConfig.ortbConfig } + set { adUnitConfig.ortbConfig = newValue } + } + + /// The banner parameters used for configuring ad unit. + public var bannerParameters: BannerParameters { + get { adUnitConfig.adConfiguration.bannerParameters } + } + + /// The video parameters used for configuring ad unit. + public var videoParameters: VideoParameters { + get { adUnitConfig.adConfiguration.videoParameters } + } + + // MARK: - Video controls configuration + + /// The area of the close button in the video controls as a percentage. + public var closeButtonArea: Double { + get { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonArea } + set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonArea = newValue } + } + + /// The position of the close button in the video controls. + public var closeButtonPosition: Position { + get { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition } + set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition = newValue } + } /// The area of the skip button in the video controls, specified as a percentage of the screen width. - @objc public var skipButtonArea: Double { + public var skipButtonArea: Double { get { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonArea } set { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonArea = newValue } } /// The position of the skip button in the video controls. - @objc public var skipButtonPosition: Position { + public var skipButtonPosition: Position { get { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonPosition } set { adUnitConfig.adConfiguration.videoControlsConfig.skipButtonPosition = newValue } } /// The delay before the skip button appears, in seconds. - @objc public var skipDelay: Double { + public var skipDelay: Double { get { adUnitConfig.adConfiguration.videoControlsConfig.skipDelay } set { adUnitConfig.adConfiguration.videoControlsConfig.skipDelay = newValue } } - - /// Initializes a new interstitial rendering ad unit with the specified configuration ID. - /// - Parameter configID: The unique identifier for the ad unit configuration. - @objc public init(configID: String) { - super.init(configID: configID, - minSizePerc: nil, - eventHandler: InterstitialEventHandlerStandalone()) + + /// A Boolean value indicating whether the video controls are muted. + public var isMuted: Bool { + get { adUnitConfig.adConfiguration.videoControlsConfig.isMuted } + set { adUnitConfig.adConfiguration.videoControlsConfig.isMuted = newValue } } - - /// Initializes a new interstitial rendering ad unit with the specified configuration ID and minimum size percentage. + + /// A Boolean value indicating whether the sound button is visible in the video controls. + public var isSoundButtonVisible: Bool { + get { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible } + set { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible = newValue } + } + + // MARK: Private properties + + private let baseAdUnit: BaseInterstitialAdUnit + + // NOTE: exposed for tests + var adUnitConfig: AdUnitConfig { + baseAdUnit.adUnitConfig + } + + private var eventHandler: PBMPrimaryAdRequesterProtocol { + baseAdUnit.eventHandler + } + + /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID. /// - Parameter configID: The unique identifier for the ad unit configuration. - /// - Parameter minSizePercentage: The minimum size percentage of the ad. - @objc public init(configID: String, minSizePercentage: CGSize) { - super.init( + public convenience init(configID: String) { + self.init( + configID: configID, + minSizePerc: nil, + primaryAdRequester: InterstitialEventHandlerStandalone() + ) + } + + /// Initializes a new `InterstitialRenderingAdUnit` with the specified configuration ID and minimum size percentage. + /// - Parameters: + /// - configID: The unique identifier for the ad unit configuration. + /// - minSizePercentage: The minimum size percentage for the ad unit. + public convenience init(configID: String, minSizePercentage: CGSize) { + self.init( configID: configID, minSizePerc: NSValue(cgSize: minSizePercentage), - eventHandler: InterstitialEventHandlerStandalone()) + primaryAdRequester: InterstitialEventHandlerStandalone() + ) } - - /// Initializes a new interstitial rendering ad unit with the specified configuration ID, minimum size percentage, and event handler. - /// - Parameter configID: The unique identifier for the ad unit configuration. - /// - Parameter minSizePercentage: The minimum size percentage of the ad. - /// - Parameter eventHandler: The event handler to manage ad events. - @objc public init(configID: String, minSizePercentage: CGSize, eventHandler: AnyObject) { - super.init( + + /// Initializes a new `InterstitialRenderingAdUnit` with the specified configuration ID and event handler. + /// - Parameters: + /// - configID: The unique identifier for the ad unit configuration. + /// - eventHandler: An object for handling ad events. + public convenience init(configID: String, eventHandler: AnyObject?) { + self.init( + configID: configID, + minSizePerc: nil, + primaryAdRequester: (eventHandler as? PBMPrimaryAdRequesterProtocol) ?? InterstitialEventHandlerStandalone() + ) + } + + /// Initializes a new `InterstitialRenderingAdUnit` with the specified configuration ID, minimum size percentage, and event handler. + /// - Parameters: + /// - configID: The unique identifier for the ad unit configuration. + /// - minSizePercentage: The minimum size percentage for the ad unit. + /// - eventHandler: An object for handling ad events. + public convenience init( + configID: String, + minSizePercentage: CGSize, + eventHandler: AnyObject + ) { + self.init( configID: configID, minSizePerc: NSValue(cgSize: minSizePercentage), - eventHandler: eventHandler) + primaryAdRequester: (eventHandler as? PBMPrimaryAdRequesterProtocol) ?? InterstitialEventHandlerStandalone() + ) } - /// Initializes a new interstitial rendering ad unit with the specified configuration ID, minimum size percentage, and event handler. - /// - Parameter configID: The unique identifier for the ad unit configuration. - /// - Parameter minSizePerc: The minimum size percentage of the ad. - /// - Parameter eventHandler: The event handler to manage ad events. - @objc required init(configID: String, minSizePerc: NSValue?, eventHandler: AnyObject?) { - super.init( + required init( + configID: String, + minSizePerc: NSValue?, + primaryAdRequester: PBMPrimaryAdRequesterProtocol + ) { + baseAdUnit = BaseInterstitialAdUnit( configID: configID, minSizePerc: minSizePerc, - eventHandler: eventHandler) + eventHandler: primaryAdRequester + ) + + super.init() + + baseAdUnit.delegate = self } - // MARK: - Protected overrides - - /// Called when an ad is successfully received. - @objc public override func callDelegate_didReceiveAd() { - if let delegate = self.delegate as? InterstitialAdUnitDelegate { - delegate.interstitialDidReceiveAd?(self) - } + // MARK: - Public methods + + /// Loads a new ad. + public func loadAd() { + baseAdUnit.loadAd() } - /// Called when the ad fails to be received. - @objc public override func callDelegate_didFailToReceiveAd(with error: Error?) { - if let delegate = self.delegate as? InterstitialAdUnitDelegate { - delegate.interstitial?(self, didFailToReceiveAdWithError: error) - } + /// Shows the ad from a specified view controller. + /// - Parameter controller: The view controller from which the ad will be presented. + /// - Note: This method must be called on the main thread. + public func show(from controller: UIViewController) { + baseAdUnit.show(from: controller) } - - /// Called when the ad will be presented. - @objc public override func callDelegate_willPresentAd() { - if let delegate = self.delegate as? InterstitialAdUnitDelegate { - delegate.interstitialWillPresentAd?(self) - } + + // MARK: - Ext Data (imp[].ext.data) + + /// Adds context data for a specified key. + /// - Parameters: + /// - data: The data to add. + /// - key: The key associated with the data. + @available(*, deprecated, message: "This method is deprecated. Please, use addExtData method instead.") + public func addContextData(_ data: String, forKey key: String) { + addExtData(key: key, value: data) } - - /// Called when the ad is dismissed. - @objc public override func callDelegate_didDismissAd() { - if let delegate = self.delegate as? InterstitialAdUnitDelegate { - delegate.interstitialDidDismissAd?(self) - } + + /// Updates context data for a specified key. + /// - Parameters: + /// - data: A set of data to update. + /// - key: The key associated with the data. + @available(*, deprecated, message: "This method is deprecated. Please, use updateExtData method instead.") + public func updateContextData(_ data: Set, forKey key: String) { + updateExtData(key: key, value: data) } - - /// Called when the user will leave the application. - @objc public override func callDelegate_willLeaveApplication() { - if let delegate = self.delegate as? InterstitialAdUnitDelegate { - delegate.interstitialWillLeaveApplication?(self) - } + + /// Removes context data for a specified key. + /// - Parameter key: The key associated with the data to remove. + @available(*, deprecated, message: "This method is deprecated. Please, use removeExtData method instead.") + public func removeContextDate(forKey key: String) { + removeExtData(forKey: key) } - - /// Called when the ad is clicked. - @objc public override func callDelegate_didClickAd() { - if let delegate = self.delegate as? InterstitialAdUnitDelegate { - delegate.interstitialDidClickAd?(self) - } + + /// Clears all context data. + @available(*, deprecated, message: "This method is deprecated. Please, use clearExtData method instead.") + public func clearContextData() { + clearExtData() } - /// Checks if the ad is ready to be displayed. - @objc public override func callEventHandler_isReady() -> Bool { - interstitialEventHandler?.isReady ?? false + /// Adds ext data. + /// - Parameters: + /// - key: The key for the data. + /// - value: The value for the data. + public func addExtData(key: String, value: String) { + adUnitConfig.addExtData(key: key, value: value) } - /// Sets the loading delegate for the event handler. - @objc public override func callEventHandler_setLoadingDelegate(_ loadingDelegate: NSObject?) { - interstitialEventHandler?.loadingDelegate = loadingDelegate as? RewardedEventLoadingDelegate + /// Updates ext data. + /// - Parameters: + /// - key: The key for the data. + /// - value: The value for the data. + public func updateExtData(key: String, value: Set) { + adUnitConfig.updateExtData(key: key, value: value) } - - /// Sets the interaction delegate for the event handler. - @objc public override func callEventHandler_setInteractionDelegate() { - interstitialEventHandler?.interactionDelegate = self + + /// Removes ext data. + /// - Parameters: + /// - key: The key for the data. + public func removeExtData(forKey: String) { + adUnitConfig.removeExtData(for: forKey) } - - /// Requests an ad with the specified bid response - @objc public override func callEventHandler_requestAd(with bidResponse: BidResponse?) { - interstitialEventHandler?.requestAd(with: bidResponse) + + /// Clears ext data. + public func clearExtData() { + adUnitConfig.clearExtData() } - - /// Shows the ad from the specified view controller. - @objc public override func callEventHandler_show(from controller: UIViewController?) { - interstitialEventHandler?.show(from: controller) + + // MARK: - Ext keywords (imp[].ext.keywords) + + /// Adds a context keyword. + /// - Parameter newElement: The keyword to add. + @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeyword method instead.") + public func addContextKeyword(_ newElement: String) { + addExtKeyword(newElement) } - - /// Tracks an impression for the ad. - @objc public override func callEventHandler_trackImpression() { - interstitialEventHandler?.trackImpression?() + + /// Adds a set of context keywords. + /// - Parameter newElements: A set of keywords to add. + @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeywords method instead.") + public func addContextKeywords(_ newElements: Set) { + addExtKeywords(newElements) + } + + /// Removes a context keyword. + /// - Parameter element: The keyword to remove. + @available(*, deprecated, message: "This method is deprecated. Please, use removeExtKeyword method instead.") + public func removeContextKeyword(_ element: String) { + removeExtKeyword(element) + } + + /// Clears all context keywords. + @available(*, deprecated, message: "This method is deprecated. Please, use clearExtKeywords method instead.") + public func clearContextKeywords() { + clearExtKeywords() + } + + /// Adds an extended keyword. + /// - Parameter newElement: The keyword to be added. + public func addExtKeyword(_ newElement: String) { + adUnitConfig.addExtKeyword(newElement) + } + + /// Adds multiple extended keywords. + /// - Parameter newElements: A set of keywords to be added. + public func addExtKeywords(_ newElements: Set) { + adUnitConfig.addExtKeywords(newElements) + } + + /// Removes an extended keyword. + /// - Parameter element: The keyword to be removed. + public func removeExtKeyword(_ element: String) { + adUnitConfig.removeExtKeyword(element) + } + + /// Clears all extended keywords. + public func clearExtKeywords() { + adUnitConfig.clearExtKeywords() + } + + // MARK: - App Content (app.content.data) + + /// Sets the app content data. + /// - Parameter appContent: The app content data. + public func setAppContent(_ appContent: PBMORTBAppContent) { + adUnitConfig.setAppContent(appContent) + } + + /// Clears the app content data. + public func clearAppContent() { + adUnitConfig.clearAppContent() + } + + /// Adds app content data objects. + /// - Parameter dataObjects: The data objects to be added. + public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { + adUnitConfig.addAppContentData(dataObjects) + } + + /// Removes an app content data object. + /// - Parameter dataObject: The data object to be removed. + public func removeAppContentDataObject(_ dataObject: PBMORTBContentData) { + adUnitConfig.removeAppContentData(dataObject) + } + + /// Clears all app content data objects. + public func clearAppContentDataObjects() { + adUnitConfig.clearAppContentData() + } + + // MARK: - User Data (user.data) + + /// Adds user data objects. + /// - Parameter userDataObjects: The user data objects to be added. + public func addUserData(_ userDataObjects: [PBMORTBContentData]) { + adUnitConfig.addUserData(userDataObjects) + } + + /// Removes a user data object. + /// - Parameter userDataObject: The user data object to be removed. + public func removeUserData(_ userDataObject: PBMORTBContentData) { + adUnitConfig.removeUserData(userDataObject) } - private var interstitialEventHandler: InterstitialEventHandlerProtocol? { - eventHandler as? InterstitialEventHandlerProtocol + /// Clears all user data objects. + public func clearUserData() { + adUnitConfig.clearUserData() + } + + // MARK: - Internal methods + + func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { + baseAdUnit.interstitialControllerDidCloseAd(interstitialController) + } + + func callDelegate_didReceiveAd() { + delegate?.interstitialDidReceiveAd?(self) + } + + func callDelegate_didFailToReceiveAd(with error: Error?) { + delegate?.interstitial?(self, didFailToReceiveAdWithError: error) + } + + func callDelegate_willPresentAd() { + delegate?.interstitialWillPresentAd?(self) + } + + func callDelegate_didDismissAd() { + delegate?.interstitialDidDismissAd?(self) + } + + func callDelegate_willLeaveApplication() { + delegate?.interstitialWillLeaveApplication?(self) + } + + func callDelegate_didClickAd() { + delegate?.interstitialDidClickAd?(self) + } + + func callEventHandler_isReady() -> Bool { + (eventHandler as? InterstitialEventHandlerProtocol)?.isReady ?? false + } + + func callEventHandler_setLoadingDelegate(_ loadingDelegate: NSObject?) { + if let eventHandler = eventHandler as? InterstitialEventHandlerProtocol { + eventHandler.loadingDelegate = loadingDelegate as? InterstitialEventLoadingDelegate + } + } + + func callEventHandler_setInteractionDelegate() { + if let eventHandler = eventHandler as? InterstitialEventHandlerProtocol { + eventHandler.interactionDelegate = baseAdUnit + } + } + + func callEventHandler_requestAd(with bidResponse: BidResponse?) { + if let eventHandler = eventHandler as? InterstitialEventHandlerProtocol { + eventHandler.requestAd(with: bidResponse) + } + } + + func callEventHandler_show(from controller: UIViewController?) { + if let eventHandler = eventHandler as? InterstitialEventHandlerProtocol { + eventHandler.show(from: controller) + } + } + + func callEventHandler_trackImpression() { + if let eventHandler = eventHandler as? InterstitialEventHandlerProtocol { + eventHandler.trackImpression?() + } } } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/PrebidReward.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/PrebidReward.swift new file mode 100644 index 000000000..ab9d5fec7 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/PrebidReward.swift @@ -0,0 +1,31 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +@objcMembers +public class PrebidReward: NSObject { + + public var type: String? + public var count: NSNumber? + public var ext: [String: Any]? + + convenience init(with ortbReward: PBMORTBRewardedReward? = nil) { + self.init() + self.type = ortbReward?.type + self.count = ortbReward?.count + self.ext = ortbReward?.ext + } +} diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift index 6a2ab7afb..4c5c757c7 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift @@ -16,177 +16,385 @@ import UIKit /// Represents an rewarded ad unit. Built for rendering type of integration. -@objc -public class RewardedAdUnit: BaseInterstitialAdUnit, - RewardedEventInteractionDelegate { - - /// The reward object for the ad unit. - @objc public private(set) var reward: NSObject? - - // MARK: - Lifecycle - - /// Initializes a `RewardedAdUnit` with the given configuration ID and event handler. - /// - /// - Parameter configID: The configuration ID for the ad unit. - /// - Parameter eventHandler: The event handler for the ad unit. - @objc public convenience init(configID: String, eventHandler: AnyObject) { +@objc @objcMembers +public class RewardedAdUnit: NSObject, BaseInterstitialAdUnitProtocol { + + /// A delegate for handling interactions with the ad unit. + public weak var delegate: RewardedAdUnitDelegate? + + /// A Boolean value indicating whether the ad unit is ready to be displayed. + public var isReady: Bool { + baseAdUnit.isReady + } + + /// The set of ad formats supported by this ad unit. + public var adFormats: Set { + get { adUnitConfig.adFormats } + set { adUnitConfig.adFormats = newValue } + } + + /// The ORTB (OpenRTB) configuration string for the ad unit. + public var ortbConfig: String? { + get { adUnitConfig.ortbConfig } + set { adUnitConfig.ortbConfig = newValue } + } + + /// The banner parameters used for configuring ad unit. + public var bannerParameters: BannerParameters { + get { adUnitConfig.adConfiguration.bannerParameters } + } + + /// The video parameters used for configuring ad unit. + public var videoParameters: VideoParameters { + get { adUnitConfig.adConfiguration.videoParameters } + } + + // MARK: - Video controls configuration + + /// The area of the close button in the video controls as a percentage. + public var closeButtonArea: Double { + get { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonArea } + set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonArea = newValue } + } + + /// The position of the close button in the video controls. + public var closeButtonPosition: Position { + get { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition } + set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition = newValue } + } + + /// A Boolean value indicating whether the video controls are muted. + public var isMuted: Bool { + get { adUnitConfig.adConfiguration.videoControlsConfig.isMuted } + set { adUnitConfig.adConfiguration.videoControlsConfig.isMuted = newValue } + } + + /// A Boolean value indicating whether the sound button is visible in the video controls. + public var isSoundButtonVisible: Bool { + get { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible } + set { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible = newValue } + } + + // MARK: Private properties + + private let baseAdUnit: BaseRewardedAdUnit + + private var adUnitConfig: AdUnitConfig { + baseAdUnit.adUnitConfig + } + + private var eventHandler: PBMPrimaryAdRequesterProtocol { + baseAdUnit.eventHandler + } + + /// Initializes a new `BaseInterstitialAdUnit` with the specified configuration ID. + /// - Parameter configID: The unique identifier for the ad unit configuration. + public convenience init(configID: String) { self.init( configID: configID, minSizePerc: nil, - eventHandler: eventHandler) + primaryAdRequester: RewardedEventHandlerStandalone() + ) } - - /// Initializes a `RewardedAdUnit` with the given configuration ID and a default event handler. - /// - /// - Parameter configID: The configuration ID for the ad unit. - @objc public convenience init(configID: String) { + + /// Initializes a new `InterstitialRenderingAdUnit` with the specified configuration ID and minimum size percentage. + /// - Parameters: + /// - configID: The unique identifier for the ad unit configuration. + /// - minSizePercentage: The minimum size percentage for the ad unit. + public convenience init(configID: String, minSizePercentage: CGSize) { + self.init( + configID: configID, + minSizePerc: NSValue(cgSize: minSizePercentage), + primaryAdRequester: RewardedEventHandlerStandalone() + ) + } + + /// Initializes a new `InterstitialRenderingAdUnit` with the specified configuration ID and event handler. + /// - Parameters: + /// - configID: The unique identifier for the ad unit configuration. + /// - eventHandler: An object for handling ad events. + public convenience init(configID: String, eventHandler: AnyObject?) { self.init( configID: configID, minSizePerc: nil, - eventHandler: RewardedEventHandlerStandalone()) + primaryAdRequester: (eventHandler as? PBMPrimaryAdRequesterProtocol) ?? RewardedEventHandlerStandalone() + ) + } + + /// Initializes a new `InterstitialRenderingAdUnit` with the specified configuration ID, minimum size percentage, and event handler. + /// - Parameters: + /// - configID: The unique identifier for the ad unit configuration. + /// - minSizePercentage: The minimum size percentage for the ad unit. + /// - eventHandler: An object for handling ad events. + public convenience init( + configID: String, + minSizePercentage: CGSize, + eventHandler: AnyObject + ) { + self.init( + configID: configID, + minSizePerc: NSValue(cgSize: minSizePercentage), + primaryAdRequester: (eventHandler as? PBMPrimaryAdRequesterProtocol) ?? RewardedEventHandlerStandalone() + ) } - /// Initializes a `RewardedAdUnit` with the given configuration ID, minimum size percentage, and event handler. - /// - /// - Parameter configID: The configuration ID for the ad unit. - /// - Parameter minSizePerc: The minimum size percentage for the ad unit. - /// - Parameter eventHandler: The event handler for the ad unit. - @objc required init(configID:String, minSizePerc: NSValue?, eventHandler: AnyObject?) { - super.init( + required init( + configID: String, + minSizePerc: NSValue?, + primaryAdRequester: PBMPrimaryAdRequesterProtocol + ) { + baseAdUnit = BaseRewardedAdUnit( configID: configID, minSizePerc: minSizePerc, - eventHandler: eventHandler) + eventHandler: primaryAdRequester + ) + + super.init() - adUnitConfig.adConfiguration.isOptIn = true - adFormats = [.video] + baseAdUnit.delegate = self } - // MARK: - PBMRewardedEventDelegate + // MARK: - Public methods - /// Called when the user earns a reward. - /// - /// - Parameter reward: The reward object associated with the event. - @objc public func userDidEarnReward(_ reward: NSObject?) { - DispatchQueue.main.async(execute: { [weak self] in - self?.reward = reward - self?.callDelegate_rewardedAdUserDidEarnReward() - }) + /// Loads a new ad. + public func loadAd() { + baseAdUnit.loadAd() } - // MARK: - BaseInterstitialAdUnitProtocol protocol + /// Shows the ad from a specified view controller. + /// - Parameter controller: The view controller from which the ad will be presented. + /// - Note: This method must be called on the main thread. + public func show(from controller: UIViewController) { + baseAdUnit.show(from: controller) + } + + // MARK: - Ext Data (imp[].ext.data) - /// Called when the interstitial ad is closed. - /// - /// - Parameter interstitialController: The controller managing the interstitial ad. - @objc public override func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { - callDelegate_rewardedAdUserDidEarnReward() - super.interstitialControllerDidCloseAd(interstitialController) + /// Adds context data for a specified key. + /// - Parameters: + /// - data: The data to add. + /// - key: The key associated with the data. + @available(*, deprecated, message: "This method is deprecated. Please, use addExtData method instead.") + public func addContextData(_ data: String, forKey key: String) { + addExtData(key: key, value: data) } - - // MARK: - Protected overrides - /// Called when the ad unit receives an ad. - @objc public override func callDelegate_didReceiveAd() { - if let delegate = self.delegate as? RewardedAdUnitDelegate { - delegate.rewardedAdDidReceiveAd?(self) - } + /// Updates context data for a specified key. + /// - Parameters: + /// - data: A set of data to update. + /// - key: The key associated with the data. + @available(*, deprecated, message: "This method is deprecated. Please, use updateExtData method instead.") + public func updateContextData(_ data: Set, forKey key: String) { + updateExtData(key: key, value: data) } - - /// Called when the ad unit fails to receive an ad. - /// - /// - Parameter error: The error describing the failure. - @objc public override func callDelegate_didFailToReceiveAd(with error: Error?) { - if let delegate = self.delegate as? RewardedAdUnitDelegate { - delegate.rewardedAd?(self, didFailToReceiveAdWithError: error) - } + + /// Removes context data for a specified key. + /// - Parameter key: The key associated with the data to remove. + @available(*, deprecated, message: "This method is deprecated. Please, use removeExtData method instead.") + public func removeContextDate(forKey key: String) { + removeExtData(forKey: key) } - /// Called when the ad unit will present an ad. - @objc public override func callDelegate_willPresentAd() { - if let delegate = self.delegate as? RewardedAdUnitDelegate { - delegate.rewardedAdWillPresentAd?(self) - } + /// Clears all context data. + @available(*, deprecated, message: "This method is deprecated. Please, use clearExtData method instead.") + public func clearContextData() { + clearExtData() } - - /// Called when the ad unit dismisses an ad. - @objc public override func callDelegate_didDismissAd() { - if let delegate = self.delegate as? RewardedAdUnitDelegate { - delegate.rewardedAdDidDismissAd?(self) - } + + /// Adds ext data. + /// - Parameters: + /// - key: The key for the data. + /// - value: The value for the data. + public func addExtData(key: String, value: String) { + adUnitConfig.addExtData(key: key, value: value) } - - /// Called when the ad unit will leave the application. - @objc public override func callDelegate_willLeaveApplication() { - if let delegate = self.delegate as? RewardedAdUnitDelegate { - delegate.rewardedAdWillLeaveApplication?(self) - } + + /// Updates ext data. + /// - Parameters: + /// - key: The key for the data. + /// - value: The value for the data. + public func updateExtData(key: String, value: Set) { + adUnitConfig.updateExtData(key: key, value: value) } - - /// Called when the ad unit is clicked. - @objc public override func callDelegate_didClickAd() { - if let delegate = self.delegate as? RewardedAdUnitDelegate { - delegate.rewardedAdDidClickAd?(self) - } + + /// Removes ext data. + /// - Parameters: + /// - key: The key for the data. + public func removeExtData(forKey: String) { + adUnitConfig.removeExtData(for: forKey) } - /// Returns whether the event handler is ready. - /// - /// - Returns: A boolean indicating if the event handler is ready. - @objc public override func callEventHandler_isReady() -> Bool { - if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { - return eventHandler.isReady - } else { - return false - } + /// Clears ext data. + public func clearExtData() { + adUnitConfig.clearExtData() } - - /// Sets the loading delegate for the event handler. - /// - /// - Parameter loadingDelegate: The loading delegate to set. - @objc public override func callEventHandler_setLoadingDelegate(_ loadingDelegate: NSObject?) { - if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { - eventHandler.loadingDelegate = loadingDelegate as? RewardedEventLoadingDelegate + + // MARK: - Ext keywords (imp[].ext.keywords) + + /// Adds a context keyword. + /// - Parameter newElement: The keyword to add. + @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeyword method instead.") + public func addContextKeyword(_ newElement: String) { + addExtKeyword(newElement) + } + + /// Adds a set of context keywords. + /// - Parameter newElements: A set of keywords to add. + @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeywords method instead.") + public func addContextKeywords(_ newElements: Set) { + addExtKeywords(newElements) + } + + /// Removes a context keyword. + /// - Parameter element: The keyword to remove. + @available(*, deprecated, message: "This method is deprecated. Please, use removeExtKeyword method instead.") + public func removeContextKeyword(_ element: String) { + removeExtKeyword(element) + } + + /// Clears all context keywords. + @available(*, deprecated, message: "This method is deprecated. Please, use clearExtKeywords method instead.") + public func clearContextKeywords() { + clearExtKeywords() + } + + /// Adds an extended keyword. + /// - Parameter newElement: The keyword to be added. + public func addExtKeyword(_ newElement: String) { + adUnitConfig.addExtKeyword(newElement) + } + + /// Adds multiple extended keywords. + /// - Parameter newElements: A set of keywords to be added. + public func addExtKeywords(_ newElements: Set) { + adUnitConfig.addExtKeywords(newElements) + } + + /// Removes an extended keyword. + /// - Parameter element: The keyword to be removed. + public func removeExtKeyword(_ element: String) { + adUnitConfig.removeExtKeyword(element) + } + + /// Clears all extended keywords. + public func clearExtKeywords() { + adUnitConfig.clearExtKeywords() + } + + // MARK: - App Content (app.content.data) + + /// Sets the app content data. + /// - Parameter appContent: The app content data. + public func setAppContent(_ appContent: PBMORTBAppContent) { + adUnitConfig.setAppContent(appContent) + } + + /// Clears the app content data. + public func clearAppContent() { + adUnitConfig.clearAppContent() + } + + /// Adds app content data objects. + /// - Parameter dataObjects: The data objects to be added. + public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { + adUnitConfig.addAppContentData(dataObjects) + } + + /// Removes an app content data object. + /// - Parameter dataObject: The data object to be removed. + public func removeAppContentDataObject(_ dataObject: PBMORTBContentData) { + adUnitConfig.removeAppContentData(dataObject) + } + + /// Clears all app content data objects. + public func clearAppContentDataObjects() { + adUnitConfig.clearAppContentData() + } + + // MARK: - User Data (user.data) + + /// Adds user data objects. + /// - Parameter userDataObjects: The user data objects to be added. + public func addUserData(_ userDataObjects: [PBMORTBContentData]) { + adUnitConfig.addUserData(userDataObjects) + } + + /// Removes a user data object. + /// - Parameter userDataObject: The user data object to be removed. + public func removeUserData(_ userDataObject: PBMORTBContentData) { + adUnitConfig.removeUserData(userDataObject) + } + + /// Clears all user data objects. + public func clearUserData() { + adUnitConfig.clearUserData() + } + + // MARK: - Internal methods + + func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { + baseAdUnit.interstitialControllerDidCloseAd(interstitialController) + } + + func callDelegate_didReceiveAd() { + delegate?.rewardedAdDidReceiveAd?(self) + } + + func callDelegate_didFailToReceiveAd(with error: Error?) { + delegate?.rewardedAd?(self, didFailToReceiveAdWithError: error) + } + + func callDelegate_willPresentAd() { + delegate?.rewardedAdWillPresentAd?(self) + } + + func callDelegate_didDismissAd() { + delegate?.rewardedAdDidDismissAd?(self) + } + + func callDelegate_willLeaveApplication() { + delegate?.rewardedAdWillLeaveApplication?(self) + } + + func callDelegate_didClickAd() { + delegate?.rewardedAdDidClickAd?(self) + } + + func callEventHandler_isReady() -> Bool { + (eventHandler as? RewardedEventHandlerProtocol)?.isReady ?? false + } + + func callEventHandler_setLoadingDelegate(_ loadingDelegate: NSObject?) { + if let eventHandler = eventHandler as? RewardedEventHandlerProtocol { + eventHandler.loadingDelegate = loadingDelegate as? InterstitialEventLoadingDelegate } } - - /// Sets the interaction delegate for the event handler. - @objc public override func callEventHandler_setInteractionDelegate() { - if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { - eventHandler.interactionDelegate = self + + func callEventHandler_setInteractionDelegate() { + if let eventHandler = eventHandler as? RewardedEventHandlerProtocol { + eventHandler.interactionDelegate = baseAdUnit } } - - /// Requests an ad with the given bid response. - /// - /// - Parameter bidResponse: The bid response to use for the ad request. - @objc public override func callEventHandler_requestAd(with bidResponse: BidResponse?) { - if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { + + func callEventHandler_requestAd(with bidResponse: BidResponse?) { + if let eventHandler = eventHandler as? RewardedEventHandlerProtocol { eventHandler.requestAd(with: bidResponse) } } - - /// Shows the ad from the specified view controller. - /// - /// - Parameter controller: The view controller from which to present the ad. - @objc public override func callEventHandler_show(from controller: UIViewController?) { - if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { + + func callEventHandler_show(from controller: UIViewController?) { + if let eventHandler = eventHandler as? RewardedEventHandlerProtocol { eventHandler.show(from: controller) } } - - /// Tracks the impression for the ad. - @objc public override func callEventHandler_trackImpression() { - if let eventHandler = self.eventHandler as? RewardedEventHandlerProtocol { + + func callEventHandler_trackImpression() { + if let eventHandler = eventHandler as? RewardedEventHandlerProtocol { eventHandler.trackImpression?() } } - // MARK: - Private helpers - - /// Calls the delegate method to notify that the user has earned a reward. - func callDelegate_rewardedAdUserDidEarnReward() { - if let delegate = self.delegate as? RewardedAdUnitDelegate { - delegate.rewardedAdUserDidEarnReward?(self) - } + func callDelegate_rewardedAdUserDidEarnReward(reward: PrebidReward) { + delegate?.rewardedAdUserDidEarnReward?(self, reward: reward) } } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnitDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnitDelegate.swift index 247ed78f2..2d2251227 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnitDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnitDelegate.swift @@ -23,7 +23,7 @@ import Foundation @objc optional func rewardedAdDidReceiveAd(_ rewardedAd: RewardedAdUnit) /// Called when user is able to receive a reward from the app - @objc optional func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit) + @objc optional func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit, reward: PrebidReward) /// Called when the load process fails to produce a viable ad @objc optional func rewardedAd(_ rewardedAd: RewardedAdUnit, diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedConfig.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedConfig.swift new file mode 100644 index 000000000..cabdbefbb --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedConfig.swift @@ -0,0 +1,81 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import Foundation + +@objc(PBMRewardedConfig) @objcMembers +public class RewardedConfig: NSObject { + + // MARK: - Reward + + public var reward: PrebidReward? { + PrebidReward(with: ortbRewarded?.reward) + } + + // MARK: - Banner + + public var bannerTime: NSNumber? { + ortbRewarded?.completion?.banner?.time + } + + public var bannerEvent: String? { + ortbRewarded?.completion?.banner?.event + } + + // MARK: - Video + + public var videoTime: NSNumber? { + ortbRewarded?.completion?.video?.time + } + + public var videoPlaybackevent: String? { + ortbRewarded?.completion?.video?.playbackevent + } + + + // MARK: - Endcard + + public var endcardTime: NSNumber? { + ortbRewarded?.completion?.video?.endcard?.time + } + + public var endcardEvent: String? { + ortbRewarded?.completion?.video?.endcard?.event + } + + // MARK: - Close + + public var closeAction: String? { + ortbRewarded?.close?.action + } + + public var postRewardTime: NSNumber? { + ortbRewarded?.close?.postrewardtime + } + + // MARK: - Default Values + + /// The timeout duration for rewarded completion, measured in seconds. + public let defaultCompletionTime: NSNumber = 120 + + /// The playback event when the SDK should send a signal to the application that the user has earned the reward + public let defaultVideoPlaybackEvent = "complete" + + private let ortbRewarded: PBMORTBRewardedConfiguration? + + init(ortbRewarded: PBMORTBRewardedConfiguration?) { + self.ortbRewarded = ortbRewarded + } +} diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventHandlerProtocol.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventHandlerProtocol.swift index f44615211..e5590aedd 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventHandlerProtocol.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventHandlerProtocol.swift @@ -21,7 +21,7 @@ import Foundation @objc public protocol RewardedEventHandlerProtocol: PBMInterstitialAd { /// Delegate for custom event handler to inform the PBM SDK about the events related to the ad server communication. - weak var loadingDelegate: RewardedEventLoadingDelegate? { get set } + weak var loadingDelegate: InterstitialEventLoadingDelegate? { get set } /// Delegate for custom event handler to inform the PBM SDK about the events related to the user's interaction with the ad. weak var interactionDelegate: RewardedEventInteractionDelegate? { get set } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventInteractionDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventInteractionDelegate.swift index f2eaa6b58..a930e6633 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventInteractionDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedEventInteractionDelegate.swift @@ -16,10 +16,7 @@ import Foundation @objc public protocol RewardedEventInteractionDelegate: InterstitialEventInteractionDelegate { - - /*! - @abstract Call this when the ad server SDK decides the use has earned reward - */ - func userDidEarnReward(_ reward: NSObject?) + + /// Call this when the ad server SDK decides the user has earned reward + func userDidEarnReward(_ reward: PrebidReward?) } - diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationRewardedAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationRewardedAdUnit.swift index 3b8179710..cec66e397 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationRewardedAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationRewardedAdUnit.swift @@ -29,7 +29,7 @@ public class MediationRewardedAdUnit : MediationBaseInterstitialAdUnit { public override init(configId: String, mediationDelegate: PrebidMediationDelegate) { super.init(configId: configId, mediationDelegate: mediationDelegate) - adUnitConfig.adConfiguration.isOptIn = true + adUnitConfig.adConfiguration.isRewarded = true adUnitConfig.adFormats = [.video] } } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/PBMStandaloneSDK/EventHandlers/RewardedEventHandlerStandalone.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/PBMStandaloneSDK/EventHandlers/RewardedEventHandlerStandalone.swift index 54927562b..a1ba781cf 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/PBMStandaloneSDK/EventHandlers/RewardedEventHandlerStandalone.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/PBMStandaloneSDK/EventHandlers/RewardedEventHandlerStandalone.swift @@ -18,7 +18,7 @@ import UIKit public class RewardedEventHandlerStandalone: NSObject, RewardedEventHandlerProtocol { - public weak var loadingDelegate: RewardedEventLoadingDelegate? + public weak var loadingDelegate: InterstitialEventLoadingDelegate? public weak var interactionDelegate: RewardedEventInteractionDelegate? public var isReady: Bool { diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialController.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialController.swift index b4108bcdf..b5b49e46b 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialController.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialController.swift @@ -13,7 +13,6 @@  limitations under the License.  */ -import Foundation import UIKit public class InterstitialController: NSObject, PBMAdViewManagerDelegate { @@ -25,10 +24,10 @@ public class InterstitialController: NSObject, PBMAdViewManagerDelegate { set { adConfiguration.adFormats = newValue } } - /// Sets a video interstitial ad unit as an opt-in video - @objc public var isOptIn: Bool { - get { adConfiguration.adConfiguration.isOptIn } - set { adConfiguration.adConfiguration.isOptIn = newValue } + /// Sets an ad unit as a rewarded + @objc public var isRewarded: Bool { + get { adConfiguration.adConfiguration.isRewarded } + set { adConfiguration.adConfiguration.isRewarded = newValue } } @objc public var videoControlsConfig: VideoControlsConfiguration { @@ -61,6 +60,7 @@ public class InterstitialController: NSObject, PBMAdViewManagerDelegate { @objc public convenience init(bid: Bid, configId: String) { let adConfig = AdUnitConfig(configId: configId) adConfig.adConfiguration.isInterstitialAd = true + adConfig.adConfiguration.isRewarded = bid.rewardedConfig != nil self.init(bid: bid, adConfiguration: adConfig) } @@ -69,9 +69,15 @@ public class InterstitialController: NSObject, PBMAdViewManagerDelegate { self.renderer = PrebidMobilePluginRegister.shared.getPluginForPreferredRenderer(bid: bid) - let connection: PrebidServerConnectionProtocol = PrebidServerConnection.shared - self.renderer?.createInterstitialController?(bid: bid, adConfiguration: adConfiguration, connection: connection, adViewManagerDelegate: self, videoControlsConfig: videoControlsConfig) + + self.renderer?.createInterstitialController?( + bid: bid, + adConfiguration: adConfiguration, + connection: connection, + adViewManagerDelegate: self, + videoControlsConfig: videoControlsConfig + ) } @objc public func show() { @@ -144,6 +150,12 @@ public class InterstitialController: NSObject, PBMAdViewManagerDelegate { } } + @objc public func adDidSendRewardedEvent() { + if let delegate = interactionDelegate { + delegate.trackUserReward?(self, PrebidReward(with: bid.rewardedConfig?.reward)) + } + } + @objc public func interstitialDisplayProperties() -> PBMInterstitialDisplayProperties { displayProperties } @@ -173,7 +185,7 @@ public class InterstitialController: NSObject, PBMAdViewManagerDelegate { modalManagerDelegate: nil) adViewManager?.adViewManagerDelegate = self adViewManager?.adConfiguration.isInterstitialAd = true - adViewManager?.adConfiguration.isOptIn = adConfiguration.adConfiguration.isOptIn + adViewManager?.adConfiguration.isRewarded = adConfiguration.adConfiguration.isRewarded adViewManager?.handleExternalTransaction(transaction) } } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerInteractionDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerInteractionDelegate.swift index eac1e8c15..12655b8b3 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerInteractionDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerInteractionDelegate.swift @@ -13,7 +13,6 @@  limitations under the License.  */ -import Foundation import UIKit @objc public protocol InterstitialControllerInteractionDelegate: NSObjectProtocol { @@ -27,4 +26,6 @@ import UIKit func interstitialControllerDidComplete(_ interstitialController: InterstitialController) func viewControllerForModalPresentation(fromInterstitialController: InterstitialController) -> UIViewController? + + @objc optional func trackUserReward(_ interstitialController: InterstitialController, _ reward: PrebidReward) } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidRenderer.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidRenderer.swift index c4eeb847b..12576bcf7 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidRenderer.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidRenderer.swift @@ -74,9 +74,6 @@ public class PrebidRenderer: NSObject, PrebidMobilePluginRenderer { adViewManager?.handleExternalTransaction(transaction) } - - - public func createInterstitialController(bid: Bid, adConfiguration: AdUnitConfig, connection: PrebidServerConnectionProtocol, adViewManagerDelegate adViewDelegate: InterstitialController?, @@ -88,6 +85,8 @@ public class PrebidRenderer: NSObject, PrebidMobilePluginRenderer { adConfiguration.adConfiguration.winningBidAdFormat = bid.adFormat videoControlsConfig?.initialize(with: bid.videoAdConfiguration) + adConfiguration.adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: bid.rewardedConfig) + // This part is dedicating to test server-side ad configurations. // Need to be removed when ext.prebid.passthrough will be available. #if DEBUG diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift index 68cbf069a..7ded2a488 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift @@ -302,16 +302,15 @@ public class AdUnitConfig: NSObject, NSCopying { let clone = AdUnitConfig(configId: self.configId, size: self.adSize) clone.adFormats = self.adFormats - clone.adConfiguration.adFormats = self.adConfiguration.adFormats - clone.adConfiguration.isInterstitialAd = self.adConfiguration.isInterstitialAd - clone.adConfiguration.isOptIn = self.adConfiguration.isOptIn clone.nativeAdConfiguration = self.nativeAdConfiguration - clone.adConfiguration.bannerParameters = self.adConfiguration.bannerParameters - clone.adConfiguration.videoParameters = self.adConfiguration.videoParameters - clone.adConfiguration.videoControlsConfig = self.adConfiguration.videoControlsConfig clone.sizes = sizes - clone.refreshInterval = self.refreshInterval + clone.adSize = adSize clone.minSizePerc = self.minSizePerc + clone.adPosition = self.adPosition + clone.additionalSizes = self.additionalSizes + clone.refreshInterval = self.refreshInterval + clone.gpid = self.gpid + clone.ortbConfig = self.ortbConfig clone.extensionData = self.extensionData.merging(clone.extensionData) { $1 } clone.appContent = self.appContent clone.extKeywords = self.extKeywords @@ -319,6 +318,27 @@ public class AdUnitConfig: NSObject, NSCopying { clone.adPosition = self.adPosition clone.pbAdSlot = self.pbAdSlot + clone.adConfiguration.rewardedConfig = self.adConfiguration.rewardedConfig + clone.adConfiguration.winningBidAdFormat = self.adConfiguration.winningBidAdFormat + clone.adConfiguration.adFormats = self.adConfiguration.adFormats + clone.adConfiguration.isOriginalAPI = self.adConfiguration.isOriginalAPI + clone.adConfiguration.size = self.adConfiguration.size + clone.adConfiguration.isBuiltInVideo = self.adConfiguration.isBuiltInVideo + clone.adConfiguration.isInterstitialAd = self.adConfiguration.isInterstitialAd + clone.adConfiguration.isRewarded = self.adConfiguration.isRewarded + clone.adConfiguration.forceInterstitialPresentation = self.adConfiguration.forceInterstitialPresentation + clone.adConfiguration.interstitialLayout = self.adConfiguration.interstitialLayout + clone.nativeAdConfiguration = self.nativeAdConfiguration + clone.adConfiguration.bannerParameters = self.adConfiguration.bannerParameters + clone.adConfiguration.videoParameters = self.adConfiguration.videoParameters + clone.adConfiguration.videoControlsConfig = self.adConfiguration.videoControlsConfig + clone.adConfiguration.clickHandlerOverride = self.adConfiguration.clickHandlerOverride + clone.adConfiguration.autoRefreshDelay = self.adConfiguration.autoRefreshDelay + clone.adConfiguration.pollFrequency = self.adConfiguration.pollFrequency + clone.adConfiguration.viewableArea = self.adConfiguration.viewableArea + clone.adConfiguration.viewableDuration = self.adConfiguration.viewableDuration + clone.adConfiguration.ortbConfig = self.adConfiguration.ortbConfig + return clone } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/Bid.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/Bid.swift index da3b7e409..98f75cfe3 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/Bid.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/Bid.swift @@ -87,6 +87,10 @@ public class Bid: NSObject { } #endif + public var rewardedConfig: PBMORTBRewardedConfiguration? { + bid.ext.prebid?.passthrough?.filter { $0.type == "prebidmobilesdk" }.first?.rewardedConfiguration + } + /// Returns YES if this bid is intented for display. @objc public var isWinning: Bool { guard let targetingInfo = self.targetingInfo else { diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBExtPrebidPassthrough.h b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBExtPrebidPassthrough.h index 9ac25c880..67781fdc4 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBExtPrebidPassthrough.h +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBExtPrebidPassthrough.h @@ -18,6 +18,7 @@ @class PBMORTBAdConfiguration; @class PBMORTBSDKConfiguration; +@class PBMORTBRewardedConfiguration; NS_ASSUME_NONNULL_BEGIN @@ -29,6 +30,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, nullable) PBMORTBSDKConfiguration *sdkConfiguration; +@property (nonatomic, strong, nullable) PBMORTBRewardedConfiguration *rewardedConfiguration; + @end NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBExtPrebidPassthrough.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBExtPrebidPassthrough.m index 871f50d50..8655c37a2 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBExtPrebidPassthrough.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBExtPrebidPassthrough.m @@ -16,6 +16,7 @@ #import "PBMORTBExtPrebidPassthrough.h" #import "PBMORTBAdConfiguration.h" #import "PBMORTBSDKConfiguration.h" +#import "PBMORTBRewardedConfiguration.h" @implementation PBMORTBExtPrebidPassthrough @@ -38,6 +39,12 @@ - (instancetype)initWithJsonDictionary:(PBMJsonDictionary *)jsonDictionary { _sdkConfiguration = [[PBMORTBSDKConfiguration alloc] initWithJsonDictionary:sdkConfigDic]; } + PBMJsonDictionary * const rewardedConfigDic = jsonDictionary[@"rwdd"]; + + if (rewardedConfigDic) { + _rewardedConfiguration = [[PBMORTBRewardedConfiguration alloc] initWithJsonDictionary:rewardedConfigDic]; + } + return self; } @@ -50,6 +57,8 @@ - (PBMJsonDictionary *)toJsonDictionary { ret[@"sdkconfiguration"] = [self.sdkConfiguration toJsonDictionary]; + ret[@"rwdd"] = [self.rewardedConfiguration toJsonDictionary]; + [ret pbmRemoveEmptyVals]; return ret; diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedClose.h b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedClose.h new file mode 100644 index 000000000..63ca40ca4 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedClose.h @@ -0,0 +1,35 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBAbstract.h" +#import "PBMORTBAbstract+Protected.h" + +NS_ASSUME_NONNULL_BEGIN + +/// Describes the close behavior. How should the SDK manage the ad when it is encountered as viewed +@interface PBMORTBRewardedClose : PBMORTBAbstract + +/// The time interval in seconds passed after the reward event when SDK should close the interstitial +@property (nonatomic, strong, nullable) NSNumber *postrewardtime; + +/// The action that SDK should do. +/// Available options: +/// - autoclose - close the interstitial; +/// - closebutton - show the close button. +@property (nonatomic, strong, nullable) NSString *action; + +@end + +NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedClose.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedClose.m new file mode 100644 index 000000000..e319e875d --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedClose.m @@ -0,0 +1,42 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBRewardedClose.h" + +@implementation PBMORTBRewardedClose + +- (instancetype)initWithJsonDictionary:(PBMJsonDictionary *)jsonDictionary { + if (!(self = [self init])) { + return nil; + } + + _postrewardtime = [jsonDictionary[@"postrewardtime"] nullToNil]; + _action = [jsonDictionary[@"action"] nullToNil]; + + return self; +} + +- (PBMJsonDictionary *)toJsonDictionary { + PBMMutableJsonDictionary * const ret = [[PBMMutableJsonDictionary alloc] init]; + + ret[@"postrewardtime"] = self.postrewardtime; + ret[@"action"] = self.action; + + [ret pbmRemoveEmptyVals]; + + return ret; +} + +@end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletion.h b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletion.h new file mode 100644 index 000000000..3d385ee09 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletion.h @@ -0,0 +1,33 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBAbstract.h" +#import "PBMORTBAbstract+Protected.h" + +@class PBMORTBRewardedCompletionBanner; +@class PBMORTBRewardedCompletionVideo; + +NS_ASSUME_NONNULL_BEGIN + +/// Describes the condition when the SDK should send a signal to the application that the user has earned the reward. +@interface PBMORTBRewardedCompletion : PBMORTBAbstract + +@property (nonatomic, strong, nullable) PBMORTBRewardedCompletionBanner * banner; + +@property (nonatomic, strong, nullable) PBMORTBRewardedCompletionVideo * video; + +@end + +NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletion.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletion.m new file mode 100644 index 000000000..2cbe95e79 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletion.m @@ -0,0 +1,45 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBRewardedCompletion.h" +#import "PBMORTBRewardedCompletionBanner.h" +#import "PBMORTBRewardedCompletionVideo.h" + +@implementation PBMORTBRewardedCompletion + +- (instancetype)initWithJsonDictionary:(PBMJsonDictionary *)jsonDictionary { + if (!(self = [self init])) { + return nil; + } + + _banner = [[PBMORTBRewardedCompletionBanner alloc] initWithJsonDictionary:[jsonDictionary[@"banner"] nullToNil]]; + _video = [[PBMORTBRewardedCompletionVideo alloc] initWithJsonDictionary:[jsonDictionary[@"video"] nullToNil]]; + + return self; +} + +- (PBMJsonDictionary *)toJsonDictionary { + PBMMutableJsonDictionary * const ret = [[PBMMutableJsonDictionary alloc] init]; + + ret[@"banner"] = [self.banner toJsonDictionary]; + ret[@"video"] = [self.video toJsonDictionary]; + + [ret pbmRemoveEmptyVals]; + + return ret; +} + + +@end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionBanner.h b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionBanner.h new file mode 100644 index 000000000..18f48bd7f --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionBanner.h @@ -0,0 +1,31 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBAbstract.h" +#import "PBMORTBAbstract+Protected.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PBMORTBRewardedCompletionBanner : PBMORTBAbstract + +/// The period of time that the ad is on the screen and the user earns a reward +@property (nonatomic, strong, nullable) NSNumber *time; + +/// The URL with a custom schema that will be sent by the creative and should be caught by the SDK +@property (nonatomic, strong, nullable) NSString *event; + +@end + +NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionBanner.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionBanner.m new file mode 100644 index 000000000..0bdf15557 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionBanner.m @@ -0,0 +1,42 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBRewardedCompletionBanner.h" + +@implementation PBMORTBRewardedCompletionBanner + +- (instancetype)initWithJsonDictionary:(PBMJsonDictionary *)jsonDictionary { + if (!(self = [self init])) { + return nil; + } + + _time = [jsonDictionary[@"time"] nullToNil]; + _event = [jsonDictionary[@"event"] nullToNil]; + + return self; +} + +- (PBMJsonDictionary *)toJsonDictionary { + PBMMutableJsonDictionary * const ret = [[PBMMutableJsonDictionary alloc] init]; + + ret[@"time"] = self.time; + ret[@"event"] = self.event; + + [ret pbmRemoveEmptyVals]; + + return ret; +} + +@end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideo.h b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideo.h new file mode 100644 index 000000000..b499da9d6 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideo.h @@ -0,0 +1,37 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBAbstract.h" +#import "PBMORTBAbstract+Protected.h" + +#import "PBMORTBRewardedCompletionVideoEndcard.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PBMORTBRewardedCompletionVideo : PBMORTBAbstract + +/// The period of time that the ad is on the screen and the user earns a reward +@property (nonatomic, strong, nullable) NSNumber *time; + +/// The playback part when the user earns a reward +@property (nonatomic, strong, nullable) NSString *playbackevent; + +/// Endcard completion criteria +@property (nonatomic, strong, nullable) PBMORTBRewardedCompletionVideoEndcard *endcard; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideo.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideo.m new file mode 100644 index 000000000..d8b21a1d9 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideo.m @@ -0,0 +1,44 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBRewardedCompletionVideo.h" + +@implementation PBMORTBRewardedCompletionVideo + +- (instancetype)initWithJsonDictionary:(PBMJsonDictionary *)jsonDictionary { + if (!(self = [self init])) { + return nil; + } + + _time = [jsonDictionary[@"time"] nullToNil]; + _playbackevent = [jsonDictionary[@"playbackevent"] nullToNil]; + _endcard = [[PBMORTBRewardedCompletionVideoEndcard alloc] initWithJsonDictionary:[jsonDictionary[@"endcard"] nullToNil]]; + + return self; +} + +- (PBMJsonDictionary *)toJsonDictionary { + PBMMutableJsonDictionary * const ret = [[PBMMutableJsonDictionary alloc] init]; + + ret[@"time"] = self.time; + ret[@"playbackevent"] = self.playbackevent; + ret[@"endcard"] = [self.endcard toJsonDictionary]; + + [ret pbmRemoveEmptyVals]; + + return ret; +} + +@end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideoEndcard.h b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideoEndcard.h new file mode 100644 index 000000000..12eb0bb78 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideoEndcard.h @@ -0,0 +1,31 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBAbstract.h" +#import "PBMORTBAbstract+Protected.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface PBMORTBRewardedCompletionVideoEndcard : PBMORTBAbstract + +/// The period of time that the ad is on the screen and the user earns a reward +@property (nonatomic, strong, nullable) NSNumber *time; + +/// The URL with a custom schema that will be sent by the creative and should be caught by the SDK +@property (nonatomic, strong, nullable) NSString *event; + +@end + +NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideoEndcard.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideoEndcard.m new file mode 100644 index 000000000..bfdd6740f --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedCompletionVideoEndcard.m @@ -0,0 +1,42 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBRewardedCompletionVideoEndcard.h" + +@implementation PBMORTBRewardedCompletionVideoEndcard + +- (instancetype)initWithJsonDictionary:(PBMJsonDictionary *)jsonDictionary { + if (!(self = [self init])) { + return nil; + } + + _time = [jsonDictionary[@"time"] nullToNil]; + _event = [jsonDictionary[@"event"] nullToNil]; + + return self; +} + +- (PBMJsonDictionary *)toJsonDictionary { + PBMMutableJsonDictionary * const ret = [[PBMMutableJsonDictionary alloc] init]; + + ret[@"time"] = self.time; + ret[@"event"] = self.event; + + [ret pbmRemoveEmptyVals]; + + return ret; +} + +@end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedConfiguration.h b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedConfiguration.h new file mode 100644 index 000000000..ec6ec7116 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedConfiguration.h @@ -0,0 +1,38 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBAbstract.h" +#import "PBMORTBAbstract+Protected.h" + +@class PBMORTBRewardedReward; +@class PBMORTBRewardedCompletion; +@class PBMORTBRewardedClose; + +NS_ASSUME_NONNULL_BEGIN + +@interface PBMORTBRewardedConfiguration : PBMORTBAbstract + +/// Metadata provided by the publisher to describe the reward. +@property (nonatomic, strong, nullable) PBMORTBRewardedReward * reward; + +/// Describes the condition when the SDK should send a signal to the application that the user has earned the reward. +@property (nonatomic, strong, nullable) PBMORTBRewardedCompletion * completion; + +/// Describes the close behavior. How should the SDK manage the ad when it is encountered as viewed. +@property (nonatomic, strong, nullable) PBMORTBRewardedClose * close; + +@end + +NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedConfiguration.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedConfiguration.m new file mode 100644 index 000000000..cd7ce4721 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedConfiguration.m @@ -0,0 +1,48 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBRewardedConfiguration.h" + +#import "PBMORTBRewardedReward.h" +#import "PBMORTBRewardedCompletion.h" +#import "PBMORTBRewardedClose.h" + +@implementation PBMORTBRewardedConfiguration + +- (instancetype)initWithJsonDictionary:(PBMJsonDictionary *)jsonDictionary { + if (!(self = [self init])) { + return nil; + } + + _reward = [[PBMORTBRewardedReward alloc] initWithJsonDictionary:[jsonDictionary[@"reward"] nullToNil]]; + _completion = [[PBMORTBRewardedCompletion alloc] initWithJsonDictionary:[jsonDictionary[@"completion"] nullToNil]]; + _close = [[PBMORTBRewardedClose alloc] initWithJsonDictionary:[jsonDictionary[@"close"] nullToNil]]; + + return self; +} + +- (PBMJsonDictionary *)toJsonDictionary { + PBMMutableJsonDictionary * const ret = [[PBMMutableJsonDictionary alloc] init]; + + ret[@"reward"] = [self.reward toJsonDictionary]; + ret[@"completion"] = [self.completion toJsonDictionary]; + ret[@"close"] = [self.close toJsonDictionary]; + + [ret pbmRemoveEmptyVals]; + + return ret; +} + +@end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedReward.h b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedReward.h new file mode 100644 index 000000000..2ef66f05d --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedReward.h @@ -0,0 +1,35 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBAbstract.h" +#import "PBMORTBAbstract+Protected.h" + +NS_ASSUME_NONNULL_BEGIN + +/// Metadata provided by the publisher to describe the reward +@interface PBMORTBRewardedReward : PBMORTBAbstract + +/// Type of the reward +@property (nonatomic, strong, nullable) NSString *type; + +/// Amount of reward +@property (nonatomic, strong, nullable) NSNumber *count; + +/// For the future extensions +@property (nonatomic, strong, nullable) PBMJsonDictionary *ext; + +@end + +NS_ASSUME_NONNULL_END diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedReward.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedReward.m new file mode 100644 index 000000000..8fb46ef99 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBRewardedReward.m @@ -0,0 +1,44 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "PBMORTBRewardedReward.h" + +@implementation PBMORTBRewardedReward + +- (instancetype)initWithJsonDictionary:(PBMJsonDictionary *)jsonDictionary { + if (!(self = [self init])) { + return nil; + } + + _type = [jsonDictionary[@"type"] nullToNil]; + _count = [jsonDictionary[@"count"] nullToNil]; + _ext = [jsonDictionary[@"ext"] nullToNil]; + + return self; +} + +- (PBMJsonDictionary *)toJsonDictionary { + PBMMutableJsonDictionary * const ret = [[PBMMutableJsonDictionary alloc] init]; + + ret[@"type"] = self.type; + ret[@"count"] = self.count; + ret[@"ext"] = self.ext; + + [ret pbmRemoveEmptyVals]; + + return ret; +} + +@end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m index edae54fd6..67e76ea70 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m @@ -185,9 +185,13 @@ - (void)buildBidRequest:(nonnull PBMORTBBidRequest *)bidRequest { nextImp.impID = [NSUUID UUID].UUIDString; nextImp.extPrebid.storedRequestID = self.adConfiguration.configId; nextImp.extPrebid.storedAuctionResponse = Prebid.shared.storedAuctionResponse; - nextImp.extPrebid.isRewardedInventory = self.adConfiguration.adConfiguration.isOptIn; nextImp.extGPID = self.adConfiguration.gpid; + nextImp.extPrebid.isRewardedInventory = self.adConfiguration.adConfiguration.isRewarded; + if (self.adConfiguration.adConfiguration.isRewarded) { + nextImp.rewarded = @(1); + } + if ([self.adConfiguration getExtData].count > 0) { nextImp.extData = [self.adConfiguration getExtData].mutableCopy; } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMSafariVCOpener.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMSafariVCOpener.m index 409effa9a..af3e71606 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMSafariVCOpener.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMSafariVCOpener.m @@ -150,28 +150,32 @@ - (BOOL)shouldOpenURLSchemeExternally:(NSString *)strURLscheme { - (BOOL)openClickthroughWithURL:(NSURL *)url viewController:(UIViewController *)viewControllerForPresentingModals { - self.safariViewController = [[SFSafariViewController alloc] initWithURL:url]; - self.safariViewController.delegate = self; - - PBMOpenMeasurementSession * const measurementSession = self.measurementSessionProvider(); - PBMWindowLocker * windowLocker = [[PBMWindowLocker alloc] initWithWindow:viewControllerForPresentingModals.view.window - measurementSession:measurementSession]; - [windowLocker lock]; - - UIViewController * presentingViewController = viewControllerForPresentingModals; - - if (self.modalManager.modalViewController) { - presentingViewController = self.modalManager.modalViewController; - } - - if (self.onWillLoadURLInClickthrough != nil) { - self.onWillLoadURLInClickthrough(); + @try { + self.safariViewController = [[SFSafariViewController alloc] initWithURL:url]; + self.safariViewController.delegate = self; + + PBMOpenMeasurementSession * const measurementSession = self.measurementSessionProvider(); + PBMWindowLocker * windowLocker = [[PBMWindowLocker alloc] initWithWindow:viewControllerForPresentingModals.view.window + measurementSession:measurementSession]; + [windowLocker lock]; + + UIViewController * presentingViewController = viewControllerForPresentingModals; + + if (self.modalManager.modalViewController) { + presentingViewController = self.modalManager.modalViewController; + } + + if (self.onWillLoadURLInClickthrough != nil) { + self.onWillLoadURLInClickthrough(); + } + + [presentingViewController presentViewController:self.safariViewController animated:YES completion:^{ + [windowLocker unlock]; + }]; + } @catch (NSException *exception) { + PBMLogError(@"Error occurred during URL opening: %@", exception.reason); } - [presentingViewController presentViewController:self.safariViewController animated:YES completion:^{ - [windowLocker unlock]; - }]; - return YES; } diff --git a/PrebidMobile/PrebidMobileRendering/Utilities/BackgroundAwareTimer.swift b/PrebidMobile/PrebidMobileRendering/Utilities/BackgroundAwareTimer.swift new file mode 100644 index 000000000..dcfe0acd5 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Utilities/BackgroundAwareTimer.swift @@ -0,0 +1,103 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import UIKit + +/// Timer that stops in background and resumes in foreground. Fires callback on timer completion. +/// Can execute only one task at a time. +@objc(PBMBackgroundAwareTimer) @objcMembers +public class BackgroundAwareTimer: NSObject { + + private(set) var isRunning: Bool + private var remainingTime: TimeInterval + private var startTime: Date? + private var completion: (() -> Void)? + + private var gcdItem: DispatchWorkItem? + + public override init() { + self.remainingTime = 0 + self.isRunning = false + + super.init() + + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationDidEnterBackground), + name: UIApplication.didEnterBackgroundNotification, + object: nil + ) + + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationWillEnterForeground), + name: UIApplication.willEnterForegroundNotification, + object: nil + ) + } + + deinit { + invalidateTimer() + } + + public func startTimer(with interval: TimeInterval, completion: @escaping () -> Void) { + guard !isRunning else { return } + + startTime = Date() + remainingTime = interval + self.completion = completion + + let gcdItem = DispatchWorkItem { [weak self] in + guard let self = self else { return } + completion() + self.invalidateTimer() + } + + self.gcdItem = gcdItem + + DispatchQueue.main.asyncAfter( + deadline: .now() + remainingTime, + execute: gcdItem + ) + + isRunning = true + } + + public func invalidateTimer() { + gcdItem = nil + completion = nil + isRunning = false + NotificationCenter.default.removeObserver(self) + } + + private func stopTimer() { + guard isRunning else { return } + + gcdItem?.cancel() + gcdItem = nil + remainingTime -= Date().timeIntervalSince(startTime ?? Date()) + isRunning = false + } + + @objc private func applicationDidEnterBackground() { + stopTimer() + } + + @objc private func applicationWillEnterForeground() { + if let completion, !isRunning && remainingTime > 0 { + startTimer(with: remainingTime, completion: completion) + } + } +} diff --git a/PrebidMobileTests/AdUnitTests/PrebidAdUnitTests.swift b/PrebidMobileTests/AdUnitTests/PrebidAdUnitTests.swift index 168240bec..4f1840d40 100644 --- a/PrebidMobileTests/AdUnitTests/PrebidAdUnitTests.swift +++ b/PrebidMobileTests/AdUnitTests/PrebidAdUnitTests.swift @@ -210,7 +210,7 @@ class PrebidAdUnitTests: XCTestCase { // fetchDemand(request:completion) adUnit.fetchDemand(request: request) { _ in XCTAssertTrue(config.adConfiguration.isInterstitialAd) - XCTAssertTrue(config.adConfiguration.isOptIn) + XCTAssertTrue(config.adConfiguration.isRewarded) XCTAssertEqual(config.adPosition, .fullScreen) XCTAssertEqual(config.adConfiguration.videoParameters.placement, .Interstitial) @@ -223,7 +223,7 @@ class PrebidAdUnitTests: XCTestCase { // fetchDemand(adObject:request:completion) adUnit.fetchDemand(adObject: testObject, request: request) { _ in XCTAssertTrue(config.adConfiguration.isInterstitialAd) - XCTAssertTrue(config.adConfiguration.isOptIn) + XCTAssertTrue(config.adConfiguration.isRewarded) XCTAssertEqual(config.adPosition, .fullScreen) XCTAssertEqual(config.adConfiguration.videoParameters.placement, .Interstitial) diff --git a/PrebidMobileTests/PrebidMobileTest-Bridging-Header.h b/PrebidMobileTests/PrebidMobileTest-Bridging-Header.h index 409142ef5..4dc020b83 100644 --- a/PrebidMobileTests/PrebidMobileTest-Bridging-Header.h +++ b/PrebidMobileTests/PrebidMobileTest-Bridging-Header.h @@ -175,3 +175,5 @@ #import "PBMLocationManagerProtocol.h" #import "InternalUserConsentDataManager.h" + +#import "PBMCloseActionManager.h" diff --git a/PrebidMobileTests/RenderingTests/Mocks/MockVideoView.swift b/PrebidMobileTests/RenderingTests/Mocks/MockVideoView.swift new file mode 100644 index 000000000..83733b930 --- /dev/null +++ b/PrebidMobileTests/RenderingTests/Mocks/MockVideoView.swift @@ -0,0 +1,26 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import Foundation +@testable import PrebidMobile + +class MockVideoView: PBMVideoView { + + var mockRequiredVideoDuration: CGFloat = 10 + + override func requiredVideoDuration() -> CGFloat { + mockRequiredVideoDuration + } +} diff --git a/PrebidMobileTests/RenderingTests/TestExtensions/PBMVideoView+pbmTestExtension.h b/PrebidMobileTests/RenderingTests/TestExtensions/PBMVideoView+pbmTestExtension.h index 80e1396f0..975272d0b 100644 --- a/PrebidMobileTests/RenderingTests/TestExtensions/PBMVideoView+pbmTestExtension.h +++ b/PrebidMobileTests/RenderingTests/TestExtensions/PBMVideoView+pbmTestExtension.h @@ -19,9 +19,11 @@ @property (nonatomic, weak, nullable) PBMVideoCreative *creative; @property (nonatomic, strong) PBMAdViewButtonDecorator * _Nonnull skipButtonDecorator; +@property (nonatomic, strong, nonnull) NSNumber * progressBarDuration; - (void)updateControls; - (CGFloat)requiredVideoDuration; - (void)handleSkipDelay:(NSTimeInterval)skipDelay videoDuration:(NSTimeInterval)videoDuration; +- (NSNumber * _Nonnull)calculateProgressBarDuration; @end diff --git a/PrebidMobileTests/RenderingTests/Tests/CloseActionManagerTests.swift b/PrebidMobileTests/RenderingTests/Tests/CloseActionManagerTests.swift new file mode 100644 index 000000000..fe82e4855 --- /dev/null +++ b/PrebidMobileTests/RenderingTests/Tests/CloseActionManagerTests.swift @@ -0,0 +1,40 @@ +/*   Copyright 2018-2023 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import XCTest +@testable import PrebidMobile + +class PBMCloseActionManagerTests: XCTestCase { + + func testGetActionWithCloseButtonDescription() { + let action = PBMCloseActionManager.getActionWithDescription("closebutton") + XCTAssertEqual(action.rawValue, PBMCloseAction.closeButton.rawValue) + } + + func testGetActionWithAutoCloseDescription() { + let action = PBMCloseActionManager.getActionWithDescription( "autoclose") + XCTAssertEqual(action.rawValue, PBMCloseAction.autoClose.rawValue) + } + + func testGetActionWithUnknownDescription() { + let action = PBMCloseActionManager.getActionWithDescription("unknown") + XCTAssertEqual(action.rawValue, PBMCloseAction.unknown.rawValue) + } + + func testGetActionWithEmptyDescription() { + let action = PBMCloseActionManager.getActionWithDescription("") + XCTAssertEqual(action.rawValue, PBMCloseAction.unknown.rawValue) + } +} diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMCreativeFactoryJobTest.swift b/PrebidMobileTests/RenderingTests/Tests/PBMCreativeFactoryJobTest.swift index e2d44b3ee..5b3feaf03 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMCreativeFactoryJobTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMCreativeFactoryJobTest.swift @@ -230,8 +230,8 @@ class PBMCreativeFactoryJobTest: XCTestCase { // banner display let transaction = UtilitiesForTesting.createEmptyTransaction() - var adConfig = AdConfiguration() - var model = PBMCreativeModel(adConfiguration: adConfig) + let adConfig = AdConfiguration() + let model = PBMCreativeModel(adConfiguration: adConfig) let finishedCallback = { (job: PBMCreativeFactoryJob, error: Error?) in } diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMCreativeFactoryTest.swift b/PrebidMobileTests/RenderingTests/Tests/PBMCreativeFactoryTest.swift index 86b7b367e..bc67a6fec 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMCreativeFactoryTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMCreativeFactoryTest.swift @@ -66,6 +66,6 @@ class PBMCreativeFactoryTest: XCTestCase { creativeFactory.start() - waitForExpectations(timeout: 5) + waitForExpectations(timeout: 10) } } diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest.swift b/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest.swift index 2a03549a8..18592996f 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest.swift @@ -13,12 +13,10 @@  limitations under the License.  */ -import Foundation import XCTest - import UIKit -@testable import PrebidMobile +@testable import PrebidMobile class PBMHTMLCreativeTest_PublicAPI: PBMHTMLCreativeTest_Base { @@ -36,46 +34,46 @@ class PBMHTMLCreativeTest_PublicAPI: PBMHTMLCreativeTest_Base { creativeModel: self.mockCreativeModel, transaction:UtilitiesForTesting.createEmptyTransaction(), webView: self.mockWebView, - sdkConfiguration: Prebid.mock + sdkConfiguration: Prebid.mock ) self.htmlCreative.setupView() PBMAssertEq(self.htmlCreative.view, nil) } - + func testSetupViewFailWithVast() { self.mockCreativeModel.html = UtilitiesForTesting.loadFileAsStringFromBundle("prebid_vast_response.xml") self.htmlCreative = MockPBMHTMLCreative( creativeModel: self.mockCreativeModel, transaction:UtilitiesForTesting.createEmptyTransaction(), webView: self.mockWebView, - sdkConfiguration: Prebid.mock + sdkConfiguration: Prebid.mock ) self.htmlCreative.setupView() - + PBMAssertEq(self.htmlCreative.view, nil) } - + func testSetupView_sizesWebViewCorrectly() { self.htmlCreative.setupView() - + let expectedFrame = CGRect(x: 0, y: 0, width: self.mockCreativeModel.width, height: self.mockCreativeModel.height) PBMAssertEq(self.htmlCreative.view?.frame, expectedFrame) } - + func testSetupView_sanitizesHTML() { self.mockCreativeModel.html = "

html content

" let expectedHTML = "\(self.mockCreativeModel.html!)" - + var actualHTML: String? mockWebView.mock_loadHTML = { (html, _, _) in actualHTML = html } - + self.htmlCreative.setupView() self.htmlCreative.display(withRootViewController:mockViewController) - + PBMAssertEq(actualHTML, expectedHTML) } - + func testDisplay_failsWithInvalidView() { //TODO: Update this test to check the log instead @@ -83,13 +81,13 @@ class PBMHTMLCreativeTest_PublicAPI: PBMHTMLCreativeTest_Base { self.htmlCreative.view = nil self.htmlCreative.display(withRootViewController: UIViewController()) PBMAssertEq(self.htmlCreative.view?.constraints, nil) - + //Set view to non-nil. Expect that display will succeed and this constraints will be non-nil. self.htmlCreative.view = UIView() self.htmlCreative.display(withRootViewController: UIViewController()) PBMAssertEq(self.htmlCreative.view?.constraints, []) } - + func testDisplay_triggersImpression() { Prebid.forcedIsViewable = true @@ -114,7 +112,7 @@ class PBMHTMLCreativeTest_PublicAPI: PBMHTMLCreativeTest_Base { } self.htmlCreative.display(withRootViewController: UIViewController()) - + self.waitForExpectations(timeout: 1) } @@ -125,7 +123,7 @@ class PBMHTMLCreativeTest_PublicAPI: PBMHTMLCreativeTest_Base { companionTrackingClickExpectation.fulfill() } } - + self.mockCreativeModel.isCompanionAd = true let mockViewController = MockViewController() self.htmlCreative.display(withRootViewController: mockViewController) @@ -150,7 +148,7 @@ class PBMHTMLCreativeTest_PublicAPI: PBMHTMLCreativeTest_Base { // Session's expectations let expectationSessionStart = self.expectation(description:"expectationSessionStart") let expectationSessionStop = self.expectation(description:"expectationSessionStop") - + measurement.initializeSessionClosure = { session in guard let session = session as? MockMeasurementSession else { XCTFail() @@ -180,19 +178,19 @@ class PBMHTMLCreativeTest_PublicAPI: PBMHTMLCreativeTest_Base { self.transaction = UtilitiesForTesting.createEmptyTransaction() transaction.measurementWrapper = measurement - + self.htmlCreative = MockPBMHTMLCreative( creativeModel: self.mockCreativeModel, transaction:transaction, webView: nil, - sdkConfiguration: Prebid.mock + sdkConfiguration: Prebid.mock ) self.htmlCreative.setupView() self.htmlCreative.display(withRootViewController:mockViewController) transaction.creatives.add(self.htmlCreative!) self.htmlCreative.createOpenMeasurementSession(); - + wait(for: measurementExpectations, timeout: 5, enforceOrder: true); self.htmlCreative = nil @@ -200,7 +198,7 @@ class PBMHTMLCreativeTest_PublicAPI: PBMHTMLCreativeTest_Base { wait(for: [expectationSessionStop], timeout: 1); } - + func testEventClick() { let expectation = self.expectation(description: "PBMTrackingEventClick Expectation") @@ -254,7 +252,7 @@ class PBMHTMLCreativeTest : XCTestCase, PBMCreativeResolutionDelegate, PBMCreati override func setUp() { super.setUp() - + //There should be no network traffic MockServer.shared.reset() MockServer.shared.notFoundRule.mockServerReceivedRequestHandler = { (urlRequest:URLRequest) in @@ -270,7 +268,7 @@ class PBMHTMLCreativeTest : XCTestCase, PBMCreativeResolutionDelegate, PBMCreati let pbmCreativeModel = PBMCreativeModel(adConfiguration: AdConfiguration()) pbmCreativeModel.displayDurationInSeconds = 30 pbmCreativeModel.html = "test html" - + self.htmlCreative = MockPBMHTMLCreative(creativeModel:pbmCreativeModel, transaction:UtilitiesForTesting.createEmptyTransaction()) self.htmlCreative.creativeResolutionDelegate = self self.htmlCreative.creativeViewDelegate = self @@ -294,13 +292,13 @@ class PBMHTMLCreativeTest : XCTestCase, PBMCreativeResolutionDelegate, PBMCreati func testWebViewLoad() { self.expectationDownloadCompleted = self.expectation(description: "Expected downloadCompleted to be called") self.expectationBlockForAWhile = self.expectation(description: "Expected webViewReadyToDisplay to be called") - + self.htmlCreative.setupView() logToFile = .init() self.htmlCreative.display(withRootViewController:mockViewController) - + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute:{ let log = Log.getLogFileAsString() ?? "" XCTAssertTrue(log.contains("PBMWebView is ready to display")) @@ -316,7 +314,7 @@ class PBMHTMLCreativeTest : XCTestCase, PBMCreativeResolutionDelegate, PBMCreati self.htmlCreative.setupView() logToFile = .init() - + let webView = self.htmlCreative.view as! PBMWebView self.htmlCreative.webView(webView, failedToLoadWithError:PBMError.error(message: "Failed to load html", type: .internalError)) @@ -336,7 +334,7 @@ class PBMHTMLCreativeTest : XCTestCase, PBMCreativeResolutionDelegate, PBMCreati self.htmlCreative.setupView() self.htmlCreative.display(withRootViewController:mockViewController) - + let webView = self.htmlCreative.view as! PBMWebView self.htmlCreative.webView(webView, receivedClickthroughLink:self.clickThroughURL) self.waitForExpectations(timeout: 5, handler: nil) @@ -367,7 +365,7 @@ class PBMHTMLCreativeTest : XCTestCase, PBMCreativeResolutionDelegate, PBMCreati creativeModel: pbmCreativeModel, transaction:UtilitiesForTesting.createEmptyTransaction(), webView: mockWebView, - sdkConfiguration: sdkConfiguration + sdkConfiguration: sdkConfiguration ) PBMFunctions.application = mockApplication @@ -375,21 +373,21 @@ class PBMHTMLCreativeTest : XCTestCase, PBMCreativeResolutionDelegate, PBMCreati waitForExpectations(timeout: 3) } - + func testHasVastTag() { let adConfiguration = AdConfiguration() let pbmCreativeModel = PBMCreativeModel(adConfiguration: adConfiguration) self.htmlCreative = MockPBMHTMLCreative(creativeModel: pbmCreativeModel, transaction: UtilitiesForTesting.createEmptyTransaction()) - + let validXML1 = "" XCTAssertTrue(self.htmlCreative.hasVastTag(validXML2)) - + let incorrectXML1 = "" XCTAssertFalse(self.htmlCreative.hasVastTag(incorrectXML1)) - + let incorrectXML2 = "VAST version=123" XCTAssertFalse(self.htmlCreative.hasVastTag(incorrectXML2)) } @@ -403,12 +401,12 @@ class PBMHTMLCreativeTest : XCTestCase, PBMCreativeResolutionDelegate, PBMCreati parentWindow.frame = CGRect(x: 0.0, y: 0.0, width: 100, height: 100) self.htmlCreative.view?.frame = CGRect(x: 0.0, y: 0.0, width: 100, height: 50) - + parentView.addSubview(self.htmlCreative.view!) parentWindow.addSubview(parentView) self.expectationCreativeDidDisplay = self.expectation(description: "Expected creativeDidDisplay to be called") - + let viewabilityTracker = PBMCreativeViewabilityTracker(creative: self.htmlCreative) viewabilityTracker.checkViewability() @@ -416,7 +414,7 @@ class PBMHTMLCreativeTest : XCTestCase, PBMCreativeResolutionDelegate, PBMCreati self.htmlCreative.view?.removeFromSuperview() self.htmlCreative.view?.frame = CGRect(x: 101.0, y: 101.0, width: 100, height: 100) - + parentView.addSubview(self.htmlCreative.view!) self.expectationCreativeDidDisplay = self.expectation(description: "Expected creativeDidDisplay should not be called") @@ -425,7 +423,160 @@ class PBMHTMLCreativeTest : XCTestCase, PBMCreativeResolutionDelegate, PBMCreati self.waitForExpectations(timeout: 1, handler: nil) } - + + func testRewardEvent_Banner() { + let time: NSNumber = 5 + let exp = expectation(description: "Reward completion") + + let ortbRewarded = PBMORTBRewardedConfiguration() + ortbRewarded.completion = PBMORTBRewardedCompletion() + ortbRewarded.completion?.banner = PBMORTBRewardedCompletionBanner() + ortbRewarded.completion?.banner?.time = time + + let adConfiguration = AdConfiguration() + adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: ortbRewarded) + adConfiguration.isRewarded = true + let creativeModel = PBMCreativeModel(adConfiguration: adConfiguration) + self.htmlCreative = MockPBMHTMLCreative( + creativeModel: creativeModel, + transaction: UtilitiesForTesting.createEmptyTransaction() + ) + + htmlCreative.onViewabilityChanged( + true, + viewExposure: PBMViewExposure( + exposureFactor: 5, + visibleRectangle: CGRect(origin: .zero, size: CGSize(width: 300, height: 250)) + ) + ) + + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(time.intValue + 1)) { + if self.htmlCreative.creativeModel?.userHasEarnedReward == true { + exp.fulfill() + } + } + + waitForExpectations(timeout: 10) + } + + func testRewardEvent_Endcard() { + let time: NSNumber = 5 + let exp = expectation(description: "Reward completion") + + let ortbRewarded = PBMORTBRewardedConfiguration() + ortbRewarded.completion = PBMORTBRewardedCompletion() + ortbRewarded.completion?.video = PBMORTBRewardedCompletionVideo() + ortbRewarded.completion?.video?.endcard = PBMORTBRewardedCompletionVideoEndcard() + ortbRewarded.completion?.video?.endcard?.time = time + + let adConfiguration = AdConfiguration() + adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: ortbRewarded) + adConfiguration.isRewarded = true + let creativeModel = PBMCreativeModel(adConfiguration: adConfiguration) + self.htmlCreative = MockPBMHTMLCreative( + creativeModel: creativeModel, + transaction: UtilitiesForTesting.createEmptyTransaction() + ) + + htmlCreative.creativeModel?.isCompanionAd = true + + htmlCreative.onViewabilityChanged( + true, + viewExposure: PBMViewExposure( + exposureFactor: 5, + visibleRectangle: CGRect(origin: .zero, size: CGSize(width: 300, height: 250)) + ) + ) + + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(time.intValue + 1)) { + + if self.htmlCreative.creativeModel?.userHasEarnedReward == true { + exp.fulfill() + } + } + + waitForExpectations(timeout: 10) + } + + func testPostRewardEvent_Banner() { + let rewardTime: NSNumber = 2 + let postRewardTime: NSNumber = 2 + let exp = expectation(description: "Post reward completion") + + let ortbRewarded = PBMORTBRewardedConfiguration() + ortbRewarded.completion = PBMORTBRewardedCompletion() + ortbRewarded.completion?.banner = PBMORTBRewardedCompletionBanner() + ortbRewarded.completion?.banner?.time = rewardTime + ortbRewarded.close = PBMORTBRewardedClose() + ortbRewarded.close?.postrewardtime = postRewardTime + + let adConfiguration = AdConfiguration() + adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: ortbRewarded) + adConfiguration.isRewarded = true + let creativeModel = PBMCreativeModel(adConfiguration: adConfiguration) + self.htmlCreative = MockPBMHTMLCreative( + creativeModel: creativeModel, + transaction: UtilitiesForTesting.createEmptyTransaction() + ) + + htmlCreative.onViewabilityChanged( + true, + viewExposure: PBMViewExposure( + exposureFactor: 5, + visibleRectangle: CGRect(origin: .zero, size: CGSize(width: 300, height: 250)) + ) + ) + + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(rewardTime.intValue + postRewardTime.intValue + 1)) { + if self.htmlCreative.creativeModel?.userPostRewardEventSent == true { + exp.fulfill() + } + } + + waitForExpectations(timeout: 10) + } + + func testPostRewardEvent_Endcard() { + let rewardTime: NSNumber = 2 + let postRewardTime: NSNumber = 2 + let exp = expectation(description: "Post reward completion") + + let ortbRewarded = PBMORTBRewardedConfiguration() + ortbRewarded.completion = PBMORTBRewardedCompletion() + ortbRewarded.completion?.video = PBMORTBRewardedCompletionVideo() + ortbRewarded.completion?.video?.endcard = PBMORTBRewardedCompletionVideoEndcard() + ortbRewarded.completion?.video?.endcard?.time = rewardTime + ortbRewarded.close = PBMORTBRewardedClose() + ortbRewarded.close?.postrewardtime = postRewardTime + + let adConfiguration = AdConfiguration() + adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: ortbRewarded) + adConfiguration.isRewarded = true + let creativeModel = PBMCreativeModel(adConfiguration: adConfiguration) + self.htmlCreative = MockPBMHTMLCreative( + creativeModel: creativeModel, + transaction: UtilitiesForTesting.createEmptyTransaction() + ) + + htmlCreative.creativeModel?.isCompanionAd = true + + htmlCreative.onViewabilityChanged( + true, + viewExposure: PBMViewExposure( + exposureFactor: 5, + visibleRectangle: CGRect(origin: .zero, size: CGSize(width: 300, height: 250)) + ) + ) + + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(rewardTime.intValue + postRewardTime.intValue + 1)) { + if self.htmlCreative.creativeModel?.userPostRewardEventSent == true { + exp.fulfill() + } + } + + waitForExpectations(timeout: 10) + } + //MARK: - PBMCreativeResolutionDelegate func creativeDidComplete(_ creative: PBMAbstractCreative) { @@ -462,4 +613,5 @@ class PBMHTMLCreativeTest : XCTestCase, PBMCreativeResolutionDelegate, PBMCreati } func creativeFullScreenDidFinish(_ creative: PBMAbstractCreative) {} + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) {} } diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest_Base.swift b/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest_Base.swift index 983881156..e16599ab0 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest_Base.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest_Base.swift @@ -130,6 +130,8 @@ class PBMHTMLCreativeTest_Base: XCTestCase, PBMCreativeViewDelegate { func creativeViewWasClicked(_ creative: PBMAbstractCreative) {} func creativeFullScreenDidFinish(_ creative: PBMAbstractCreative) {} + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) {} + // MARK: - Utilities /** Setup an expectation and associated, mocked `PBMWebView` to fulfill that expectation. diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest_MRAIDClose.swift b/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest_MRAIDClose.swift index 066f42003..9d809c6e4 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest_MRAIDClose.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMHTMLCreativeTest_MRAIDClose.swift @@ -133,4 +133,5 @@ class PBMHTMLCreativeTest_MRAIDClose: XCTestCase, PBMCreativeViewDelegate { func creativeMraidDidExpand(_ creative: PBMAbstractCreative) {} func creativeViewWasClicked(_ creative: PBMAbstractCreative) {} func creativeFullScreenDidFinish(_ creative: PBMAbstractCreative) {} + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) {} } diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMMRAIDControllerTest_Base.swift b/PrebidMobileTests/RenderingTests/Tests/PBMMRAIDControllerTest_Base.swift index 988d06939..c2e8ab989 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMMRAIDControllerTest_Base.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMMRAIDControllerTest_Base.swift @@ -132,6 +132,7 @@ class PBMMRAIDControllerTest_Base: XCTestCase, PBMCreativeViewDelegate { func creativeDidDisplay(_ creative: PBMAbstractCreative) {} func creativeViewWasClicked(_ creative: PBMAbstractCreative) {} func creativeFullScreenDidFinish(_ creative: PBMAbstractCreative) {} + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) {} // MARK: - Utilities /** diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMModalViewControllerTest.swift b/PrebidMobileTests/RenderingTests/Tests/PBMModalViewControllerTest.swift index 0d4bb9244..f297a4902 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMModalViewControllerTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMModalViewControllerTest.swift @@ -125,21 +125,30 @@ class PBMModalViewControllerTest: XCTestCase, PBMModalViewControllerDelegate { XCTAssertFalse(viewController.prefersStatusBarHidden) } - func testCreativeDisplayCompleted() { + func testCreativeDisplayCompleted_Rewarded() { let controller = PBMModalViewController() let displayProperties = PBMInterstitialDisplayProperties() displayProperties.closeDelayLeft = 3 let adConfiguration = AdConfiguration() - adConfiguration.isOptIn = true + adConfiguration.isRewarded = true + adConfiguration.winningBidAdFormat = .video + + let modalState = PBMModalState( + view: UIView(), + adConfiguration: adConfiguration, + displayProperties: displayProperties, + onStatePopFinished: nil, + onStateHasLeftApp: nil + ) - let modalState = PBMModalState(view: UIView(), adConfiguration: adConfiguration, displayProperties:displayProperties, onStatePopFinished: nil, onStateHasLeftApp: nil) controller.modalState = modalState - controller.closeButtonDecorator.button.isHidden = true; + controller.closeButtonDecorator.button.isHidden = true XCTAssertTrue(controller.closeButtonDecorator.button.isHidden) let creative = UtilitiesForTesting.createHTMLCreative() + creative.creativeModel?.adConfiguration = adConfiguration controller.creativeDisplayCompleted(creative) XCTAssertFalse(controller.closeButtonDecorator.button.isHidden) } diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift b/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift index f6f4161f7..6b067e50f 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift @@ -256,11 +256,12 @@ class PBMORTBAbstractTest : XCTestCase { pbmORTBImp.displaymanager = "MOCK_SDK_NAME" pbmORTBImp.displaymanagerver = "MOCK_SDK_VERSION" pbmORTBImp.instl = 1 + pbmORTBImp.rewarded = 1 pbmORTBImp.tagid = "tagid" pbmORTBImp.secure = 1 pbmORTBImp.extData = ["lookup_words": ["dragon", "flame"]] - codeAndDecode(abstract: pbmORTBImp, expectedString: "{\"clickbrowser\":0,\"displaymanager\":\"MOCK_SDK_NAME\",\"displaymanagerver\":\"MOCK_SDK_VERSION\",\"ext\":{\"data\":{\"lookup_words\":[\"dragon\",\"flame\"]},\"dlp\":1},\"id\":\"\(uuid)\",\"instl\":1,\"native\":{\"ver\":\"1.2\"},\"secure\":1,\"tagid\":\"tagid\"}") + codeAndDecode(abstract: pbmORTBImp, expectedString: "{\"clickbrowser\":0,\"displaymanager\":\"MOCK_SDK_NAME\",\"displaymanagerver\":\"MOCK_SDK_VERSION\",\"ext\":{\"data\":{\"lookup_words\":[\"dragon\",\"flame\"]},\"dlp\":1},\"id\":\"\(uuid)\",\"instl\":1,\"native\":{\"ver\":\"1.2\"},\"rwdd\":1,\"secure\":1,\"tagid\":\"tagid\"}") } func testPBMORTBImpExtSkadnToJsonString() { diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMRewardedVideoCreativeTestCloseDelay.swift b/PrebidMobileTests/RenderingTests/Tests/PBMRewardedVideoCreativeTestCloseDelay.swift index 2ecacf4ee..73b27a64d 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMRewardedVideoCreativeTestCloseDelay.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMRewardedVideoCreativeTestCloseDelay.swift @@ -24,7 +24,7 @@ class PBMRewardedVideoCreativeTestCloseDelay : XCTestCase { var actual: TimeInterval let model = PBMCreativeModel(adConfiguration:AdConfiguration()) - model.adConfiguration?.isOptIn = true + model.adConfiguration?.isRewarded = true model.displayDurationInSeconds = 10 actual = calculateCloseDelay(with:model, pubCloseDelay:5) diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMVideoCreativeTestCloseDelay.swift b/PrebidMobileTests/RenderingTests/Tests/PBMVideoCreativeTestCloseDelay.swift index df4f64d3f..f7580af00 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMVideoCreativeTestCloseDelay.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMVideoCreativeTestCloseDelay.swift @@ -27,7 +27,7 @@ class PBMVideoCreativeTestCloseDelay : XCTestCase { model.displayDurationInSeconds = 10 //if model is opt in or has companion ad - return display duration - model.adConfiguration?.isOptIn = true + model.adConfiguration?.isRewarded = true model.hasCompanionAd = true expected = 10 @@ -35,7 +35,7 @@ class PBMVideoCreativeTestCloseDelay : XCTestCase { PBMAssertEq(actual, expected) //reset - model.adConfiguration?.isOptIn = false + model.adConfiguration?.isRewarded = false model.hasCompanionAd = false //for video without end cart use skip delay diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMVideoViewTest.swift b/PrebidMobileTests/RenderingTests/Tests/PBMVideoViewTest.swift index 8e524bbd6..27bb08117 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMVideoViewTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMVideoViewTest.swift @@ -1,23 +1,23 @@ /*   Copyright 2018-2021 Prebid.org, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ import XCTest @testable import PrebidMobile class PBMVideoViewTest: XCTestCase, PBMCreativeResolutionDelegate, PBMCreativeViewDelegate, PBMVideoViewDelegate { - + var vc: UIViewController? let connection = UtilitiesForTesting.createConnectionForMockedTest() @@ -36,7 +36,7 @@ class PBMVideoViewTest: XCTestCase, PBMCreativeResolutionDelegate, PBMCreativeVi override func setUp() { MockServer.shared.reset() } - + override func tearDown() { MockServer.shared.reset() self.videoCreative = nil @@ -145,7 +145,7 @@ class PBMVideoViewTest: XCTestCase, PBMCreativeResolutionDelegate, PBMCreativeVi // Expected duration of video small.mp4 is 6 sec let expectedVideoDuration = 6.0 let expectedPausedTime = 3.0 - + setupVideoCreative(videoFileURL: "http://get_video/small.mp4", localVideoFileName: "small.mp4") self.videoCreative.creativeModel!.displayDurationInSeconds = expectedVideoDuration as NSNumber @@ -187,7 +187,7 @@ class PBMVideoViewTest: XCTestCase, PBMCreativeResolutionDelegate, PBMCreativeVi // Expected duration of video small.mp4 is 6 sec let expectedVideoDuration = 6.0 let expectedStoppedDely = 3.0 - + setupVideoCreative(videoFileURL: "http://get_video/small.mp4", localVideoFileName: "small.mp4") self.videoCreative.creativeModel!.displayDurationInSeconds = expectedVideoDuration as NSNumber @@ -262,7 +262,7 @@ class PBMVideoViewTest: XCTestCase, PBMCreativeResolutionDelegate, PBMCreativeVi // Expected duration of video small.mp4 is 6 sec let expectedVideoDuration = 6.0 - + setupVideoCreative(videoFileURL: "http://get_video/small.mp4", localVideoFileName: "small.mp4") self.videoCreative.creativeModel!.displayDurationInSeconds = expectedVideoDuration as NSNumber @@ -317,6 +317,212 @@ class PBMVideoViewTest: XCTestCase, PBMCreativeResolutionDelegate, PBMCreativeVi waitForExpectations(timeout: 2, handler: nil) } + func testCalculateProgressBarDuration_whenNotRewardedAndHasCompanionAd() { + let adConfiguration = AdConfiguration() + adConfiguration.isRewarded = true + + let model = PBMCreativeModel(adConfiguration:adConfiguration) + + self.videoCreative = PBMVideoCreative( + creativeModel:model, + transaction:UtilitiesForTesting.createEmptyTransaction(), + videoData:Data() + ) + + let mockVideoView = MockVideoView(creative: self.videoCreative) + mockVideoView.mockRequiredVideoDuration = 10 + + let expectedDuration = mockVideoView.requiredVideoDuration() + let result = mockVideoView.calculateProgressBarDuration().doubleValue + + XCTAssertEqual(result, expectedDuration) + } + + func testCalculateProgressBarDuration_whenPlaybackEventIsStart() { + let postRewardTime: NSNumber = 5 + + let adConfiguration = AdConfiguration() + adConfiguration.isRewarded = true + adConfiguration.rewardedConfig = createRewardedConfig(postRewardTime: postRewardTime, playbackevent: "start") + + let model = PBMCreativeModel(adConfiguration:adConfiguration) + + self.videoCreative = PBMVideoCreative( + creativeModel:model, + transaction:UtilitiesForTesting.createEmptyTransaction(), + videoData:Data() + ) + + let mockVideoView = MockVideoView(creative: self.videoCreative) + mockVideoView.mockRequiredVideoDuration = 10 + + let expectedDuration = postRewardTime.doubleValue + let result = mockVideoView.calculateProgressBarDuration().doubleValue + + XCTAssertEqual(result, expectedDuration) + } + + func testCalculateProgressBarDuration_whenPlaybackEventIsFirstQuartile() { + let postRewardTime: NSNumber = 5 + + let adConfiguration = AdConfiguration() + adConfiguration.isRewarded = true + adConfiguration.rewardedConfig = createRewardedConfig(postRewardTime: postRewardTime, playbackevent: "firstquartile") + + let model = PBMCreativeModel(adConfiguration:adConfiguration) + + self.videoCreative = PBMVideoCreative( + creativeModel:model, + transaction:UtilitiesForTesting.createEmptyTransaction(), + videoData:Data() + ) + + let mockVideoView = MockVideoView(creative: self.videoCreative) + + let expectedDuration = 0.25 * mockVideoView.requiredVideoDuration() + postRewardTime.doubleValue + let result = mockVideoView.calculateProgressBarDuration().doubleValue + + XCTAssertEqual(result, expectedDuration) + } + + func testCalculateProgressBarDuration_whenPlaybackEventIsMidpoint() { + let postRewardTime: NSNumber = 5 + + let adConfiguration = AdConfiguration() + adConfiguration.isRewarded = true + adConfiguration.rewardedConfig = createRewardedConfig(postRewardTime: postRewardTime, playbackevent: "midpoint") + + let model = PBMCreativeModel(adConfiguration:adConfiguration) + + self.videoCreative = PBMVideoCreative( + creativeModel:model, + transaction:UtilitiesForTesting.createEmptyTransaction(), + videoData:Data() + ) + + let mockVideoView = MockVideoView(creative: self.videoCreative) + + let expectedDuration = 0.5 * mockVideoView.requiredVideoDuration() + postRewardTime.doubleValue + let result = mockVideoView.calculateProgressBarDuration().doubleValue + + XCTAssertEqual(result, expectedDuration) + } + + func testCalculateProgressBarDuration_whenPlaybackEventIsThirdQuartile() { + let postRewardTime: NSNumber = 5 + let adConfiguration = AdConfiguration() + adConfiguration.isRewarded = true + adConfiguration.rewardedConfig = createRewardedConfig(postRewardTime: postRewardTime, playbackevent: "thirdquartile") + + let model = PBMCreativeModel(adConfiguration:adConfiguration) + + self.videoCreative = PBMVideoCreative( + creativeModel:model, + transaction:UtilitiesForTesting.createEmptyTransaction(), + videoData:Data() + ) + + let mockVideoView = MockVideoView(creative: self.videoCreative) + + let expectedDuration = 0.5 * mockVideoView.requiredVideoDuration() + postRewardTime.doubleValue + let result = mockVideoView.calculateProgressBarDuration().doubleValue + + XCTAssertEqual(result, expectedDuration) + } + + func testCalculateProgressBarDuration_whenPlaybackEventIsComplete() { + let postRewardTime: NSNumber = 5 + + let adConfiguration = AdConfiguration() + adConfiguration.isRewarded = true + adConfiguration.rewardedConfig = createRewardedConfig(postRewardTime: postRewardTime, playbackevent: "complete") + + let model = PBMCreativeModel(adConfiguration:adConfiguration) + + self.videoCreative = PBMVideoCreative( + creativeModel:model, + transaction:UtilitiesForTesting.createEmptyTransaction(), + videoData:Data() + ) + + let mockVideoView = MockVideoView(creative: self.videoCreative) + + let expectedDuration = mockVideoView.requiredVideoDuration() + let result = mockVideoView.calculateProgressBarDuration().doubleValue + + XCTAssertEqual(result, expectedDuration) + } + + func testCalculateProgressBarDuration_whenCompletionCriteriaIsTime() { + let rewardTime: NSNumber = 2 + let postRewardTime: NSNumber = 5 + + let adConfiguration = AdConfiguration() + adConfiguration.isRewarded = true + adConfiguration.rewardedConfig = createRewardedConfig(postRewardTime: postRewardTime, time: rewardTime) + + let model = PBMCreativeModel(adConfiguration:adConfiguration) + + self.videoCreative = PBMVideoCreative( + creativeModel:model, + transaction:UtilitiesForTesting.createEmptyTransaction(), + videoData:Data() + ) + + let mockVideoView = MockVideoView(creative: self.videoCreative) + + let expectedDuration = rewardTime.doubleValue + postRewardTime.doubleValue + let result = mockVideoView.calculateProgressBarDuration().doubleValue + + XCTAssertEqual(result, expectedDuration) + } + + func testCalculateProgressBarDuration_whenCompletionCriteriaIsTimeAndExceedsVideoDuration() { + let rewardTime: NSNumber = 100 + let postRewardTime: NSNumber = 5 + + let adConfiguration = AdConfiguration() + adConfiguration.isRewarded = true + adConfiguration.rewardedConfig = createRewardedConfig(postRewardTime: postRewardTime, time: rewardTime) + + let model = PBMCreativeModel(adConfiguration:adConfiguration) + + self.videoCreative = PBMVideoCreative( + creativeModel:model, + transaction:UtilitiesForTesting.createEmptyTransaction(), + videoData:Data() + ) + + let mockVideoView = MockVideoView(creative: self.videoCreative) + + let expectedDuration = mockVideoView.requiredVideoDuration() + let result = mockVideoView.calculateProgressBarDuration().doubleValue + + XCTAssertEqual(result, expectedDuration) + } + + func testCalculateProgressBarDuration_default() { + let adConfiguration = AdConfiguration() + adConfiguration.isRewarded = true + adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: PBMORTBRewardedConfiguration()) + + let model = PBMCreativeModel(adConfiguration: adConfiguration) + + self.videoCreative = PBMVideoCreative( + creativeModel:model, + transaction:UtilitiesForTesting.createEmptyTransaction(), + videoData:Data() + ) + + let mockVideoView = MockVideoView(creative: self.videoCreative) + + let expectedDuration = mockVideoView.requiredVideoDuration() + let result = mockVideoView.calculateProgressBarDuration().doubleValue + + XCTAssertEqual(result, expectedDuration) + } + + // MARK: - PBMCreativeDownloadDelegate func creativeReady(_ creative: PBMAbstractCreative) { @@ -365,7 +571,8 @@ class PBMVideoViewTest: XCTestCase, PBMCreativeResolutionDelegate, PBMCreativeVi func learnMoreWasClicked() {} func creativeViewWasClicked(_ creative: PBMAbstractCreative) {} func creativeFullScreenDidFinish(_ creative: PBMAbstractCreative) {} - + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) {} + func videoViewCurrentPlayingTime(_ currentPlayingTime: NSNumber) {} // MARK: - Helper Methods @@ -418,7 +625,20 @@ class PBMVideoViewTest: XCTestCase, PBMCreativeResolutionDelegate, PBMCreativeVi self.expectationVideoViewCompletedDisplay = expectation(description: "expectationVideoViewCompletedDisplay") waitForExpectations(timeout: expectedDuration + 5, handler:nil) - + XCTAssertTrue(self.isVideoViewCompletedDisplay) } + + private func createRewardedConfig(postRewardTime: NSNumber = 0, playbackevent: String? = nil, + time: NSNumber? = nil) -> RewardedConfig { + let ortbRewarded = PBMORTBRewardedConfiguration() + ortbRewarded.completion = PBMORTBRewardedCompletion() + ortbRewarded.completion?.video = PBMORTBRewardedCompletionVideo() + ortbRewarded.completion?.video?.playbackevent = playbackevent + ortbRewarded.completion?.video?.time = time + ortbRewarded.close = PBMORTBRewardedClose() + ortbRewarded.close?.postrewardtime = postRewardTime + + return RewardedConfig(ortbRewarded: ortbRewarded) + } } diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMWebViewObjCTest.m b/PrebidMobileTests/RenderingTests/Tests/PBMWebViewObjCTest.m index 304d321da..8695a92da 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMWebViewObjCTest.m +++ b/PrebidMobileTests/RenderingTests/Tests/PBMWebViewObjCTest.m @@ -92,7 +92,7 @@ - (void)testEvaluateJSNil { NSString *str = nil; [webView evaluateJavaScript:str]; - [self waitForExpectationsWithTimeout:3.0 handler:nil]; + [self waitForExpectationsWithTimeout:10.0 handler:nil]; } #pragma mark - WKNavigationDelegate diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMWebViewTest.swift b/PrebidMobileTests/RenderingTests/Tests/PBMWebViewTest.swift index 9ca695a91..c174326ec 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMWebViewTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMWebViewTest.swift @@ -685,7 +685,7 @@ class PBMWebViewTest : XCTestCase, PBMWebViewDelegate { XCTAssertEqual(webView.mraidState, .notEnabled) XCTAssertEqual(webView.state, .loading) - waitForExpectations(timeout: 3, handler: { _ in + waitForExpectations(timeout: 5, handler: { _ in XCTAssertEqual(webView.state, .loaded) XCTAssertEqual(webView.mraidState, PBMMRAIDState.default) }) @@ -898,7 +898,7 @@ class PBMWebViewTest : XCTestCase, PBMWebViewDelegate { } checkJSEvaluating(webView: webView, commandBlock: commandBlock, evaluatingBlock: jsEvaluationBlock) - waitForExpectations(timeout: 3) + waitForExpectations(timeout: 5) } func testMRAID_onExposureChangeTrue() { @@ -914,7 +914,7 @@ class PBMWebViewTest : XCTestCase, PBMWebViewDelegate { } checkJSEvaluating(webView: webView, commandBlock: commandBlock, evaluatingBlock: jsEvaluationBlock) - waitForExpectations(timeout: 3) + waitForExpectations(timeout: 5) } func testMRAID_updatePlacementType() { @@ -930,7 +930,7 @@ class PBMWebViewTest : XCTestCase, PBMWebViewDelegate { } checkJSEvaluating(webView: webView, commandBlock: commandBlock, evaluatingBlock: jsEvaluationBlock) - waitForExpectations(timeout: 3) + waitForExpectations(timeout: 5) } func testAudioVolumeChange() { @@ -1055,6 +1055,8 @@ class PBMWebViewTest : XCTestCase, PBMWebViewDelegate { expectationWebViewReceivedMRAIDLink?.fulfill() } + func webView(_ webView: PBMWebView, receivedRewardedEventLink url: URL) {} + // MARK: - Check methods private func checkJSEvaluating( webView: PBMWebView, diff --git a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/BasicParameterBuilderTest.swift b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/BasicParameterBuilderTest.swift index 461632d3f..94ab42ca7 100644 --- a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/BasicParameterBuilderTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/BasicParameterBuilderTest.swift @@ -148,23 +148,22 @@ class PBMBasicParameterBuilderTest: XCTestCase { } func testParameterBuilderDefaultInterstitialConfig() { - var adUnit = BaseInterstitialAdUnit.init(configID: "configId") + var adUnit = InterstitialRenderingAdUnit(configID: "configId") checkDefaultParametersForAdUnit(adConfiguration: adUnit.adUnitConfig.adConfiguration) - adUnit = BaseInterstitialAdUnit.init(configID: "configId", minSizePerc: 0.2 as NSValue, eventHandler: InterstitialEventHandlerStandalone()) + adUnit = InterstitialRenderingAdUnit(configID: "configId", minSizePerc: 0.2 as NSValue, primaryAdRequester: InterstitialEventHandlerStandalone()) checkDefaultParametersForAdUnit(adConfiguration: adUnit.adUnitConfig.adConfiguration) - adUnit = BaseInterstitialAdUnit.init(configID: "configId", minSizePercentage: CGSize.zero) + adUnit = InterstitialRenderingAdUnit(configID: "configId", minSizePercentage: CGSize.zero) checkDefaultParametersForAdUnit(adConfiguration: adUnit.adUnitConfig.adConfiguration) - adUnit = BaseInterstitialAdUnit.init(configID: "configId", minSizePercentage: CGSize.zero, eventHandler: InterstitialEventHandlerStandalone()) + adUnit = InterstitialRenderingAdUnit(configID: "configId", minSizePercentage: CGSize.zero, eventHandler: InterstitialEventHandlerStandalone()) checkDefaultParametersForAdUnit(adConfiguration: adUnit.adUnitConfig.adConfiguration) - adUnit = BaseInterstitialAdUnit.init(configID: "configId", eventHandler: InterstitialEventHandlerStandalone()) + adUnit = InterstitialRenderingAdUnit(configID: "configId", eventHandler: InterstitialEventHandlerStandalone()) checkDefaultParametersForAdUnit(adConfiguration: adUnit.adUnitConfig.adConfiguration) - let mediationAdUnit = MediationBaseInterstitialAdUnit.init(configId: "configId", mediationDelegate: MockMediationUtils(adObject: MockAdObject())) - + let mediationAdUnit = MediationBaseInterstitialAdUnit(configId: "configId", mediationDelegate: MockMediationUtils(adObject: MockAdObject())) checkDefaultParametersForAdUnit(adConfiguration: mediationAdUnit.adUnitConfig.adConfiguration) } diff --git a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift index d1e1700a9..9de2d84be 100644 --- a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift @@ -63,7 +63,11 @@ class PrebidParameterBuilderTest: XCTestCase { func testAdPositionFullScreen() { let configId = "b6260e2b-bc4c-4d10-bdb5-f7bdd62f5ed4" - let interstitialAdUnit = InterstitialRenderingAdUnit(configID: configId) + let interstitialAdUnit = BaseInterstitialAdUnit( + configID: configId, + minSizePerc: nil, + eventHandler: InterstitialEventHandlerStandalone() + ) let bidRequest = buildBidRequest(with: interstitialAdUnit.adUnitConfig) @@ -396,7 +400,12 @@ class PrebidParameterBuilderTest: XCTestCase { XCTAssertEqual(imp.banner?.api, apiSignalsAsNumbers) } - let redenderingInterstitialAdUnit = BaseInterstitialAdUnit(configID: "configID") + let redenderingInterstitialAdUnit = BaseInterstitialAdUnit( + configID: "configID", + minSizePerc: nil, + eventHandler: InterstitialEventHandlerStandalone() + ) + bidRequest = buildBidRequest(with: redenderingInterstitialAdUnit.adUnitConfig) bidRequest.imp.forEach { @@ -583,8 +592,14 @@ class PrebidParameterBuilderTest: XCTestCase { } func testParameterBuilderInterstitialVAST() { - let adUnit = InterstitialRenderingAdUnit.init(configID: "configId") - adUnit.adFormats = [.video] + let adUnit = BaseInterstitialAdUnit( + configID: "configID", + minSizePerc: nil, + eventHandler: InterstitialEventHandlerStandalone() + ) + + adUnit.adUnitConfig.adFormats = [.video] + let adConfiguration = adUnit.adUnitConfig.adConfiguration let parameters = VideoParameters(mimes: []) parameters.placement = .Interstitial diff --git a/PrebidMobileTests/RenderingTests/Tests/Prebid/AdLoadFlow/PBMAdLoadFlowControllerTest.swift b/PrebidMobileTests/RenderingTests/Tests/Prebid/AdLoadFlow/PBMAdLoadFlowControllerTest.swift index f4b4d36c3..74bf3ac00 100644 --- a/PrebidMobileTests/RenderingTests/Tests/Prebid/AdLoadFlow/PBMAdLoadFlowControllerTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/Prebid/AdLoadFlow/PBMAdLoadFlowControllerTest.swift @@ -28,9 +28,11 @@ class PBMAdLoadFlowControllerTest: XCTestCase { } func testNoImmediateCalls() { + let adUnitConfig = AdUnitConfig(configId: "configId") let compositeMock = CompositeMock(expectedCalls: []) let flowController = PBMAdLoadFlowController(bidRequesterFactory: compositeMock.mockRequesterFactory, adLoader: compositeMock.mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock.mockFlowControllerDelegate, configValidationBlock: compositeMock.mockConfigValidator) let timeExp = expectation(description: "no event") @@ -66,7 +68,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { let successReported = expectation(description: "success reported") compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { adUnitConfig })), .configValidation(call: { (adConfig, renderWithPrebid) in XCTAssertFalse(renderWithPrebid) return true @@ -110,6 +111,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -150,7 +152,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { let successReported = expectation(description: "success reported") compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { adUnitConfig })), .configValidation(call: { (adConfig, renderWithPrebid) in XCTAssertFalse(renderWithPrebid) return true @@ -202,6 +203,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -234,7 +236,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { let successReported = expectation(description: "success reported") compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { adUnitConfig })), .configValidation(call: { (adConfig, renderWithPrebid) in XCTAssertFalse(renderWithPrebid) return true @@ -285,6 +286,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -311,7 +313,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { let failureReported = expectation(description: "failure reported") compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { adUnitConfig })), .configValidation(call: { (adConfig, renderWithPrebid) in XCTAssertFalse(renderWithPrebid) return true @@ -355,6 +356,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -381,7 +383,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { let failureReported = expectation(description: "failure reported") compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { adUnitConfig })), .configValidation(call: { (adConfig, renderWithPrebid) in XCTAssertFalse(renderWithPrebid) return true @@ -429,6 +430,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -457,7 +459,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { let successReported = expectation(description: "success reported") compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { adUnitConfig })), .configValidation(call: { (adConfig, renderWithPrebid) in XCTAssertFalse(renderWithPrebid) return true @@ -511,6 +512,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -539,7 +541,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { let failureReported = expectation(description: "failure reported") compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { adUnitConfig })), .configValidation(call: { (adConfig, renderWithPrebid) in XCTAssertFalse(renderWithPrebid) return true @@ -586,6 +587,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -609,7 +611,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { let failureReported = expectation(description: "failure reported") compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { adUnitConfig })), .configValidation(call: { (adConfig, renderWithPrebid) in XCTAssertFalse(renderWithPrebid) return false @@ -623,6 +624,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -646,7 +648,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { let failureReported = expectation(description: "failure reported") compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { adUnitConfig })), .configValidation(call: { (adConfig, renderWithPrebid) in XCTAssertFalse(renderWithPrebid) return true @@ -686,6 +687,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -709,7 +711,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { let failureReported = expectation(description: "failure reported") compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { adUnitConfig })), .configValidation(call: { (adConfig, renderWithPrebid) in XCTAssertFalse(renderWithPrebid) return true @@ -750,6 +751,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -778,10 +780,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { let successReported = expectation(description: "success reported") compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { - flowController().refresh() - return adUnitConfig - })), .configValidation(call: { (adConfig, renderWithPrebid) in flowController().refresh() return true @@ -837,6 +835,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -873,7 +872,6 @@ class PBMAdLoadFlowControllerTest: XCTestCase { } compositeMockBox[0] = CompositeMock(expectedCalls: [ - .flowControllerDelegate(call: .adUnitConfig(provider: { adUnitConfig })), .configValidation(call: { (adConfig, renderWithPrebid) in true }), .flowControllerDelegate(call: .willSendBidRequest(handler: { loader in })), .makeBidRequester(handler: { config, mockRequester in mockRequester }), @@ -915,6 +913,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { flowControllerBox[0] = PBMAdLoadFlowController(bidRequesterFactory: compositeMock().mockRequesterFactory, adLoader: compositeMock().mockAdLoader, + adUnitConfig: adUnitConfig, delegate: compositeMock().mockFlowControllerDelegate, configValidationBlock: compositeMock().mockConfigValidator) @@ -926,7 +925,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { waitForExpectations(timeout: 1) XCTAssertEqual(flowController().flowState, .demandReceived) - XCTAssertEqual(compositeMock().getProgress().done, 6) + XCTAssertEqual(compositeMock().getProgress().done, 5) nextShouldContinueExpectationBox[0] = expectation(description: "Second 'shouldContinue' reached") let secondTimeout = expectation(description: "first timeout") @@ -936,7 +935,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { waitForExpectations(timeout: 1) XCTAssertEqual(flowController().flowState, .readyToDeploy) - XCTAssertEqual(compositeMock().getProgress().done, 13) + XCTAssertEqual(compositeMock().getProgress().done, 12) successReportedExpectationBox[0] = expectation(description: "success reported") @@ -945,7 +944,7 @@ class PBMAdLoadFlowControllerTest: XCTestCase { XCTAssertFalse(flowController().hasFailedLoading) XCTAssertEqual(flowController().flowState, .idle) - XCTAssertEqual(compositeMock().getProgress().done, 14) + XCTAssertEqual(compositeMock().getProgress().done, 13) compositeMock().checkIsFinished() } } diff --git a/PrebidMobileTests/RenderingTests/Tests/Prebid/BaseInterstitialAdUnitTest.swift b/PrebidMobileTests/RenderingTests/Tests/Prebid/BaseInterstitialAdUnitTest.swift index febebecc2..88fa3cea7 100644 --- a/PrebidMobileTests/RenderingTests/Tests/Prebid/BaseInterstitialAdUnitTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/Prebid/BaseInterstitialAdUnitTest.swift @@ -13,31 +13,44 @@  limitations under the License.  */ - import XCTest -import PrebidMobile +@testable import PrebidMobile class BaseInterstitialAdUnitTest: XCTestCase { func testCloseButtonArea() { - let adUnit = BaseInterstitialAdUnit(configID: "test") - XCTAssertTrue(adUnit.closeButtonArea == 0.1) + let adUnit = BaseInterstitialAdUnit( + configID: "test", + minSizePerc: nil, + eventHandler: InterstitialEventHandlerStandalone() + ) + + let videoConfig = adUnit.adUnitConfig.adConfiguration.videoControlsConfig + + XCTAssertTrue(videoConfig.closeButtonArea == 0.1) - adUnit.closeButtonArea = 1.1 - XCTAssertTrue(adUnit.closeButtonArea == 0.1) + videoConfig.closeButtonArea = 1.1 + XCTAssertTrue(videoConfig.closeButtonArea == 0.1) - adUnit.closeButtonArea = -0.1 - XCTAssertTrue(adUnit.closeButtonArea == 0.1) + videoConfig.closeButtonArea = -0.1 + XCTAssertTrue(videoConfig.closeButtonArea == 0.1) - adUnit.closeButtonArea = 0.25 - XCTAssertTrue(adUnit.closeButtonArea == 0.25) + videoConfig.closeButtonArea = 0.25 + XCTAssertTrue(videoConfig.closeButtonArea == 0.25) } func testCloseButtonPosition() { - let adUnit = BaseInterstitialAdUnit(configID: "test") - XCTAssertEqual(adUnit.closeButtonPosition, .topRight) + let adUnit = BaseInterstitialAdUnit( + configID: "test", + minSizePerc: nil, + eventHandler: InterstitialEventHandlerStandalone() + ) + + let videoConfig = adUnit.adUnitConfig.adConfiguration.videoControlsConfig + + XCTAssertEqual(videoConfig.closeButtonPosition, .topRight) - adUnit.closeButtonPosition = .topLeft - XCTAssertEqual(adUnit.adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition, .topLeft) + videoConfig.closeButtonPosition = .topLeft + XCTAssertEqual(videoConfig.closeButtonPosition, .topLeft) } } diff --git a/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MediationRewardedAdUnitTest.swift b/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MediationRewardedAdUnitTest.swift index b48793728..d9478ce3b 100644 --- a/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MediationRewardedAdUnitTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MediationRewardedAdUnitTest.swift @@ -26,7 +26,7 @@ class MediationRewardedAdUnitTest: XCTestCase { let adUnitConfig = adUnit.adUnitConfig XCTAssertTrue(adUnitConfig.adConfiguration.isInterstitialAd) - XCTAssertTrue(adUnitConfig.adConfiguration.isOptIn) + XCTAssertTrue(adUnitConfig.adConfiguration.isRewarded) PBMAssertEq(adUnitConfig.adPosition, .fullScreen) XCTAssertTrue(adUnitConfig.adFormats.contains(.video)) } diff --git a/PrebidMobileTests/RenderingTests/Tests/Prebid/PBMBaseInterstitialAdUnit_DelegationTest.swift b/PrebidMobileTests/RenderingTests/Tests/Prebid/PBMBaseInterstitialAdUnit_DelegationTest.swift index b21f2fceb..9cdd91286 100644 --- a/PrebidMobileTests/RenderingTests/Tests/Prebid/PBMBaseInterstitialAdUnit_DelegationTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/Prebid/PBMBaseInterstitialAdUnit_DelegationTest.swift @@ -13,23 +13,10 @@  limitations under the License.  */ -import Foundation import XCTest @testable import PrebidMobile -class MockInterstitialAdUnit: InterstitialRenderingAdUnit { - override var lastBidResponse: BidResponse? { - return WinningBidResponseFabricator.makeWinningBidResponse(bidPrice: 0.85) - } -} - -class MockRewardedAdUnit: RewardedAdUnit { - override var lastBidResponse: BidResponse? { - return WinningBidResponseFabricator.makeWinningBidResponse(bidPrice: 0.85) - } -} - class PBMBaseInterstitialAdUnit_DelegationTest: XCTestCase { override func tearDown() { Prebid.reset() @@ -40,28 +27,28 @@ class PBMBaseInterstitialAdUnit_DelegationTest: XCTestCase { private let configId = "someConfigId" func testInterstitialDelegateCalls_noOptionalMethods() { - let adUnit = MockInterstitialAdUnit(configID: configId) + let adUnit = InterstitialRenderingAdUnit(configID: configId) let delegate = DummyInterstitialDelegate() adUnit.delegate = delegate callInterstitialDelegateMethods(adUnit: adUnit) } func testInterstitialDelegateCalls_receiveAllMethods() { - let adUnit = MockInterstitialAdUnit(configID: configId) + let adUnit = InterstitialRenderingAdUnit(configID: configId) let delegate = InterstitialProxyDelegate() adUnit.delegate = delegate callInterstitialDelegateMethods(adUnit: adUnit, proxyDelegate: delegate) } func testRewardedAdDelegateCalls_noOptionalMethods() { - let adUnit = MockRewardedAdUnit(configID: configId, minSizePerc: nil, eventHandler: RewardedEventHandlerStandalone()) + let adUnit = RewardedAdUnit(configID: configId, minSizePerc: nil, primaryAdRequester: RewardedEventHandlerStandalone()) let delegate = DummyRewardedAdDelegate() adUnit.delegate = delegate callRewardedAdDelegateMethods(adUnit: adUnit) } func testRewardedAdDelegateCalls_receiveAllMethods() { - let adUnit = MockRewardedAdUnit(configID: configId, minSizePerc: nil, eventHandler: RewardedEventHandlerStandalone()) + let adUnit = RewardedAdUnit(configID: configId, minSizePerc: nil, primaryAdRequester: RewardedEventHandlerStandalone()) let delegate = RewardedAdProxyDelegate() adUnit.delegate = delegate callRewardedAdDelegateMethods(adUnit: adUnit, proxyDelegate: delegate) @@ -72,14 +59,14 @@ class PBMBaseInterstitialAdUnit_DelegationTest: XCTestCase { Prebid.shared.prebidServerAccountId = "" - let interstitial = MockInterstitialAdUnit(configID: testID) + let interstitial = InterstitialRenderingAdUnit(configID: testID) let exp = expectation(description: "loading callback called") let delegate = InterstitialProxyDelegate() interstitial.delegate = delegate delegate.onCall = { selector, args in XCTAssertEqual(selector, "interstitial:didFailToReceiveAdWithError:") XCTAssertEqual(args.count, 2) - XCTAssertEqual(args[0] as? MockInterstitialAdUnit, interstitial) + XCTAssertEqual(args[0] as? InterstitialRenderingAdUnit, interstitial) XCTAssertEqual(args[1] as? NSError, PBMError.prebidInvalidAccountId as NSError?) exp.fulfill() } @@ -94,14 +81,14 @@ class PBMBaseInterstitialAdUnit_DelegationTest: XCTestCase { Prebid.shared.prebidServerAccountId = "" - let rewarded = MockRewardedAdUnit(configID: testID, minSizePerc: nil, eventHandler: RewardedEventHandlerStandalone()) + let rewarded = RewardedAdUnit(configID: testID, minSizePerc: nil, primaryAdRequester: RewardedEventHandlerStandalone()) let exp = expectation(description: "loading callback called") let delegate = RewardedAdProxyDelegate() rewarded.delegate = delegate delegate.onCall = { selector, args in XCTAssertEqual(selector, "rewardedAd:didFailToReceiveAdWithError:") XCTAssertEqual(args.count, 2) - XCTAssertEqual(args[0] as? MockRewardedAdUnit, rewarded) + XCTAssertEqual(args[0] as? RewardedAdUnit, rewarded) XCTAssertEqual(args[1] as? NSError, PBMError.prebidInvalidAccountId as NSError?) exp.fulfill() } @@ -139,58 +126,45 @@ class PBMBaseInterstitialAdUnit_DelegationTest: XCTestCase { private class InterstitialProxyDelegate: BaseProxyDelegate, InterstitialAdUnitDelegate { func interstitialDidReceiveAd(_ interstitial: InterstitialRenderingAdUnit) { report(selectorName: "interstitialDidReceiveAd:", args: [interstitial]) - XCTAssertNotNil(interstitial.lastBidResponse) } func interstitial(_ interstitial: InterstitialRenderingAdUnit, didFailToReceiveAdWithError error: Error?) { report(selectorName: "interstitial:didFailToReceiveAdWithError:", args: [interstitial, error as Any]) - XCTAssertNotNil(interstitial.lastBidResponse) } func interstitialWillPresentAd(_ interstitial: InterstitialRenderingAdUnit) { report(selectorName: "interstitialWillPresentAd:", args: [interstitial]) - XCTAssertNotNil(interstitial.lastBidResponse) } func interstitialDidDismissAd(_ interstitial: InterstitialRenderingAdUnit) { report(selectorName: "interstitialDidDismissAd:", args: [interstitial]) - XCTAssertNotNil(interstitial.lastBidResponse) } func interstitialWillLeaveApplication(_ interstitial: InterstitialRenderingAdUnit) { report(selectorName: "interstitialWillLeaveApplication:", args: [interstitial]) - XCTAssertNotNil(interstitial.lastBidResponse) } func interstitialDidClickAd(_ interstitial: InterstitialRenderingAdUnit) { report(selectorName: "interstitialDidClickAd:", args: [interstitial]) - XCTAssertNotNil(interstitial.lastBidResponse) } } private class RewardedAdProxyDelegate: BaseProxyDelegate, RewardedAdUnitDelegate { func rewardedAdDidReceiveAd(_ rewardedAd: RewardedAdUnit) { report(selectorName: "rewardedAdDidReceiveAd:", args: [rewardedAd]) - XCTAssertNotNil(rewardedAd.lastBidResponse) } func rewardedAd(_ rewardedAd: RewardedAdUnit, didFailToReceiveAdWithError error: Error?) { report(selectorName: "rewardedAd:didFailToReceiveAdWithError:", args: [rewardedAd, error as Any]) - XCTAssertNotNil(rewardedAd.lastBidResponse) } func rewardedAdWillPresentAd(_ rewardedAd: RewardedAdUnit) { report(selectorName: "rewardedAdWillPresentAd:", args: [rewardedAd]) - XCTAssertNotNil(rewardedAd.lastBidResponse) } func rewardedAdDidDismissAd(_ rewardedAd: RewardedAdUnit) { report(selectorName: "rewardedAdDidDismissAd:", args: [rewardedAd]) - XCTAssertNotNil(rewardedAd.lastBidResponse) } func rewardedAdWillLeaveApplication(_ rewardedAd: RewardedAdUnit) { report(selectorName: "rewardedAdWillLeaveApplication:", args: [rewardedAd]) - XCTAssertNotNil(rewardedAd.lastBidResponse) } func rewardedAdDidClickAd(_ rewardedAd: RewardedAdUnit) { report(selectorName: "rewardedAdDidClickAd:", args: [rewardedAd]) - XCTAssertNotNil(rewardedAd.lastBidResponse) } - func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit) { - report(selectorName: "rewardedAdUserDidEarnReward:", args: [rewardedAd]) - XCTAssertNotNil(rewardedAd.lastBidResponse) + func rewardedAdUserDidEarnReward(_ rewardedAd: RewardedAdUnit, reward: PrebidReward) { + report(selectorName: "rewardedAdUserDidEarnReward:", args: [rewardedAd, reward]) } } @@ -290,24 +264,32 @@ class PBMBaseInterstitialAdUnit_DelegationTest: XCTestCase { private func callRewardedAdDelegateMethods(adUnit: RewardedAdUnit, proxyDelegate: RewardedAdProxyDelegate? = nil, - file: StaticString = #file, line: UInt = #line) - { + file: StaticString = #file, line: UInt = #line) { + func testCall(_ expectedSelector: String, args expectedArgs: [Any], block: () throws -> ()) { - PBMBaseInterstitialAdUnit_DelegationTest.callDelegateMethod(proxyDelegate: proxyDelegate, - expectedSelector: expectedSelector, - expectedArgs: expectedArgs, - file: file, line: line, - block: block) + PBMBaseInterstitialAdUnit_DelegationTest + .callDelegateMethod( + proxyDelegate: proxyDelegate, + expectedSelector: expectedSelector, + expectedArgs: expectedArgs, + file: file, + line: line, + block: block + ) } - callProtectedSelectors(baseAdUnit: adUnit, - proxyDelegate: proxyDelegate, - selectorPrefix: "rewardedAd", - file: file, line: line) + callProtectedSelectors( + baseAdUnit: adUnit, + proxyDelegate: proxyDelegate, + selectorPrefix: "rewardedAd", + file: file, + line: line + ) - testCall("rewardedAdUserDidEarnReward:", args: [adUnit]) { - adUnit.callDelegate_rewardedAdUserDidEarnReward() + let reward = PrebidReward() + + testCall("rewardedAdUserDidEarnReward:", args: [adUnit, reward]) { + adUnit.callDelegate_rewardedAdUserDidEarnReward(reward: reward) } } - } diff --git a/PrebidMobileTests/RenderingTests/Tests/Prebid/PBMORTBTest.swift b/PrebidMobileTests/RenderingTests/Tests/Prebid/PBMORTBTest.swift index 432753fdf..b8c7fdd27 100644 --- a/PrebidMobileTests/RenderingTests/Tests/Prebid/PBMORTBTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/Prebid/PBMORTBTest.swift @@ -311,6 +311,35 @@ class PBMORTBTest: XCTestCase { } } + func testRewardedResponse() { + let rewarded = PBMORTBRewardedConfiguration() + + rewarded.completion = PBMORTBRewardedCompletion() + + rewarded.completion?.banner = PBMORTBRewardedCompletionBanner() + rewarded.completion?.banner?.time = 5 + rewarded.completion?.banner?.event = "rwdd" + + rewarded.completion?.video = PBMORTBRewardedCompletionVideo() + rewarded.completion?.video?.time = 5 + rewarded.completion?.video?.playbackevent = "complete" + + rewarded.completion?.video?.endcard = PBMORTBRewardedCompletionVideoEndcard() + rewarded.completion?.video?.endcard?.time = 5 + rewarded.completion?.video?.endcard?.event = "rwdd" + + rewarded.close = PBMORTBRewardedClose() + rewarded.close?.action = "closebutton" + rewarded.close?.postrewardtime = 5 + + rewarded.reward = PBMORTBRewardedReward() + rewarded.reward?.type = "coins" + rewarded.reward?.count = 5 + + codeAndDecode(abstract: rewarded, expectedString: "{\"close\":{\"action\":\"closebutton\",\"postrewardtime\":5},\"completion\":{\"banner\":{\"event\":\"rwdd\",\"time\":5},\"video\":{\"endcard\":{\"event\":\"rwdd\",\"time\":5},\"playbackevent\":\"complete\",\"time\":5}},\"reward\":{\"count\":5,\"type\":\"coins\"}}") + } + + // MARK: - Private helpers private func codeAndDecode(abstract:T, expectedString:String, file: StaticString = #file, line: UInt = #line, decoder: (JsonDictionary) -> T?) { diff --git a/PrebidMobileTests/RenderingTests/Tests/RewardedVideo/PBMRewardedVideoViewTest.swift b/PrebidMobileTests/RenderingTests/Tests/RewardedVideo/PBMRewardedVideoViewTest.swift index 46da3a1b6..6636f7d4b 100644 --- a/PrebidMobileTests/RenderingTests/Tests/RewardedVideo/PBMRewardedVideoViewTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/RewardedVideo/PBMRewardedVideoViewTest.swift @@ -121,6 +121,7 @@ class PBMRewardedVideoViewTest: XCTestCase, PBMCreativeResolutionDelegate, PBMCr // MARK: - PBMVideoViewDelegate + func videoViewCurrentPlayingTime(_ currentPlayingTime: NSNumber) {} func videoViewFailedWithError(_ error: Error) {} func videoViewReadyToDisplay() {} func videoViewCompletedDisplay() {} @@ -152,6 +153,7 @@ class PBMRewardedVideoViewTest: XCTestCase, PBMCreativeResolutionDelegate, PBMCr func learnMoreWasClicked() {} func creativeViewWasClicked(_ creative: PBMAbstractCreative) {} func creativeFullScreenDidFinish(_ creative: PBMAbstractCreative) {} + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) {} // MARK: - Helper Methods private func setupVideoCreative(videoFileURL:String = "http://get_video/small.mp4", localVideoFileName:String = "small.mp4") { @@ -162,7 +164,7 @@ class PBMRewardedVideoViewTest: XCTestCase, PBMCreativeResolutionDelegate, PBMCr let model = PBMCreativeModel(adConfiguration:AdConfiguration()) model.videoFileURL = videoFileURL model.displayDurationInSeconds = 6 - model.adConfiguration?.isOptIn = true + model.adConfiguration?.isRewarded = true self.expectationDownloadCompleted = self.expectation(description: "expectationDownloadVideoData") diff --git a/PrebidMobileTests/RenderingTests/Tests/RewardedVideo/RewardedVideo_CompanionTest.swift b/PrebidMobileTests/RenderingTests/Tests/RewardedVideo/RewardedVideo_CompanionTest.swift index f4f886075..d4a973fad 100644 --- a/PrebidMobileTests/RenderingTests/Tests/RewardedVideo/RewardedVideo_CompanionTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/RewardedVideo/RewardedVideo_CompanionTest.swift @@ -180,7 +180,7 @@ class RewardedVideo_CompanionTest: XCTestCase { adConfiguration.adFormats = [.video] adConfiguration.isInterstitialAd = true - adConfiguration.isOptIn = true + adConfiguration.isRewarded = true return adConfiguration } } diff --git a/PrebidMobileTests/RenderingTests/Tests/VASTTests/PBMRewardedVideoCreativeTest.swift b/PrebidMobileTests/RenderingTests/Tests/VASTTests/PBMRewardedVideoCreativeTest.swift index bb15f9547..062c636e5 100644 --- a/PrebidMobileTests/RenderingTests/Tests/VASTTests/PBMRewardedVideoCreativeTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/VASTTests/PBMRewardedVideoCreativeTest.swift @@ -87,6 +87,7 @@ class PBMRewardedVideoCreativeTest: XCTestCase, PBMCreativeResolutionDelegate, P func creativeMraidDidCollapse(_ creative: PBMAbstractCreative) {} func creativeMraidDidExpand(_ creative: PBMAbstractCreative) {} func creativeFullScreenDidFinish(_ creative: PBMAbstractCreative) {} + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) {} func creativeReady(_ creative: PBMAbstractCreative) { self.expectationDownloadCompleted.fulfill() diff --git a/PrebidMobileTests/RenderingTests/Tests/VASTTests/PBMVideoCreativeTest.swift b/PrebidMobileTests/RenderingTests/Tests/VASTTests/PBMVideoCreativeTest.swift index d6f1bc9a3..4a924d71b 100644 --- a/PrebidMobileTests/RenderingTests/Tests/VASTTests/PBMVideoCreativeTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/VASTTests/PBMVideoCreativeTest.swift @@ -13,7 +13,6 @@  limitations under the License.  */ -import Foundation import UIKit import XCTest import AVFoundation @@ -31,6 +30,7 @@ class VideoCreativeDelegateTest: XCTestCase, PBMCreativeResolutionDelegate, PBMC var expectationDownloadFailed:XCTestExpectation! var expectationDidLeaveApp:XCTestExpectation! var expectationVideoViewCompletedDisplay:XCTestExpectation? + var expectationCreativeDidSendRewardedEvent:XCTestExpectation? var isVideoViewCompletedDisplay = false; private var logToFile: LogToFileLock? @@ -231,7 +231,7 @@ class VideoCreativeDelegateTest: XCTestCase, PBMCreativeResolutionDelegate, PBMC let mockModalManager = MockModalManager() self.videoCreative.modalManager = mockModalManager self.videoCreative.creativeModel?.hasCompanionAd = false - self.videoCreative.creativeModel?.adConfiguration?.isOptIn = false + self.videoCreative.creativeModel?.adConfiguration?.isRewarded = false self.videoCreative.creativeModel?.adConfiguration?.videoControlsConfig.skipDelay = 1000 mockModalManager.mock_pushModalClosure = { (modalState, _, _, _, completionHandler) in expectation.fulfill() @@ -324,7 +324,75 @@ class VideoCreativeDelegateTest: XCTestCase, PBMCreativeResolutionDelegate, PBMC waitForExpectations(timeout: 1) } - //MARK: - PBMCreativeResolutionDelegate + func testRewardEvent() { + let time: NSNumber = 5 + expectationCreativeDidSendRewardedEvent = expectation(description: "Reward event - creativeDidSendRewardedEvent called") + + let ortbRewarded = PBMORTBRewardedConfiguration() + ortbRewarded.completion = PBMORTBRewardedCompletion() + ortbRewarded.completion?.video = PBMORTBRewardedCompletionVideo() + ortbRewarded.completion?.video?.time = time + + let adConfiguration = AdConfiguration() + adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: ortbRewarded) + adConfiguration.isRewarded = true + + let creativeModel = PBMCreativeModel(adConfiguration: adConfiguration) + + self.videoCreative = PBMVideoCreative( + creativeModel:creativeModel, + transaction:UtilitiesForTesting.createEmptyTransaction(), + videoData:Data() + ) + + self.videoCreative.creativeResolutionDelegate = self + self.videoCreative.creativeViewDelegate = self + + self.videoCreative.videoViewCurrentPlayingTime(time) + + waitForExpectations(timeout: 1) + + // If video without endcard - we should show learn more button when reward completion is fired + XCTAssertTrue(videoCreative.videoView.showLearnMore) + } + + func testPostRewardEvent() { + let rewardTime: NSNumber = 5 + let postRewardTime: NSNumber = 2 + expectationCreativeDidSendRewardedEvent = expectation(description: "Reward event - creativeDidSendRewardedEvent called") + + let ortbRewarded = PBMORTBRewardedConfiguration() + ortbRewarded.completion = PBMORTBRewardedCompletion() + ortbRewarded.completion?.video = PBMORTBRewardedCompletionVideo() + ortbRewarded.completion?.video?.time = rewardTime + ortbRewarded.close = PBMORTBRewardedClose() + ortbRewarded.close?.postrewardtime = postRewardTime + + let adConfiguration = AdConfiguration() + adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: ortbRewarded) + adConfiguration.isRewarded = true + + let creativeModel = PBMCreativeModel(adConfiguration: adConfiguration) + + self.videoCreative = PBMVideoCreative( + creativeModel:creativeModel, + transaction:UtilitiesForTesting.createEmptyTransaction(), + videoData:Data() + ) + + self.videoCreative.creativeResolutionDelegate = self + self.videoCreative.creativeViewDelegate = self + + self.videoCreative.videoViewCurrentPlayingTime(rewardTime) + self.videoCreative.videoViewCurrentPlayingTime(postRewardTime) + + XCTAssertTrue(videoCreative.creativeModel!.userPostRewardEventSent) + + waitForExpectations(timeout: 1) + } + + + // MARK: - PBMCreativeResolutionDelegate func creativeReady(_ creative:PBMAbstractCreative) { self.expectationDownloadCompleted.fulfill() self.videoCreative.display(withRootViewController: UIViewController()) @@ -364,6 +432,12 @@ class VideoCreativeDelegateTest: XCTestCase, PBMCreativeResolutionDelegate, PBMC func creativeViewWasClicked(_ creative: PBMAbstractCreative) {} func videoViewReadyToDisplay() {} + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) { + expectationCreativeDidSendRewardedEvent?.fulfill() + } + + func videoViewCurrentPlayingTime(_ currentPlayingTime: NSNumber) {} + func videoViewCompletedDisplay() { isVideoViewCompletedDisplay = true self.expectationVideoViewCompletedDisplay?.fulfill() diff --git a/PrebidMobileTests/RenderingTests/Tests/VASTTests/RewardedVideoEventsTest.swift b/PrebidMobileTests/RenderingTests/Tests/VASTTests/RewardedVideoEventsTest.swift index 1ff5165e7..7312747fc 100644 --- a/PrebidMobileTests/RenderingTests/Tests/VASTTests/RewardedVideoEventsTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/VASTTests/RewardedVideoEventsTest.swift @@ -165,6 +165,7 @@ class RewardedVideoEventsTest : XCTestCase, PBMCreativeViewDelegate { func creativeInterstitialDidLeaveApp(_ creative:PBMAbstractCreative) {} func creativeViewWasClicked(_ creative: PBMAbstractCreative) {} func creativeFullScreenDidFinish(_ creative: PBMAbstractCreative) {} + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) {} //MARK: - Utility @@ -261,7 +262,7 @@ class RewardedVideoEventsTest : XCTestCase, PBMCreativeViewDelegate { let adConfiguration = AdConfiguration() adConfiguration.adFormats = [.video] adConfiguration.isInterstitialAd = true - adConfiguration.isOptIn = true + adConfiguration.isRewarded = true return adConfiguration } diff --git a/PrebidMobileTests/RenderingTests/Tests/VASTTests/VastEventTrackingTest.swift b/PrebidMobileTests/RenderingTests/Tests/VASTTests/VastEventTrackingTest.swift index d278ed812..0925878c7 100644 --- a/PrebidMobileTests/RenderingTests/Tests/VASTTests/VastEventTrackingTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/VASTTests/VastEventTrackingTest.swift @@ -151,7 +151,8 @@ class VastEventTrackingTest : XCTestCase, PBMCreativeViewDelegate { self.wait(for: [inlineVastRequestSuccessfulExpectation], timeout: 1) } - //MARK: - CreativeViewDelegate + // MARK: - CreativeViewDelegate + func videoCreativeDidComplete(_ creative: PBMAbstractCreative) {} func creativeDidComplete(_ creative:PBMAbstractCreative) {} func creativeWasClicked(_ creative: PBMAbstractCreative) {} @@ -164,6 +165,7 @@ class VastEventTrackingTest : XCTestCase, PBMCreativeViewDelegate { func creativeDidDisplay(_ creative: PBMAbstractCreative) {} func creativeViewWasClicked(_ creative: PBMAbstractCreative) {} func creativeFullScreenDidFinish(_ creative: PBMAbstractCreative) {} + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) {} //MARK: - Utility diff --git a/PrebidMobileTests/RenderingTests/Tests/VASTTests/VideoEventsTest.swift b/PrebidMobileTests/RenderingTests/Tests/VASTTests/VideoEventsTest.swift index 23cf66d30..7924bf52f 100644 --- a/PrebidMobileTests/RenderingTests/Tests/VASTTests/VideoEventsTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/VASTTests/VideoEventsTest.swift @@ -167,6 +167,7 @@ class VideoEventsTest : XCTestCase, PBMCreativeViewDelegate, PBMVideoViewDelegat func learnMoreWasClicked() {} func creativeViewWasClicked(_ creative: PBMAbstractCreative) {} func creativeFullScreenDidFinish(_ creative: PBMAbstractCreative) {} + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) {} // MARK: - PBMVideoViewDelegate @@ -178,4 +179,5 @@ class VideoEventsTest : XCTestCase, PBMCreativeViewDelegate, PBMVideoViewDelegat } func videoWasClicked() {} + func videoViewCurrentPlayingTime(_ currentPlayingTime: NSNumber) {} } diff --git a/PrebidMobileTests/RenderingTests/Tests/VASTTests/VideoFileTypeTest.swift b/PrebidMobileTests/RenderingTests/Tests/VASTTests/VideoFileTypeTest.swift index 9b4001e5a..1b5ba26c7 100644 --- a/PrebidMobileTests/RenderingTests/Tests/VASTTests/VideoFileTypeTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/VASTTests/VideoFileTypeTest.swift @@ -145,6 +145,8 @@ class VideoFileTypeTest : XCTestCase, PBMCreativeViewDelegate, PBMVideoViewDeleg func creativeViewWasClicked(_ creative: PBMAbstractCreative) {} func creativeFullScreenDidFinish(_ creative: PBMAbstractCreative) {} + func creativeDidSendRewardedEvent(_ creative: PBMAbstractCreative) {} + // MARK: - PBMVideoViewDelegate func videoViewFailedWithError(_ error: Error) {} @@ -155,4 +157,5 @@ class VideoFileTypeTest : XCTestCase, PBMCreativeViewDelegate, PBMVideoViewDeleg } func videoWasClicked() {} + func videoViewCurrentPlayingTime(_ currentPlayingTime: NSNumber) {} } From cb51b11d60ff50bf9a721a78b5ffd0318a947d01 Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Wed, 11 Dec 2024 09:54:26 +0200 Subject: [PATCH 07/23] Support ad position in Original, Rendering and Mediation ad units (#1066) * feat: support ad position in original, rendering and mediation ad units * feat: add adPosition to InterstitialRenderingAdUnit and RewardedAdUnit --- PrebidMobile.xcodeproj/project.pbxproj | 10 +++++- PrebidMobile/AdUnits/AdUnit.swift | 6 ++++ .../MultiformatAdUnit/PrebidAdUnit.swift | 1 + .../MultiformatAdUnit/PrebidRequest.swift | 14 ++++++-- .../PrebidMobileRendering/AdPosition.swift | 17 ++++++--- .../GAM/InterstitialRenderingAdUnit.swift | 6 ++++ .../Integrations/GAM/RewardedAdUnit.swift | 17 ++++++--- .../MediationBaseInterstitialAdUnit.swift | 6 ++++ .../AdUnitTests/AdUnitSuccessorTests.swift | 7 ++-- .../AdUnitTests/AdUnitTests.swift | 20 +++++++++++ .../AdUnitTests/PrebidAdUnitTests.swift | 15 ++++++++ .../InterstitialRenderingAdUnitTest.swift | 36 +++++++++++++++++++ .../MediationInterstitialAdUnitTest.swift | 18 ++++++++++ .../MediationAPI/MockMediationUtils.swift | 6 ++++ .../Tests/Prebid/RewardedAdUnitTest.swift | 36 +++++++++++++++++++ 15 files changed, 202 insertions(+), 13 deletions(-) create mode 100644 PrebidMobileTests/RenderingTests/Tests/Prebid/InterstitialRenderingAdUnitTest.swift create mode 100644 PrebidMobileTests/RenderingTests/Tests/Prebid/RewardedAdUnitTest.swift diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index 1fe7c0b11..39588dc73 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -100,6 +100,8 @@ 534C61462CB562E40026119A /* CloseActionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 534C61452CB562E40026119A /* CloseActionManagerTests.swift */; }; 535145E02CCB758800D40B19 /* NSObject+PBMExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 535145DF2CCB758800D40B19 /* NSObject+PBMExtensions.m */; }; 535145E12CCB758800D40B19 /* NSObject+PBMExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 535145DE2CCB758800D40B19 /* NSObject+PBMExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 53514CC62D01B5B900A480C0 /* InterstitialRenderingAdUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CC52D01B5AF00A480C0 /* InterstitialRenderingAdUnitTest.swift */; }; + 53514CC82D01B5F900A480C0 /* RewardedAdUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CC72D01B5F200A480C0 /* RewardedAdUnitTest.swift */; }; 5355ACA929C454070014F16E /* VAST_with_empty_companion.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5355ACA829C454070014F16E /* VAST_with_empty_companion.xml */; }; 5355ACAB29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355ACAA29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift */; }; 536A39262A84C50F00B1CCEA /* StringExtensionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536A39252A84C50F00B1CCEA /* StringExtensionsTest.swift */; }; @@ -985,6 +987,8 @@ 534C61452CB562E40026119A /* CloseActionManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseActionManagerTests.swift; sourceTree = ""; }; 535145DE2CCB758800D40B19 /* NSObject+PBMExtensions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject+PBMExtensions.h"; sourceTree = ""; }; 535145DF2CCB758800D40B19 /* NSObject+PBMExtensions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+PBMExtensions.m"; sourceTree = ""; }; + 53514CC52D01B5AF00A480C0 /* InterstitialRenderingAdUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterstitialRenderingAdUnitTest.swift; sourceTree = ""; }; + 53514CC72D01B5F200A480C0 /* RewardedAdUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RewardedAdUnitTest.swift; sourceTree = ""; }; 5355ACA829C454070014F16E /* VAST_with_empty_companion.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = VAST_with_empty_companion.xml; sourceTree = ""; }; 5355ACAA29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreativeModelCollectionMakerVASTTests.swift; sourceTree = ""; }; 536A39252A84C50F00B1CCEA /* StringExtensionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensionsTest.swift; sourceTree = ""; }; @@ -3051,6 +3055,7 @@ isa = PBXGroup; children = ( 925D5DA92737C4E800A8A2B5 /* AdLoadFlow */, + 927ADB2528058170006EB8D5 /* MediationAPI */, 925D5E082737DF5F00A8A2B5 /* PBMBannerViewTest.swift */, 925D5E112737E04B00A8A2B5 /* PBMBidRequesterTest.swift */, 925D5E132737E74C00A8A2B5 /* PBMBidResponseTransformerTest.swift */, @@ -3058,9 +3063,10 @@ 925D5E172737E7D200A8A2B5 /* PBMErrorTest.swift */, 925D5E1B2737E89400A8A2B5 /* PBMORTBTest.swift */, 925D5E1D2737E8BF00A8A2B5 /* PBMBaseInterstitialAdUnit_DelegationTest.swift */, - 927ADB2528058170006EB8D5 /* MediationAPI */, 925D5E2C2737ECEA00A8A2B5 /* PBMWinNotifierTest.swift */, 92F6720727F252E500EA3B8D /* BaseInterstitialAdUnitTest.swift */, + 53514CC52D01B5AF00A480C0 /* InterstitialRenderingAdUnitTest.swift */, + 53514CC72D01B5F200A480C0 /* RewardedAdUnitTest.swift */, 533135C5282A869800AA1E4D /* BidTest.swift */, ); path = Prebid; @@ -3868,6 +3874,7 @@ FAC837D82321583500565051 /* CollectionExtensionTest.swift in Sources */, 922AFCE82736FAAE00732C53 /* LogToFileLock.swift in Sources */, 600CF0702208963500C95176 /* UtilsTests.swift in Sources */, + 53514CC62D01B5B900A480C0 /* InterstitialRenderingAdUnitTest.swift in Sources */, 922AFD392737286D00732C53 /* PBMHTMLCreativeTest_ModalManagerDelegate.swift in Sources */, 6013E3F82215C82900FEE2B1 /* CLLocationManager+Swizzle.swift in Sources */, 925D5D722737B79D00A8A2B5 /* PBMTrackingRecordTest.swift in Sources */, @@ -3997,6 +4004,7 @@ 925D5D872737BAB300A8A2B5 /* PBMOpenMeasurementWrapperTest.swift in Sources */, 536A39262A84C50F00B1CCEA /* StringExtensionsTest.swift in Sources */, 925D5E782737F60100A8A2B5 /* VastEventTrackingTest.swift in Sources */, + 53514CC82D01B5F900A480C0 /* RewardedAdUnitTest.swift in Sources */, 533FDF852A12030C0066ED5A /* MockPrebidJSLibraryManager.swift in Sources */, 922AFD452737298200732C53 /* PBMHTMLCreativeTest_MRAIDPlayVideo.swift in Sources */, 47D9AD2385D2A68745D1AD77 /* MockLocationManager.swift in Sources */, diff --git a/PrebidMobile/AdUnits/AdUnit.swift b/PrebidMobile/AdUnits/AdUnit.swift index 15c29e380..51f5a68ed 100644 --- a/PrebidMobile/AdUnits/AdUnit.swift +++ b/PrebidMobile/AdUnits/AdUnit.swift @@ -23,6 +23,12 @@ public class AdUnit: NSObject, DispatcherDelegate { set { adUnitConfig.setPbAdSlot(newValue) } } + /// The position of the ad on the screen. + public var adPosition: AdPosition { + get { adUnitConfig.adPosition } + set { adUnitConfig.adPosition = newValue } + } + var adSizes: [CGSize] { get { [adUnitConfig.adSize] + (adUnitConfig.additionalSizes ?? []) } } diff --git a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift index cddc6f131..76e0b5287 100644 --- a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift +++ b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift @@ -126,6 +126,7 @@ public class PrebidAdUnit: NSObject { adUnit.adUnitConfig.adConfiguration.isInterstitialAd = request.isInterstitial adUnit.adUnitConfig.adConfiguration.isRewarded = request.isRewarded + adUnit.adUnitConfig.adPosition = request.adPosition if request.isInterstitial || request.isRewarded { adUnit.adUnitConfig.adPosition = .fullScreen diff --git a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift index 46794377b..5208570bb 100644 --- a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift +++ b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift @@ -19,6 +19,11 @@ import Foundation @objcMembers public class PrebidRequest: NSObject { + // MARK: - Public properties + + /// The position of the ad on the screen. + public var adPosition: AdPosition = .undefined + // MARK: - Internal properties private(set) var bannerParameters: BannerParameters? @@ -45,8 +50,13 @@ public class PrebidRequest: NSObject { /// - nativeParameters: The native parameters for the ad request. /// - isInterstitial: Indicates if the request is for an interstitial ad. /// - isRewarded: Indicates if the request is for a rewarded ad. - public init(bannerParameters: BannerParameters? = nil, videoParameters: VideoParameters? = nil, - nativeParameters: NativeParameters? = nil, isInterstitial: Bool = false, isRewarded: Bool = false) { + public init( + bannerParameters: BannerParameters? = nil, + videoParameters: VideoParameters? = nil, + nativeParameters: NativeParameters? = nil, + isInterstitial: Bool = false, + isRewarded: Bool = false + ) { self.bannerParameters = bannerParameters self.videoParameters = videoParameters self.nativeParameters = nativeParameters diff --git a/PrebidMobile/PrebidMobileRendering/AdPosition.swift b/PrebidMobile/PrebidMobileRendering/AdPosition.swift index a062c2fe1..6c068bf29 100644 --- a/PrebidMobile/PrebidMobileRendering/AdPosition.swift +++ b/PrebidMobile/PrebidMobileRendering/AdPosition.swift @@ -33,16 +33,25 @@ import Foundation public enum AdPosition: Int { /// 0 Unknown - case undefined = 0 + case undefined = 0 + + /// 1 Above the Fold + case aboveTheFold = 1 + + /// 2 Locked (i.e. fixed position) + case locked = 2 + + /// 3 Below the Fold + case belowTheFold = 3 /// 4 Header - case header = 4 + case header = 4 /// 5 Footer - case footer = 5 + case footer = 5 /// 6 Sidebar - case sidebar = 6 + case sidebar = 6 /// 7 Full Screen case fullScreen = 7 diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift index f4e69cbb7..f0420c478 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift @@ -33,6 +33,12 @@ public class InterstitialRenderingAdUnit: NSObject, BaseInterstitialAdUnitProtoc set { adUnitConfig.adFormats = newValue } } + /// The position of the ad on the screen. + public var adPosition: AdPosition { + get { adUnitConfig.adPosition } + set { adUnitConfig.adPosition = newValue } + } + /// The ORTB (OpenRTB) configuration string for the ad unit. public var ortbConfig: String? { get { adUnitConfig.ortbConfig } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift index 4c5c757c7..8cfb0b53d 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift @@ -33,6 +33,12 @@ public class RewardedAdUnit: NSObject, BaseInterstitialAdUnitProtocol { set { adUnitConfig.adFormats = newValue } } + /// The position of the ad on the screen. + public var adPosition: AdPosition { + get { adUnitConfig.adPosition } + set { adUnitConfig.adPosition = newValue } + } + /// The ORTB (OpenRTB) configuration string for the ad unit. public var ortbConfig: String? { get { adUnitConfig.ortbConfig } @@ -75,14 +81,17 @@ public class RewardedAdUnit: NSObject, BaseInterstitialAdUnitProtocol { set { adUnitConfig.adConfiguration.videoControlsConfig.isSoundButtonVisible = newValue } } - // MARK: Private properties - - private let baseAdUnit: BaseRewardedAdUnit + // MARK: Internal Properties - private var adUnitConfig: AdUnitConfig { + // Note: exposed for tests + var adUnitConfig: AdUnitConfig { baseAdUnit.adUnitConfig } + // MARK: Private properties + + private let baseAdUnit: BaseRewardedAdUnit + private var eventHandler: PBMPrimaryAdRequesterProtocol { baseAdUnit.eventHandler } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift index f67e23387..a2251e46d 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift @@ -28,6 +28,12 @@ public class MediationBaseInterstitialAdUnit : NSObject { get { adUnitConfig.adConfiguration.videoParameters } } + /// The position of the ad on the screen. + public var adPosition: AdPosition { + get { adUnitConfig.adPosition } + set { adUnitConfig.adPosition = newValue } + } + /// Indicates whether the video ad is muted. public var isMuted: Bool { get { adUnitConfig.adConfiguration.videoControlsConfig.isMuted } diff --git a/PrebidMobileTests/AdUnitTests/AdUnitSuccessorTests.swift b/PrebidMobileTests/AdUnitTests/AdUnitSuccessorTests.swift index 4650891b0..14bd020f2 100644 --- a/PrebidMobileTests/AdUnitTests/AdUnitSuccessorTests.swift +++ b/PrebidMobileTests/AdUnitTests/AdUnitSuccessorTests.swift @@ -26,7 +26,7 @@ class AdUnitSuccessorTests: XCTestCase { Prebid.shared.useExternalClickthroughBrowser = false } - //MARK: - BannerAdUnit + // MARK: - BannerAdUnit func testBannerAdUnitCreation() { //when @@ -51,7 +51,8 @@ class AdUnitSuccessorTests: XCTestCase { bannerBasedAdUnits.forEach { checkBannerParametersHelper($0) } } - //MARK: - InterstitialAdUnit + // MARK: - InterstitialAdUnit + func testInterstitialAdUnitCreation() { //when let adUnit = InterstitialAdUnit(configId: Constants.configID1) @@ -86,6 +87,7 @@ class AdUnitSuccessorTests: XCTestCase { } //MARK: - VideoAdUnit + func testVideoAdUnitCreation() { //when let adUnit = VideoAdUnit(configId: Constants.configID1, size: CGSize(width: Constants.width1, height: Constants.height1)) @@ -95,6 +97,7 @@ class AdUnitSuccessorTests: XCTestCase { } //MARK: - VideoInterstitialAdUnit + func testVideoInterstitialAdUnitCreation() { //when let adUnit = VideoInterstitialAdUnit(configId: Constants.configID1) diff --git a/PrebidMobileTests/AdUnitTests/AdUnitTests.swift b/PrebidMobileTests/AdUnitTests/AdUnitTests.swift index a46c600b5..bc6c62315 100644 --- a/PrebidMobileTests/AdUnitTests/AdUnitTests.swift +++ b/PrebidMobileTests/AdUnitTests/AdUnitTests.swift @@ -702,4 +702,24 @@ class AdUnitTests: XCTestCase { let objects2 = adUnit.getUserData()! XCTAssertEqual(0, objects2.count) } + + func testAdUnitSetAdPosition() { + let adUnit = AdUnit( + configId: "test", + size: CGSize(width: 300, height: 250), + adFormats: [.banner, .video] + ) + + let adUnitConfig = adUnit.adUnitConfig + + adUnit.adPosition = .header + + XCTAssertEqual(adUnit.adPosition, adUnitConfig.adPosition) + XCTAssertEqual(adUnitConfig.adPosition, .header) + + adUnit.adPosition = .footer + + XCTAssertEqual(adUnit.adPosition, adUnitConfig.adPosition) + XCTAssertEqual(adUnitConfig.adPosition, .footer) + } } diff --git a/PrebidMobileTests/AdUnitTests/PrebidAdUnitTests.swift b/PrebidMobileTests/AdUnitTests/PrebidAdUnitTests.swift index 4f1840d40..3e8d7ddc8 100644 --- a/PrebidMobileTests/AdUnitTests/PrebidAdUnitTests.swift +++ b/PrebidMobileTests/AdUnitTests/PrebidAdUnitTests.swift @@ -402,4 +402,19 @@ class PrebidAdUnitTests: XCTestCase { waitForExpectations(timeout: 5, handler: nil) } + + func testSetAdPosition() { + let request = PrebidRequest(bannerParameters: BannerParameters()) + request.adPosition = .header + + let adUnit = PrebidAdUnit(configId: "test-config-id") + adUnit.fetchDemand(request: request, completion: { _ in }) + + XCTAssertEqual(adUnit.getConfiguration().adPosition, .header) + + request.adPosition = .footer + adUnit.fetchDemand(request: request, completion: { _ in }) + + XCTAssertEqual(adUnit.getConfiguration().adPosition, .footer) + } } diff --git a/PrebidMobileTests/RenderingTests/Tests/Prebid/InterstitialRenderingAdUnitTest.swift b/PrebidMobileTests/RenderingTests/Tests/Prebid/InterstitialRenderingAdUnitTest.swift new file mode 100644 index 000000000..d1e551231 --- /dev/null +++ b/PrebidMobileTests/RenderingTests/Tests/Prebid/InterstitialRenderingAdUnitTest.swift @@ -0,0 +1,36 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import XCTest +@testable import PrebidMobile + +class InterstitialRenderingAdUnitTest: XCTestCase { + + func testSetAdPosition() { + let adUnit = InterstitialRenderingAdUnit(configID: "test") + + let adUnitConfig = adUnit.adUnitConfig + + adUnit.adPosition = .header + + XCTAssertEqual(adUnit.adPosition, adUnitConfig.adPosition) + XCTAssertEqual(adUnitConfig.adPosition, .header) + + adUnit.adPosition = .footer + + XCTAssertEqual(adUnit.adPosition, adUnitConfig.adPosition) + XCTAssertEqual(adUnitConfig.adPosition, .footer) + } +} diff --git a/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MediationInterstitialAdUnitTest.swift b/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MediationInterstitialAdUnitTest.swift index 04f4a7f84..8006fad82 100644 --- a/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MediationInterstitialAdUnitTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MediationInterstitialAdUnitTest.swift @@ -101,4 +101,22 @@ class MediationInterstitialAdUnitTest: XCTestCase { waitForExpectations(timeout: 0.1) } + func testSetAdPosition() { + let adUnit = MediationBaseInterstitialAdUnit( + configId: "test", + mediationDelegate: MockEmptyPrebidMediationDelegate() + ) + + let adUnitConfig = adUnit.adUnitConfig + + adUnit.adPosition = .header + + XCTAssertEqual(adUnit.adPosition, adUnitConfig.adPosition) + XCTAssertEqual(adUnitConfig.adPosition, .header) + + adUnit.adPosition = .footer + + XCTAssertEqual(adUnit.adPosition, adUnitConfig.adPosition) + XCTAssertEqual(adUnitConfig.adPosition, .footer) + } } diff --git a/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MockMediationUtils.swift b/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MockMediationUtils.swift index 2b47d30f7..08295b146 100644 --- a/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MockMediationUtils.swift +++ b/PrebidMobileTests/RenderingTests/Tests/Prebid/MediationAPI/MockMediationUtils.swift @@ -30,6 +30,12 @@ fileprivate let HBKeywordPrefix = "hb_" @objc var localExtras: [AnyHashable : Any]? } +class MockEmptyPrebidMediationDelegate: PrebidMediationDelegate { + func setUpAdObject(with values: [String : Any]) -> Bool { true } + func cleanUpAdObject() {} + func getAdView() -> UIView? { UIView(frame: .zero)} +} + class MockMediationUtils: PrebidMediationDelegate { let adObject: MockAdObject diff --git a/PrebidMobileTests/RenderingTests/Tests/Prebid/RewardedAdUnitTest.swift b/PrebidMobileTests/RenderingTests/Tests/Prebid/RewardedAdUnitTest.swift new file mode 100644 index 000000000..a0aeb75dc --- /dev/null +++ b/PrebidMobileTests/RenderingTests/Tests/Prebid/RewardedAdUnitTest.swift @@ -0,0 +1,36 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import XCTest +@testable import PrebidMobile + +class RewardedAdUnitTest: XCTestCase { + + func testSetAdPosition() { + let adUnit = RewardedAdUnit(configID: "test") + + let adUnitConfig = adUnit.adUnitConfig + + adUnit.adPosition = .header + + XCTAssertEqual(adUnit.adPosition, adUnitConfig.adPosition) + XCTAssertEqual(adUnitConfig.adPosition, .header) + + adUnit.adPosition = .footer + + XCTAssertEqual(adUnit.adPosition, adUnitConfig.adPosition) + XCTAssertEqual(adUnitConfig.adPosition, .footer) + } +} From b0389abac0aab0f4dd9a90cf808e692d20b50ae2 Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:37:52 +0200 Subject: [PATCH 08/23] Send pbadslot ORTB field (#1074) --- .../PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift | 1 + .../Prebid/PBMCore/PBMPrebidParameterBuilder.m | 6 +++++- .../ParameterBuilderTests/PrebidParameterBuilderTest.swift | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift index 7ded2a488..7d6ef7d7f 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift @@ -275,6 +275,7 @@ public class AdUnitConfig: NSObject, NSCopying { // MARK: - The Prebid Ad Slot public func setPbAdSlot(_ newElement: String?) { + Log.warn("Prebid SDK will stop sending `imp[].ext.data.adslot` field soon. If you still need it, add a comment to: https://github.com/prebid/prebid-mobile-android/issues/810.") pbAdSlot = newElement } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m index 67e76ea70..9db0a2fdc 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m @@ -201,7 +201,11 @@ - (void)buildBidRequest:(nonnull PBMORTBBidRequest *)bidRequest { nextImp.extKeywords = [extKeywords componentsJoinedByString:@","]; } - nextImp.extData[@"adslot"] = [self.adConfiguration getPbAdSlot]; + NSString * pbAdSlot = [self.adConfiguration getPbAdSlot]; + + // NOTE: `adslot` will be removed in future versions of Prebid SDK. + nextImp.extData[@"adslot"] = pbAdSlot; + nextImp.extData[@"pbadslot"] = pbAdSlot; for (AdFormat* adFormat in adFormats) { if (adFormat == AdFormat.banner || adFormat == AdFormat.display) { diff --git a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift index 9de2d84be..65464da38 100644 --- a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift @@ -236,6 +236,7 @@ class PrebidParameterBuilderTest: XCTestCase { XCTAssertEqual(Set(result), Set(["value1", "value2"])) XCTAssertEqual(extData["adslot"] as? String, testAdSlot) + XCTAssertEqual(extData["pbadslot"] as? String, testAdSlot) } } From 0d6d691b410c791501e14ac5b781075698b15233 Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:53:39 +0200 Subject: [PATCH 09/23] Arbitrary ORTB request configuration changes (#1064) * feat: add global ortbConfig to Targeting * feat: implement arbitrary ORTB service * test: add unit tests for arbitrary ORTB service * feat: undo chnages for ORTBBidRequest project visibility * refactor: remove unneeded tests * feat: arbitrary ORTB service improvements * feat: mark setImpressionORTBConfig and getImpressionORTBConfig as @objc * feat: add examples of setting arbitrary ORTB to demo apps * fix: add missing reset of global config * feat: rename setImpressionORTBConfig to setImpORTBConfig & deprecate obsolete API * feat: bring back removed old arbitrary ORTB API --- ...AMOriginalAPIDisplayBannerViewController.m | 34 +- ...iginalAPIDisplayBannerViewController.swift | 32 +- PrebidMobile.xcodeproj/project.pbxproj | 30 +- PrebidMobile/AdUnits/AdUnit.swift | 24 +- .../MultiformatAdUnit/PrebidAdUnit.swift | 3 +- .../MultiformatAdUnit/PrebidRequest.swift | 16 +- .../ConfigurationAndTargeting/Targeting.swift | 16 + .../AdTypes/AdView/AdConfiguration.swift | 4 + .../Parameters/ArbitraryORTBHelper.swift | 120 +++ .../Parameters/ArbitraryORTBService.swift | 53 ++ .../Parameters/PBMORTBParameterBuilder.h | 2 +- .../Parameters/PBMORTBParameterBuilder.m | 12 +- .../Parameters/PBMParameterBuilderService.h | 1 + .../Parameters/PBMParameterBuilderService.m | 18 +- .../ORTB/PBMORTBBidRequest.h | 6 +- .../ORTB/PBMORTBBidRequest.m | 3 +- .../Prebid/Integrations/GAM/BannerView.swift | 15 + .../GAM/InterstitialRenderingAdUnit.swift | 15 + .../Integrations/GAM/RewardedAdUnit.swift | 15 + .../MediationAPI/MediationBannerAdUnit.swift | 17 +- .../MediationBaseInterstitialAdUnit.swift | 21 +- .../MediationAPI/MediationNativeAdUnit.swift | 26 +- .../Prebid/PBMCore/AdUnitConfig.swift | 7 +- PrebidMobile/Utils/CollectionExtension.swift | 67 +- .../AdUnitTests/AdUnitTests.swift | 2 +- .../Tests/PBMORTBAbstractTest.swift | 42 +- .../Tests/PBMORTBParameterBuilderTest.swift | 16 +- .../ArbitraryORTBHelperTests.swift | 140 ++++ .../ArbitraryORTBServiceTests.swift | 691 ++++++++++++++++++ .../PrebidParameterBuilderTest.swift | 2 + .../UnitTests/CollectionExtensionTest.swift | 200 ++++- 31 files changed, 1552 insertions(+), 98 deletions(-) create mode 100644 PrebidMobile/PrebidMobileRendering/Networking/Parameters/ArbitraryORTBHelper.swift create mode 100644 PrebidMobile/PrebidMobileRendering/Networking/Parameters/ArbitraryORTBService.swift create mode 100644 PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/ArbitraryORTBHelperTests.swift create mode 100644 PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/ArbitraryORTBServiceTests.swift diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/OriginalAPI/GAMOriginalAPIDisplayBannerViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/OriginalAPI/GAMOriginalAPIDisplayBannerViewController.m index 694270544..6aa18f8e3 100644 --- a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/OriginalAPI/GAMOriginalAPIDisplayBannerViewController.m +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/GAM/OriginalAPI/GAMOriginalAPIDisplayBannerViewController.m @@ -40,16 +40,40 @@ - (void)loadView { [self createAd]; } +- (void)dealloc { + // Clean up global ORTB config + [Targeting.shared setGlobalORTBConfig:nil]; +} + - (void)createAd { - // 1. Create a BannerAdUnit + // 1. Set global ORTB + NSString *globalORTB = [NSString stringWithFormat: + @"{" + "\"ext\": {" + "\"myext\": {" + "\"test\": 1" + "}" + "}," + "\"displaymanager\": \"Google\"," + "\"displaymanagerver\": \"%@\"" + "}", + GADGetStringFromVersionNumber([GADMobileAds sharedInstance].versionNumber) + ]; + + [Targeting.shared setGlobalORTBConfig:globalORTB]; + + // 2. Create a BannerAdUnit self.adUnit = [[BannerAdUnit alloc] initWithConfigId:storedImpDisplayBanner size:self.adSize]; - // 2. Configure banner parameters + // 3. Configure banner parameters BannerParameters * parameters = [[BannerParameters alloc] init]; parameters.api = @[PBApi.MRAID_2]; self.adUnit.bannerParameters = parameters; - // 3. Create a GAMBannerView + // 4. Set impression-level ORTB + [self.adUnit setImpORTBConfig:@"{\"bidfloor\":0.01,\"banner\":{\"battr\":[1,2,3,4]}}"]; + + // 5. Create a GAMBannerView GAMRequest * gamRequest = [GAMRequest new]; self.gamBanner = [[GAMBannerView alloc] initWithAdSize:GADAdSizeFromCGSize(self.adSize)]; self.gamBanner.adUnitID = gamAdUnitDisplayBannerOriginal; @@ -59,13 +83,13 @@ - (void)createAd { // Add GMA SDK banner view to the app UI [self.bannerView addSubview:self.gamBanner]; - // 4. Make a bid request to Prebid Server + // 6. Make a bid request to Prebid Server @weakify(self); [self.adUnit fetchDemandWithAdObject:gamRequest completion:^(enum ResultCode resultCode) { @strongify(self); if (!self) { return; } - // 5. Load GAM Ad + // 7. Load GAM Ad [self.gamBanner loadRequest:gamRequest]; }]; } diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/OriginalAPI/GAMOriginalAPIDisplayBannerViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/OriginalAPI/GAMOriginalAPIDisplayBannerViewController.swift index a5c3d79f5..aea4db635 100644 --- a/Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/OriginalAPI/GAMOriginalAPIDisplayBannerViewController.swift +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/GAM/OriginalAPI/GAMOriginalAPIDisplayBannerViewController.swift @@ -34,17 +34,39 @@ class GAMOriginalAPIDisplayBannerViewController: BannerBaseViewController, GADBa createAd() } + deinit { + // Clean up global ORTB config + Targeting.shared.setGlobalORTBConfig(nil) + } + func createAd() { - // 1. Create a BannerAdUnit + // 1. Set global ORTB + let globalORTB = """ + { + "ext": { + "myext": { + "test": 1 + } + }, + "displaymanager": "Google", + "displaymanagerver": "\(GADGetStringFromVersionNumber(GADMobileAds.sharedInstance().versionNumber))" + } + """ + Targeting.shared.setGlobalORTBConfig(globalORTB) + + // 2. Create a BannerAdUnit adUnit = BannerAdUnit(configId: storedImpDisplayBanner, size: adSize) adUnit.setAutoRefreshMillis(time: 30000) - // 2. Configure banner parameters + // 3. Configure banner parameters let parameters = BannerParameters() parameters.api = [Signals.Api.MRAID_2] adUnit.bannerParameters = parameters - // 3. Create a GAMBannerView + // 4. Set impression-level ORTB + adUnit.setImpORTBConfig("{\"bidfloor\":0.01,\"banner\":{\"battr\":[1,2,3,4]}}") + + // 5. Create a GAMBannerView gamBanner = GAMBannerView(adSize: GADAdSizeFromCGSize(adSize)) gamBanner.adUnitID = gamAdUnitDisplayBannerOriginal gamBanner.rootViewController = self @@ -53,12 +75,12 @@ class GAMOriginalAPIDisplayBannerViewController: BannerBaseViewController, GADBa // Add GMA SDK banner view to the app UI bannerView?.addSubview(gamBanner) - // 4. Make a bid request to Prebid Server + // 6. Make a bid request to Prebid Server let gamRequest = GAMRequest() adUnit.fetchDemand(adObject: gamRequest) { [weak self] resultCode in PrebidDemoLogger.shared.info("Prebid demand fetch for GAM \(resultCode.name())") - // 5. Load GAM Ad + // 7. Load GAM Ad self?.gamBanner.load(gamRequest) } } diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index 39588dc73..38eb9de04 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -70,6 +70,10 @@ 53138B2E2A713A7800B18B5C /* PrebidGAMVersionCheckerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53138B2D2A713A7800B18B5C /* PrebidGAMVersionCheckerTest.swift */; }; 531CF21927E8FC1B005E5ABE /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531CF21827E8FC1B005E5ABE /* LogLevel.swift */; }; 53269D55282E6D0F0098550D /* ServerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53269D54282E6D0F0098550D /* ServerEvent.swift */; }; + 532936792CEB887D0056FD8D /* ArbitraryORTBService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 532936782CEB887D0056FD8D /* ArbitraryORTBService.swift */; }; + 5329367B2CEBE92F0056FD8D /* ArbitraryORTBHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5329367A2CEBE92F0056FD8D /* ArbitraryORTBHelper.swift */; }; + 5329367E2CEC92340056FD8D /* ArbitraryORTBServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5329367D2CEC92340056FD8D /* ArbitraryORTBServiceTests.swift */; }; + 532936932CEF1FFE0056FD8D /* ArbitraryORTBHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 532936922CEF1FFE0056FD8D /* ArbitraryORTBHelperTests.swift */; }; 533135C6282A869800AA1E4D /* BidTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 533135C5282A869800AA1E4D /* BidTest.swift */; }; 53322AA3282D45EE0049229D /* PrebidServerEventTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53322AA2282D45EE0049229D /* PrebidServerEventTracker.swift */; }; 53322AA8282E29910049229D /* EventManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53322AA7282E29910049229D /* EventManager.swift */; }; @@ -958,6 +962,10 @@ 53138B2D2A713A7800B18B5C /* PrebidGAMVersionCheckerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidGAMVersionCheckerTest.swift; sourceTree = ""; }; 531CF21827E8FC1B005E5ABE /* LogLevel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogLevel.swift; sourceTree = ""; }; 53269D54282E6D0F0098550D /* ServerEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerEvent.swift; sourceTree = ""; }; + 532936782CEB887D0056FD8D /* ArbitraryORTBService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArbitraryORTBService.swift; sourceTree = ""; }; + 5329367A2CEBE92F0056FD8D /* ArbitraryORTBHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArbitraryORTBHelper.swift; sourceTree = ""; }; + 5329367D2CEC92340056FD8D /* ArbitraryORTBServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArbitraryORTBServiceTests.swift; sourceTree = ""; }; + 532936922CEF1FFE0056FD8D /* ArbitraryORTBHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArbitraryORTBHelperTests.swift; sourceTree = ""; }; 533135C5282A869800AA1E4D /* BidTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BidTest.swift; sourceTree = ""; }; 53322AA2282D45EE0049229D /* PrebidServerEventTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidServerEventTracker.swift; sourceTree = ""; }; 53322AA7282E29910049229D /* EventManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventManager.swift; sourceTree = ""; }; @@ -2736,10 +2744,15 @@ 5BC3788A271F1CFF00444D5E /* Parameters */ = { isa = PBXGroup; children = ( - 5BC3788B271F1CFF00444D5E /* PBMAppInfoParameterBuilder.h */, - 5BC37898271F1CFF00444D5E /* PBMAppInfoParameterBuilder.m */, + 5BC3789B271F1CFF00444D5E /* PBMParameterBuilderProtocol.h */, + 5BC3789C271F1CFF00444D5E /* PBMParameterBuilderService.h */, + 5BC3788E271F1CFF00444D5E /* PBMParameterBuilderService.m */, + 5BC37893271F1CFF00444D5E /* PBMORTBParameterBuilder.h */, + 5BC3789D271F1CFF00444D5E /* PBMORTBParameterBuilder.m */, 5BC3789A271F1CFF00444D5E /* PBMBasicParameterBuilder.h */, 5BC3788F271F1CFF00444D5E /* PBMBasicParameterBuilder.m */, + 5BC3788B271F1CFF00444D5E /* PBMAppInfoParameterBuilder.h */, + 5BC37898271F1CFF00444D5E /* PBMAppInfoParameterBuilder.m */, 5BC37890271F1CFF00444D5E /* PBMBundleProtocol.h */, 5BC3788C271F1CFF00444D5E /* PBMDeviceInfoParameterBuilder.h */, 5BC37897271F1CFF00444D5E /* PBMDeviceInfoParameterBuilder.m */, @@ -2747,15 +2760,12 @@ 5BC37895271F1CFF00444D5E /* PBMGeoLocationParameterBuilder.m */, 5BC37899271F1CFF00444D5E /* PBMNetworkParameterBuilder.h */, 5BC37891271F1CFF00444D5E /* PBMNetworkParameterBuilder.m */, - 5BC37893271F1CFF00444D5E /* PBMORTBParameterBuilder.h */, - 5BC3789D271F1CFF00444D5E /* PBMORTBParameterBuilder.m */, - 5BC3789B271F1CFF00444D5E /* PBMParameterBuilderProtocol.h */, - 5BC3789C271F1CFF00444D5E /* PBMParameterBuilderService.h */, - 5BC3788E271F1CFF00444D5E /* PBMParameterBuilderService.m */, 5BC37896271F1CFF00444D5E /* PBMSKAdNetworksParameterBuilder.h */, 5BC3788D271F1CFF00444D5E /* PBMSKAdNetworksParameterBuilder.m */, 5BC3789E271F1CFF00444D5E /* PBMUserConsentParameterBuilder.h */, 5BC37892271F1CFF00444D5E /* PBMUserConsentParameterBuilder.m */, + 532936782CEB887D0056FD8D /* ArbitraryORTBService.swift */, + 5329367A2CEBE92F0056FD8D /* ArbitraryORTBHelper.swift */, ); path = Parameters; sourceTree = ""; @@ -3047,6 +3057,8 @@ 925D5DA42737C3EA00A8A2B5 /* PBMUserConsentParameterBuilderTest.swift */, 925D5DA62737C44B00A8A2B5 /* PrebidParameterBuilderTest.swift */, 53ED304B29798585007D13EE /* SkadnParameterBuilderTest.swift */, + 5329367D2CEC92340056FD8D /* ArbitraryORTBServiceTests.swift */, + 532936922CEF1FFE0056FD8D /* ArbitraryORTBHelperTests.swift */, ); path = ParameterBuilderTests; sourceTree = ""; @@ -3903,6 +3915,7 @@ 922AFD4B27372A0500732C53 /* PBMHTMLCreativeTest_NoMRAID.swift in Sources */, 9743CB86235F264B002E2CAA /* NativeEventTrackerTests.swift in Sources */, 922AFCE12735424F00732C53 /* PBMAssert.swift in Sources */, + 532936932CEF1FFE0056FD8D /* ArbitraryORTBHelperTests.swift in Sources */, 925D5E362737EE8600A8A2B5 /* PBMAdRequestResponseVASTTests.swift in Sources */, 922AFCD627353C8000732C53 /* MockServerURLProtocol.m in Sources */, 922AFD152737192300732C53 /* AdViewButtonDecoratorTests.swift in Sources */, @@ -4032,6 +4045,7 @@ 47D9AE6C09857CB297B6D8EA /* MockPBMAdLoadManagerVAST.swift in Sources */, 922AFD022737108200732C53 /* BasicParameterBuilderPureTargetingTest.swift in Sources */, 922AFD1C27371C7F00732C53 /* PBMCircularProgressBarViewTests.swift in Sources */, + 5329367E2CEC92340056FD8D /* ArbitraryORTBServiceTests.swift in Sources */, 47D9A3EF88B06B204F40463E /* MockPBMCreativeModel.swift in Sources */, 922AFD5027372AD900732C53 /* PBMHTMLFormatterTest.swift in Sources */, 53B221D32A0E3DA900C91CCB /* PrebidJSLibraryManagerTests.swift in Sources */, @@ -4288,6 +4302,7 @@ 5BC37A8D271F1D0000444D5E /* InterstitialRenderingAdUnit.swift in Sources */, 5BC379C0271F1D0000444D5E /* PBMInterstitialDisplayProperties.m in Sources */, 53D93505282CEA900092E243 /* DateFormatService.swift in Sources */, + 532936792CEB887D0056FD8D /* ArbitraryORTBService.swift in Sources */, 924F661E27FDBC3100C8DAF7 /* PBMORTBExtPrebidPassthrough.m in Sources */, 5BC37A51271F1D0000444D5E /* PBMPrebidParameterBuilder.m in Sources */, 92C85D6427A979F20080BAC5 /* NativeLink.swift in Sources */, @@ -4399,6 +4414,7 @@ 5BC3796B271F1D0000444D5E /* PBMVastCreativeAbstract.m in Sources */, 9266456B272AB9610064F7BD /* SkadnParametersManager.swift in Sources */, 5BC379D0271F1D0000444D5E /* PBMAdLoadManagerVAST.m in Sources */, + 5329367B2CEBE92F0056FD8D /* ArbitraryORTBHelper.swift in Sources */, 537B65402833C091008AE9D1 /* NetworkType.swift in Sources */, 53A657A02A860F7200AE0B4F /* CacheExpiryDelegateWrapper.swift in Sources */, 9298964F275FE61200D3C174 /* PBMORTBContentProducer.m in Sources */, diff --git a/PrebidMobile/AdUnits/AdUnit.swift b/PrebidMobile/AdUnits/AdUnit.swift index 51f5a68ed..475edbddc 100644 --- a/PrebidMobile/AdUnits/AdUnit.swift +++ b/PrebidMobile/AdUnits/AdUnit.swift @@ -415,23 +415,31 @@ public class AdUnit: NSObject, DispatcherDelegate { return adUnitConfig.gpid } - // MARK: Global ORTBObject + // MARK: Arbitrary ORTB Configuration - /// Sets the OpenRTB configuration string for the ad unit. - /// - /// - Parameter ortbObject: The OpenRTB configuration string to set. Can be `nil` to clear the configuration. + @available(*, deprecated, message: "Deprecated. Use setImpORTBConfig(_:) for impression-level ORTB configuration.") public func setOrtbConfig(_ ortbObject: String?) { adUnitConfig.ortbConfig = ortbObject } - /// Retrieves the current OpenRTB configuration string for the ad unit. - /// - /// - Returns: The OpenRTB configuration string, or `nil` if no configuration is set. + @available(*, deprecated, message: "Deprecated. Use getImpORTBConfig() for impression-level ORTB configuration.") public func getOrtbConfig() -> String? { return adUnitConfig.ortbConfig } - // MARK: - others + /// Sets the impression-level OpenRTB configuration string for the ad unit. + /// + /// - Parameter ortbObject: The impression-level OpenRTB configuration string to set. Can be `nil` to clear the configuration. + public func setImpORTBConfig(_ ortbConfig: String?) { + adUnitConfig.impORTBConfig = ortbConfig + } + + /// Returns the impression-level OpenRTB configuration string. + public func getImpORTBConfig() -> String? { + adUnitConfig.impORTBConfig + } + + // MARK: - Others /// This method allows to set the auto refresh period for the demand diff --git a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift index 76e0b5287..9a8ba0aa7 100644 --- a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift +++ b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidAdUnit.swift @@ -127,7 +127,8 @@ public class PrebidAdUnit: NSObject { adUnit.adUnitConfig.adConfiguration.isInterstitialAd = request.isInterstitial adUnit.adUnitConfig.adConfiguration.isRewarded = request.isRewarded adUnit.adUnitConfig.adPosition = request.adPosition - + adUnit.adUnitConfig.impORTBConfig = request.getImpORTBConfig() + if request.isInterstitial || request.isRewarded { adUnit.adUnitConfig.adPosition = .fullScreen adUnit.adUnitConfig.adConfiguration.videoParameters.placement = .Interstitial diff --git a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift index 5208570bb..9ca056ac1 100644 --- a/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift +++ b/PrebidMobile/AdUnits/MultiformatAdUnit/PrebidRequest.swift @@ -34,7 +34,7 @@ public class PrebidRequest: NSObject { private(set) var isRewarded = false private(set) var gpid: String? - private(set) var ortbConfig: String? + private var impORTBConfig: String? // MARK: - Private properties @@ -74,6 +74,20 @@ public class PrebidRequest: NSObject { self.gpid = gpid } + // MARK: Arbitrary ORTB Configuration + + /// Sets the impression-level OpenRTB configuration string for the ad unit. + /// + /// - Parameter ortbObject: The impression-level OpenRTB configuration string to set. Can be `nil` to clear the configuration. + public func setImpORTBConfig(_ ortbConfig: String?) { + self.impORTBConfig = ortbConfig + } + + /// Returns the impression-level OpenRTB configuration string. + public func getImpORTBConfig() -> String? { + impORTBConfig + } + // MARK: - adunit ext data aka inventory data (imp[].ext.data) diff --git a/PrebidMobile/ConfigurationAndTargeting/Targeting.swift b/PrebidMobile/ConfigurationAndTargeting/Targeting.swift index a45879c71..c0661e0d4 100644 --- a/PrebidMobile/ConfigurationAndTargeting/Targeting.swift +++ b/PrebidMobile/ConfigurationAndTargeting/Targeting.swift @@ -303,6 +303,20 @@ public class Targeting: NSObject { // MARK: - Public Methods + // MARK: Arbitrary ORTB Configuration + + /// Sets the global-level OpenRTB configuration string. + /// + /// - Parameter ortbObject: The global-level OpenRTB configuration string to set. Can be `nil` to clear the configuration. + public func setGlobalORTBConfig(_ ortbConfig: String?) { + globalORTBConfig = ortbConfig + } + + /// Returns the global-level OpenRTB configuration string. + public func getGlobalORTBConfig() -> String? { + globalORTBConfig + } + /// Adds a parameter to the parameter dictionary with a specified name. /// /// - Parameters: @@ -643,6 +657,8 @@ public class Targeting: NSObject { private var yearofbirth = 0 + private var globalORTBConfig: String? + // MARK: - Internal Methods func makeCustomParamFromName(_ name: String) -> String { diff --git a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/AdConfiguration.swift b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/AdConfiguration.swift index ef3b688e2..6d9adc88a 100644 --- a/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/AdConfiguration.swift +++ b/PrebidMobile/PrebidMobileRendering/AdTypes/AdView/AdConfiguration.swift @@ -129,8 +129,12 @@ public class AdConfiguration: AutoRefreshCountConfig { private var _autoRefreshDelay: TimeInterval? = PBMAutoRefresh.AUTO_REFRESH_DELAY_DEFAULT + public var impORTBConfig: String? + + @available(*, deprecated, message: "This property is deprecated in favour of `impORTBConfig`.") public var ortbConfig: String? + @available(*, deprecated, message: "This method is deprecated and will be removed in future versions.") public func getCheckedOrtbConfig() -> [String: Any]? { //return ortbConfig in dictionary form after checking if it's valid json if let jsonString = ortbConfig { diff --git a/PrebidMobile/PrebidMobileRendering/Networking/Parameters/ArbitraryORTBHelper.swift b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/ArbitraryORTBHelper.swift new file mode 100644 index 000000000..49e24b32b --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/ArbitraryORTBHelper.swift @@ -0,0 +1,120 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import Foundation + +protocol ArbitraryORTBHelperProtocol { + func getValidatedORTBDict() -> [String: Any]? +} + +class ArbitraryImpORTBHelper: ArbitraryORTBHelperProtocol { + + private let ortb: String + + init(ortb: String) { + self.ortb = ortb + } + + func getValidatedORTBDict() -> [String : Any]? { + guard let ortbDict = try? PBMFunctions.dictionaryFromJSONString(ortb) else { + Log.warn("The provided impression-level ortbConfig object is not valid JSON and will be ignored.") + return nil + } + + return ortbDict + } +} + +class ArbitraryGlobalORTBHelper: ArbitraryORTBHelperProtocol { + + private let ortb: String + + struct ProtectedFields { + + static var deviceProps: [String] { + [ + "w" ,"h" ,"lmt", "ifa","make", "model", "os", "osv", "hwv", "language", + "connectiontype", "mccmnc", "carrier", "ua", "pxratio", "geo" + ] + } + + static var deviceExtProps: [String] { + [ "atts", "ifv" ] + } + + static var regsProps: [String] { + [ "gpp_sid", "gpp", "coppa" ] + } + + static var regsExtProps: [String] { + [ "gdpr", "us_privacy" ] + } + + static var userProps: [String] { + [ "geo" ] + } + + static var userExtProps: [String] { + [ "consent" ] + } + } + + init(ortb: String) { + self.ortb = ortb + } + + func getValidatedORTBDict() -> [String : Any]? { + guard var ortbDict = try? PBMFunctions.dictionaryFromJSONString(ortb) else { + Log.warn("The provided global-level ortbConfig object is not valid JSON and will be ignored.") + return nil + } + + ortbDict["regs"] = removeProtectedFields( + from: ortbDict["regs"] as? [String: Any], + props: ProtectedFields.regsProps, + extProps: ProtectedFields.regsExtProps + ) + + ortbDict["device"] = removeProtectedFields( + from: ortbDict["device"] as? [String: Any], + props: ProtectedFields.deviceProps, + extProps: ProtectedFields.deviceExtProps + ) + + ortbDict["user"] = removeProtectedFields( + from: ortbDict["user"] as? [String: Any], + props: ProtectedFields.userProps, + extProps: ProtectedFields.userExtProps + ) + + return ortbDict + } + + private func removeProtectedFields( + from dict: [String: Any]?, + props: [String], + extProps: [String] + ) -> [String: Any]? { + guard var dict = dict else { return nil } + + if var ext = dict["ext"] as? [String: Any] { + extProps.forEach { ext[$0] = nil } + dict["ext"] = ext + } + + props.forEach { dict[$0] = nil } + return dict + } +} diff --git a/PrebidMobile/PrebidMobileRendering/Networking/Parameters/ArbitraryORTBService.swift b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/ArbitraryORTBService.swift new file mode 100644 index 000000000..f522cd195 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/ArbitraryORTBService.swift @@ -0,0 +1,53 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +@objc(PBMArbitraryORTBService) @objcMembers +public class ArbitraryORTBService: NSObject { + + private override init() { + super.init() + } + + public static func merge( + sdkORTB: [String: Any], + impORTB: String?, + globalORTB: String? + ) -> [String: Any] { + var resultORTB = sdkORTB + var resultImp = resultORTB["imp"] as? [[String: Any]] + + if let impORTB, + let impORTBDict = ArbitraryImpORTBHelper(ortb: impORTB).getValidatedORTBDict() { + // Imp objects from imp configuration should be merged to existing imp objects. + resultImp = resultImp?.map { $0.deepMerging(with: impORTBDict) } ?? [impORTBDict] + } + + if let globalORTB, + let globalORTBDict = ArbitraryGlobalORTBHelper(ortb: globalORTB).getValidatedORTBDict() { + resultORTB = resultORTB.deepMerging(with: globalORTBDict) + + // Imp objects from global configuration should be appended to existing array. + if let globalImps = globalORTBDict["imp"] as? [[String: Any]] { + resultImp = (resultImp ?? []) + globalImps + } + } + + resultORTB["imp"] = resultImp + + return resultORTB + } +} diff --git a/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMORTBParameterBuilder.h b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMORTBParameterBuilder.h index 2ac672dfb..af1bb8f5b 100644 --- a/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMORTBParameterBuilder.h +++ b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMORTBParameterBuilder.h @@ -19,6 +19,6 @@ @interface PBMORTBParameterBuilder : NSObject -+ (NSDictionary *)buildOpenRTBFor:(PBMORTBBidRequest *)bidRequest; ++ (NSDictionary *)buildOpenRTBFor:(PBMJsonDictionary *)ortbJsonDictionary; @end diff --git a/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMORTBParameterBuilder.m b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMORTBParameterBuilder.m index 6a71c1872..49b765ec7 100644 --- a/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMORTBParameterBuilder.m +++ b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMORTBParameterBuilder.m @@ -16,6 +16,8 @@ #import "PBMORTBParameterBuilder.h" #import "PBMConstants.h" #import "PBMORTBBidRequest.h" +#import "PBMFunctions.h" +#import "PBMFunctions+Private.h" #import "PrebidMobileSwiftHeaders.h" #if __has_include("PrebidMobile-Swift.h") @@ -26,18 +28,18 @@ @implementation PBMORTBParameterBuilder -+ (NSDictionary *)buildOpenRTBFor:(PBMORTBBidRequest *)bidRequest { ++ (NSDictionary *)buildOpenRTBFor:(PBMJsonDictionary *)ortbJsonDictionary { NSMutableDictionary *ret = [NSMutableDictionary new]; - if (!bidRequest) { + if (!ortbJsonDictionary) { PBMLogError(@"Invalid properties"); return ret; } NSError *error = nil; - NSString *json = [bidRequest toJsonStringWithError:&error]; - if (json) { - ret[PBMParameterKeysOPEN_RTB] = json; + NSString *jsonString = [PBMFunctions toStringJsonDictionary:ortbJsonDictionary error:&error]; + if (jsonString) { + ret[PBMParameterKeysOPEN_RTB] = jsonString; } else { PBMLogError(@"%@", [error localizedDescription]); } diff --git a/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMParameterBuilderService.h b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMParameterBuilderService.h index d9277956f..a3fc668c1 100644 --- a/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMParameterBuilderService.h +++ b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMParameterBuilderService.h @@ -31,6 +31,7 @@ @class PBMDeviceAccessManager; @class PBMLocationManager; @class Prebid; +@class PBMArbitraryORTBParameterBuilder; @interface PBMParameterBuilderService : NSObject diff --git a/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMParameterBuilderService.m b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMParameterBuilderService.m index be500459d..e3ea108d2 100644 --- a/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMParameterBuilderService.m +++ b/PrebidMobile/PrebidMobileRendering/Networking/Parameters/PBMParameterBuilderService.m @@ -81,9 +81,12 @@ @implementation PBMParameterBuilderService [[PBMGeoLocationParameterBuilder alloc] initWithLocationManager:pbmLocationManager], [[PBMAppInfoParameterBuilder alloc] initWithBundle:bundle targeting:targeting], [[PBMDeviceInfoParameterBuilder alloc] initWithDeviceAccessManager:pbmDeviceAccessManager], - [[PBMNetworkParameterBuilder alloc] initWithCtTelephonyNetworkInfo:ctTelephonyNetworkInfo reachability:reachability], + [[PBMNetworkParameterBuilder alloc] initWithCtTelephonyNetworkInfo:ctTelephonyNetworkInfo + reachability:reachability], [[PBMUserConsentParameterBuilder alloc] init], - [[PBMSKAdNetworksParameterBuilder alloc] initWithBundle:bundle targeting:targeting adConfiguration:adConfiguration], + [[PBMSKAdNetworksParameterBuilder alloc] initWithBundle:bundle + targeting:targeting + adConfiguration:adConfiguration], ]]; if (extraParameterBuilders) { @@ -94,14 +97,20 @@ @implementation PBMParameterBuilderService [builder buildBidRequest:bidRequest]; } - return [PBMORTBParameterBuilder buildOpenRTBFor:bidRequest]; + NSDictionary *ortb = [bidRequest toJsonDictionary]; + + NSDictionary * arbitratyORTB = [PBMArbitraryORTBService mergeWithSdkORTB:ortb + impORTB:adConfiguration.impORTBConfig + globalORTB:[targeting getGlobalORTBConfig]]; + + return [PBMORTBParameterBuilder buildOpenRTBFor:arbitratyORTB]; } + (nonnull PBMORTBBidRequest *)createORTBBidRequestWithTargeting:(nonnull Targeting *)targeting { PBMORTBBidRequest *bidRequest = [PBMORTBBidRequest new]; NSNumber * yob = [targeting getYearOfBirth]; - if (![yob isEqual: @0]) { + if (![yob isEqual:@0]) { bidRequest.user.yob = yob; } @@ -144,6 +153,7 @@ + (nonnull PBMORTBBidRequest *)createORTBBidRequestWithTargeting:(nonnull Target bidRequest.user.geo.lat = @(coord2d.latitude); bidRequest.user.geo.lon = @(coord2d.longitude); } + return bidRequest; } diff --git a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequest.h b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequest.h index ec2c03136..c195ef052 100644 --- a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequest.h +++ b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequest.h @@ -14,6 +14,7 @@  */ #import "PBMORTBAbstract.h" +#import "PBMMacros.h" @class PBMORTBApp; @class PBMORTBBidRequestExtPrebid; @@ -135,9 +136,8 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, strong) PBMORTBBidRequestExtPrebid *extPrebid; -@property(nonatomic, strong, nullable) NSDictionary *arbitraryJsonConfig; - -@property(nonatomic, strong, nullable) NSDictionary *ortbObject; +@property(nonatomic, strong, nullable) NSDictionary *arbitraryJsonConfig __deprecated__("This property is deprecated and will be removed in future versions"); +@property(nonatomic, strong, nullable) NSDictionary *ortbObject __deprecated__("This property is deprecated and will be removed in future versions"); - (instancetype)init; diff --git a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequest.m b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequest.m index cec9acb1a..a5adc07cb 100644 --- a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequest.m +++ b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequest.m @@ -105,7 +105,7 @@ - (nonnull PBMJsonDictionary *)toJsonDictionary { } - (nonnull PBMMutableJsonDictionary *)mergeDictionaries:(NSMutableDictionary*)dictionary1 joiningArgument2:(NSMutableDictionary*)dictionary2 - joiningArgument3:(Boolean)firstHasPriority{ + joiningArgument3:(Boolean)firstHasPriority __deprecated__("This method is deprecated and will be removed in future versions") { PBMMutableJsonDictionary *ret = dictionary1; for (id key in dictionary2) @@ -135,6 +135,7 @@ - (instancetype)initWithJsonDictionary:(nonnull PBMJsonDictionary *)jsonDictiona if (!(self = [self init])) { return nil; } + _requestID = jsonDictionary[@"id"]; NSMutableArray *impressions = [NSMutableArray new]; diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift index de71986a5..499acd271 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift @@ -76,6 +76,7 @@ public class BannerView: UIView, set { adUnitConfig.adPosition = newValue } } + @available(*, deprecated, message: "Deprecated. Use setImpORTBConfig(_:) and getImpORTBConfig() for impression-level ORTB configuration.") @objc public var ortbConfig: String? { get { adUnitConfig.ortbConfig } set { adUnitConfig.ortbConfig = newValue } @@ -235,6 +236,20 @@ public class BannerView: UIView, Prebid.shared.storedAuctionResponse = storedAuction } + // MARK: Arbitrary ORTB Configuration + + /// Sets the impression-level OpenRTB configuration string for the ad unit. + /// + /// - Parameter ortbObject: The impression-level OpenRTB configuration string to set. Can be `nil` to clear the configuration. + @objc public func setImpORTBConfig(_ ortbConfig: String?) { + adUnitConfig.impORTBConfig = ortbConfig + } + + /// Returns the impression-level OpenRTB configuration string. + @objc public func getImpORTBConfig() -> String? { + adUnitConfig.impORTBConfig + } + /// Stops the auto-refresh of the ad. @objc public func stopRefresh() { adLoadFlowController?.enqueueGatedBlock { [weak self] in diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift index f0420c478..2ee10b83d 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift @@ -40,6 +40,7 @@ public class InterstitialRenderingAdUnit: NSObject, BaseInterstitialAdUnitProtoc } /// The ORTB (OpenRTB) configuration string for the ad unit. + @available(*, deprecated, message: "Deprecated. Use setImpORTBConfig(_:) and getImpORTBConfig() for impression-level ORTB configuration.") public var ortbConfig: String? { get { adUnitConfig.ortbConfig } set { adUnitConfig.ortbConfig = newValue } @@ -193,6 +194,20 @@ public class InterstitialRenderingAdUnit: NSObject, BaseInterstitialAdUnitProtoc baseAdUnit.show(from: controller) } + // MARK: Arbitrary ORTB Configuration + + /// Sets the impression-level OpenRTB configuration string for the ad unit. + /// + /// - Parameter ortbObject: The impression-level OpenRTB configuration string to set. Can be `nil` to clear the configuration. + public func setImpORTBConfig(_ ortbConfig: String?) { + adUnitConfig.impORTBConfig = ortbConfig + } + + /// Returns the impression-level OpenRTB configuration string. + public func getImpORTBConfig() -> String? { + adUnitConfig.impORTBConfig + } + // MARK: - Ext Data (imp[].ext.data) /// Adds context data for a specified key. diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift index 8cfb0b53d..6f7cc158b 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift @@ -40,6 +40,7 @@ public class RewardedAdUnit: NSObject, BaseInterstitialAdUnitProtocol { } /// The ORTB (OpenRTB) configuration string for the ad unit. + @available(*, deprecated, message: "Deprecated. Use setImpORTBConfig(_:) and getImpORTBConfig() for impression-level ORTB configuration.") public var ortbConfig: String? { get { adUnitConfig.ortbConfig } set { adUnitConfig.ortbConfig = newValue } @@ -177,6 +178,20 @@ public class RewardedAdUnit: NSObject, BaseInterstitialAdUnitProtocol { baseAdUnit.show(from: controller) } + // MARK: Arbitrary ORTB Configuration + + /// Sets the impression-level OpenRTB configuration string for the ad unit. + /// + /// - Parameter ortbObject: The impression-level OpenRTB configuration string to set. Can be `nil` to clear the configuration. + public func setImpORTBConfig(_ ortbConfig: String?) { + adUnitConfig.impORTBConfig = ortbConfig + } + + /// Returns the impression-level OpenRTB configuration string. + public func getImpORTBConfig() -> String? { + adUnitConfig.impORTBConfig + } + // MARK: - Ext Data (imp[].ext.data) /// Adds context data for a specified key. diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBannerAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBannerAdUnit.swift index 8e6882482..ec71e5751 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBannerAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBannerAdUnit.swift @@ -80,11 +80,26 @@ public class MediationBannerAdUnit : NSObject { set { adUnitConfig.additionalSizes = newValue } } - /// OpenRTB configuration + /// The ORTB (OpenRTB) configuration string for the ad unit. + @available(*, deprecated, message: "Deprecated. Use setImpORTBConfig(_:) and getImpORTBConfig() for impression-level ORTB configuration.") public var ortbConfig: String? { get { adUnitConfig.ortbConfig } set { adUnitConfig.ortbConfig = newValue } } + + // MARK: Arbitrary ORTB Configuration + + /// Sets the impression-level OpenRTB configuration string for the ad unit. + /// + /// - Parameter ortbObject: The impression-level OpenRTB configuration string to set. Can be `nil` to clear the configuration. + public func setImpORTBConfig(_ ortbConfig: String?) { + adUnitConfig.impORTBConfig = ortbConfig + } + + /// Returns the impression-level OpenRTB configuration string. + public func getImpORTBConfig() -> String? { + adUnitConfig.impORTBConfig + } // MARK: - Ext Data (imp[].ext.data) diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift index a2251e46d..95e717749 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationBaseInterstitialAdUnit.swift @@ -58,19 +58,20 @@ public class MediationBaseInterstitialAdUnit : NSObject { set { adUnitConfig.adConfiguration.videoControlsConfig.closeButtonPosition = newValue } } - /// OpenRTB configuration for the ad unit. + /// The ORTB (OpenRTB) configuration string for the ad unit. + @available(*, deprecated, message: "Deprecated. Use setImpORTBConfig(_:) and getImpORTBConfig() for impression-level ORTB configuration.") public var ortbConfig: String? { get { adUnitConfig.ortbConfig } set { adUnitConfig.ortbConfig = newValue } } - - let adUnitConfig: AdUnitConfig /// The configuration ID for the ad unit. public var configId: String { adUnitConfig.configId } + let adUnitConfig: AdUnitConfig + var bidRequester: PBMBidRequester? var completion: ((ResultCode) -> Void)? @@ -99,6 +100,20 @@ public class MediationBaseInterstitialAdUnit : NSObject { completion: completion) } + // MARK: Arbitrary ORTB Configuration + + /// Sets the impression-level OpenRTB configuration string for the ad unit. + /// + /// - Parameter ortbObject: The impression-level OpenRTB configuration string to set. Can be `nil` to clear the configuration. + public func setImpORTBConfig(_ ortbConfig: String?) { + adUnitConfig.impORTBConfig = ortbConfig + } + + /// Returns the impression-level OpenRTB configuration string. + public func getImpORTBConfig() -> String? { + adUnitConfig.impORTBConfig + } + // MARK: - Ext Data (imp[].ext.data) /// This method obtains the context data keyword & value for adunit context targeting diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationNativeAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationNativeAdUnit.swift index 9fc8564a0..6fe5eb31d 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationNativeAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/MediationAPI/MediationNativeAdUnit.swift @@ -107,17 +107,29 @@ public class MediationNativeAdUnit : NSObject { public func setExt(_ ext: [String: Any]) { nativeAdUnit.ext = ext } - - /// Retrieves the OpenRTB configuration for the native ad unit. - /// - Returns: The OpenRTB configuration as a string, or `nil` if not set. + + // MARK: Arbitrary ORTB Configuration + + @available(*, deprecated, message: "Deprecated. Use setImpORTBConfig(_:) for impression-level ORTB configuration.") + public func setOrtbConfig(_ ortbConfig: String?) { + nativeAdUnit.setOrtbConfig(ortbConfig) + } + + @available(*, deprecated, message: "Deprecated. Use getImpORTBConfig() for impression-level ORTB configuration.") public func getOrtbConfig() -> String? { return nativeAdUnit.getOrtbConfig() } - /// Sets the OpenRTB configuration for the native ad unit. - /// - Parameter ortbConfig: The OpenRTB configuration to set. - public func setOrtbConfig(_ ortbConfig: String?) { - nativeAdUnit.setOrtbConfig(ortbConfig) + /// Sets the impression-level OpenRTB configuration string for the ad unit. + /// + /// - Parameter ortbObject: The impression-level OpenRTB configuration string to set. Can be `nil` to clear the configuration. + public func setImpORTBConfig(_ ortbConfig: String?) { + nativeAdUnit.adUnitConfig.impORTBConfig = ortbConfig + } + + /// Returns the impression-level OpenRTB configuration string. + public func getImpORTBConfig() -> String? { + nativeAdUnit.adUnitConfig.impORTBConfig } // MARK: - App Content (app.content.data) diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift index 7d6ef7d7f..155916ca4 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift @@ -86,6 +86,11 @@ public class AdUnitConfig: NSObject, NSCopying { get {adConfiguration.ortbConfig} set {adConfiguration.ortbConfig = newValue} } + + public var impORTBConfig: String? { + get { adConfiguration.impORTBConfig } + set { adConfiguration.impORTBConfig = newValue } + } // MARK: - Public Methods @@ -311,7 +316,6 @@ public class AdUnitConfig: NSObject, NSCopying { clone.additionalSizes = self.additionalSizes clone.refreshInterval = self.refreshInterval clone.gpid = self.gpid - clone.ortbConfig = self.ortbConfig clone.extensionData = self.extensionData.merging(clone.extensionData) { $1 } clone.appContent = self.appContent clone.extKeywords = self.extKeywords @@ -319,6 +323,7 @@ public class AdUnitConfig: NSObject, NSCopying { clone.adPosition = self.adPosition clone.pbAdSlot = self.pbAdSlot + clone.adConfiguration.impORTBConfig = self.adConfiguration.impORTBConfig clone.adConfiguration.rewardedConfig = self.adConfiguration.rewardedConfig clone.adConfiguration.winningBidAdFormat = self.adConfiguration.winningBidAdFormat clone.adConfiguration.adFormats = self.adConfiguration.adFormats diff --git a/PrebidMobile/Utils/CollectionExtension.swift b/PrebidMobile/Utils/CollectionExtension.swift index 310dd6f7d..799b1c8a5 100644 --- a/PrebidMobile/Utils/CollectionExtension.swift +++ b/PrebidMobile/Utils/CollectionExtension.swift @@ -123,7 +123,6 @@ extension Dictionary where Key == AnyHashable, Value == Any { return resultString } - } extension Dictionary where Key == String, Value == String { @@ -132,6 +131,72 @@ extension Dictionary where Key == String, Value == String { } } +extension Array where Element: Hashable { + + func removingDuplicates() -> [Element] { + var addedDict = [Element: Bool]() + + return filter { + addedDict.updateValue(true, forKey: $0) == nil + } + } +} + +extension Dictionary where Key == String { + + /// Merges the current dictionary with another dictionary recursively, + /// with options to control replacement behavior. + /// + /// - Parameters: + /// - otherDict: The dictionary to merge with the current dictionary. + /// - shouldReplace: A Boolean indicating whether to replace existing values in the + /// current dictionary with values from `otherDict`. Defaults to `false`. + /// + /// - Returns: A new dictionary that is the result of the merge operation. + /// + /// - Notes: + /// - Arrays with matching keys are concatenated and deduplicated (if possible). + /// - Nested dictionaries are merged recursively. + func deepMerging( + with otherDict: [String: Any], + shouldReplace: Bool = false + ) -> [String: Any] { + var result: [String: Any] = self + + for (key, value) in otherDict { + if let existingValue = result[key] { + if shouldReplace { + result[key] = value + } else { + if let existingArray = existingValue as? [Any], let newArray = value as? [Any] { + let mergedArray = existingArray + newArray + if let hashableMergedArray = mergedArray as? [AnyHashable] { + result[key] = hashableMergedArray.removingDuplicates() + } else { + result[key] = mergedArray + } + } + else if let existingDict = existingValue as? [String: Any], + let newDict = value as? [String: Any] { + result[key] = existingDict.deepMerging( + with: newDict, + shouldReplace: shouldReplace + ) + } + else { + result[key] = value + } + } + } + else { + result[key] = value + } + } + + return result + } +} + extension Array where Element: SingleContainerInt { func toIntArray() -> [Int] { diff --git a/PrebidMobileTests/AdUnitTests/AdUnitTests.swift b/PrebidMobileTests/AdUnitTests/AdUnitTests.swift index bc6c62315..600c11326 100644 --- a/PrebidMobileTests/AdUnitTests/AdUnitTests.swift +++ b/PrebidMobileTests/AdUnitTests/AdUnitTests.swift @@ -173,7 +173,7 @@ class AdUnitTests: XCTestCase { exception.fulfill() } - waitForExpectations(timeout: 2, handler: nil) + waitForExpectations(timeout: 10, handler: nil) AdUnitSwizzleHelper.toggleCheckRefreshTime() PBHTTPStubbingManager.shared().disable() diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift b/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift index 6b067e50f..865f808d5 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift @@ -190,26 +190,6 @@ class PBMORTBAbstractTest : XCTestCase { codeAndDecode(abstract: pbmORTBBidRequest, expectedString: "{\"ext\":{\"prebid\":{\"data\":{\"bidders\":[\"openx\",\"prebid\",\"thanatos\"]},\"storedauctionresponse\":{\"id\":\"stored-auction-response-test\"},\"storedrequest\":{\"id\":\"b4eb1475-4e3d-4186-97b7-25b6a6cf8618\"},\"targeting\":{}}},\"imp\":[{\"clickbrowser\":0,\"ext\":{\"dlp\":1},\"instl\":0,\"secure\":0}]}") } - func testBidRequestWithOrtbObjectToJsonString() { - let pbmORTBBidRequest = PBMORTBBidRequest() - let uuid = UUID().uuidString - pbmORTBBidRequest.requestID = uuid - pbmORTBBidRequest.tmax = 2000 - pbmORTBBidRequest.ortbObject = ["arbitraryparamkey1": "arbitraryparamvalue1", "tmax": 3000, "id": "1231234"] - - codeAndDecode(abstract: pbmORTBBidRequest, expectedString: "{\"arbitraryparamkey1\":\"arbitraryparamvalue1\",\"id\":\"1231234\",\"imp\":[{\"clickbrowser\":0,\"ext\":{\"dlp\":1},\"instl\":0,\"secure\":0}],\"tmax\":3000}") - } - - func testBidRequestWithOrtbObjectOverridingReservedToJsonString() { - let pbmORTBBidRequest = PBMORTBBidRequest() - let uuid = UUID().uuidString - pbmORTBBidRequest.requestID = uuid - pbmORTBBidRequest.tmax = 2000 - pbmORTBBidRequest.ortbObject = ["arbitraryparamkey1": "arbitraryparamvalue1", "tmax": 3000, "id": "1231234", "device": "myTestDevice", "geo": "mylatlong", "regs": ["reg1":"reg2"]] - - codeAndDecode(abstract: pbmORTBBidRequest, expectedString: "{\"arbitraryparamkey1\":\"arbitraryparamvalue1\",\"id\":\"1231234\",\"imp\":[{\"clickbrowser\":0,\"ext\":{\"dlp\":1},\"instl\":0,\"secure\":0}],\"tmax\":3000}") - } - func testSourceToJsonString() { let pbmORTBSource = PBMORTBSource() @@ -570,6 +550,28 @@ class PBMORTBAbstractTest : XCTestCase { codeAndDecode(abstract:user, expectedString:"{\"ext\":{\"eids\":[{\"key\":{\"key\":\"value\"}},{\"key2\":{\"key2\":\"value2\"}}]},\"gender\":\"M\",\"yob\":1981}") } + // MARK: Arbitrary ORTB (Deprecated API) + + func testBidRequestWithOrtbObjectToJsonString() { + let pbmORTBBidRequest = PBMORTBBidRequest() + let uuid = UUID().uuidString + pbmORTBBidRequest.requestID = uuid + pbmORTBBidRequest.tmax = 2000 + pbmORTBBidRequest.ortbObject = ["arbitraryparamkey1": "arbitraryparamvalue1", "tmax": 3000, "id": "1231234"] + + codeAndDecode(abstract: pbmORTBBidRequest, expectedString: "{\"arbitraryparamkey1\":\"arbitraryparamvalue1\",\"id\":\"1231234\",\"imp\":[{\"clickbrowser\":0,\"ext\":{\"dlp\":1},\"instl\":0,\"secure\":0}],\"tmax\":3000}") + } + + func testBidRequestWithOrtbObjectOverridingReservedToJsonString() { + let pbmORTBBidRequest = PBMORTBBidRequest() + let uuid = UUID().uuidString + pbmORTBBidRequest.requestID = uuid + pbmORTBBidRequest.tmax = 2000 + pbmORTBBidRequest.ortbObject = ["arbitraryparamkey1": "arbitraryparamvalue1", "tmax": 3000, "id": "1231234", "device": "myTestDevice", "geo": "mylatlong", "regs": ["reg1":"reg2"]] + + codeAndDecode(abstract: pbmORTBBidRequest, expectedString: "{\"arbitraryparamkey1\":\"arbitraryparamvalue1\",\"id\":\"1231234\",\"imp\":[{\"clickbrowser\":0,\"ext\":{\"dlp\":1},\"instl\":0,\"secure\":0}],\"tmax\":3000}") + } + //MARK: - Utility func initORTBDevice(ifa: String?) -> PBMORTBDevice { diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMORTBParameterBuilderTest.swift b/PrebidMobileTests/RenderingTests/Tests/PBMORTBParameterBuilderTest.swift index 8f6c0d466..652234a60 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMORTBParameterBuilderTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMORTBParameterBuilderTest.swift @@ -17,16 +17,7 @@ import XCTest // MARK: Test Properties -fileprivate let errorMessage = "MockedRequest.toJsonString error" - -// MARK: - Mock - -class MockedRequest : PBMORTBBidRequest { - - override func toJsonString() throws -> String { - throw PBMError.error(message: errorMessage, type: .internalError) - } -} +fileprivate let errorMessage = "Not valid JSON object" // MARK: - Test Case @@ -40,7 +31,7 @@ class PBMORTBParameterBuilderTest: XCTestCase { } func testAppendBuilderParameters() { - let res = PBMORTBParameterBuilder.buildOpenRTB(for: PBMORTBBidRequest())! + let res = PBMORTBParameterBuilder.buildOpenRTB(for: [:])! XCTAssertEqual(res.keys.count, 1) XCTAssertNotNil(res["openrtb"]) @@ -49,7 +40,8 @@ class PBMORTBParameterBuilderTest: XCTestCase { func testAppendBuilderParametersWitError() { logToFile = .init() - PBMORTBParameterBuilder.buildOpenRTB(for: MockedRequest()) + class Dummy {} + PBMORTBParameterBuilder.buildOpenRTB(for: ["key": Dummy()]) let log = Log.getLogFileAsString() ?? "" XCTAssert(log.contains(errorMessage)) diff --git a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/ArbitraryORTBHelperTests.swift b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/ArbitraryORTBHelperTests.swift new file mode 100644 index 000000000..15d929d6b --- /dev/null +++ b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/ArbitraryORTBHelperTests.swift @@ -0,0 +1,140 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import XCTest +@testable import PrebidMobile + +class ArbitraryORTBHelperTests: XCTestCase { + + func testImpORTBHelper_ValidJSON() { + let validJSON = """ + { + "key1": "value1", + "key2": 123 + } + """ + + let result = ArbitraryImpORTBHelper(ortb: validJSON).getValidatedORTBDict() + + XCTAssertNotNil(result) + XCTAssertEqual(result?["key1"] as? String, "value1") + XCTAssertEqual(result?["key2"] as? Int, 123) + } + + func testImpORTBHelper_InvalidJSON() { + let invalidJSON = """ + { + "key1": "value1", + "key2": + """ + + let result = ArbitraryImpORTBHelper(ortb: invalidJSON).getValidatedORTBDict() + + XCTAssertNil(result) + } + + func testGlobalORTBHelper_ValidJSON_RemoveProtectedFields() { + let json = """ + { + "regs": { + "ext": { + "gdpr": 1, + "us_privacy": "1NY" + }, + "gpp_sid": [1, 2], + "gpp": "some_value" + }, + "device": { + "ext": { + "atts": 2, + "ifv": "some_id" + }, + "geo": { + "lat": 37, + "lon": -122 + }, + "w": 320, + "h": 480, + "lmt": 1, + "make": "Apple", + "model": "iPhone", + "os": "iOS", + "osv": "17.5", + "pxratio": 3, + "ua": "placeholder-user-agent", + "hwv": "hardware-version-placeholder", + "ifa": "placeholder-ifa", + "language": "placeholder-language", + "connectiontype": 2 + }, + "user": { + "ext": { + "consent": "some_consent" + }, + "geo": { + "lat": 12.34, + "lon": 56.78 + } + } + } + """ + + let result = ArbitraryGlobalORTBHelper(ortb: json).getValidatedORTBDict() + + XCTAssertNotNil(result) + + let regs = result?["regs"] as! [String: Any] + for prop in ArbitraryGlobalORTBHelper.ProtectedFields.regsProps { + XCTAssertNil(regs[prop]) + } + + let regsExt = regs["ext"] as! [String: Any] + for prop in ArbitraryGlobalORTBHelper.ProtectedFields.regsExtProps { + XCTAssertNil(regsExt[prop]) + } + + let device = result?["device"] as! [String: Any] + for prop in ArbitraryGlobalORTBHelper.ProtectedFields.deviceProps { + XCTAssertNil(device[prop]) + } + + let deviceExt = device["ext"] as! [String: Any] + for prop in ArbitraryGlobalORTBHelper.ProtectedFields.deviceExtProps { + XCTAssertNil(deviceExt[prop]) + } + + let user = result?["user"] as! [String: Any] + for prop in ArbitraryGlobalORTBHelper.ProtectedFields.userProps { + XCTAssertNil(user[prop]) + } + + let userExt = user["ext"] as! [String: Any] + for prop in ArbitraryGlobalORTBHelper.ProtectedFields.userExtProps { + XCTAssertNil(userExt[prop]) + } + } + + func testGlobalORTBHelper_InvalidJSON() { + let invalidJSON = """ + { + "key1": "value1", + "key2": + """ + + let result = ArbitraryGlobalORTBHelper(ortb: invalidJSON).getValidatedORTBDict() + + XCTAssertNil(result) + } +} diff --git a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/ArbitraryORTBServiceTests.swift b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/ArbitraryORTBServiceTests.swift new file mode 100644 index 000000000..9db8c5575 --- /dev/null +++ b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/ArbitraryORTBServiceTests.swift @@ -0,0 +1,691 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import XCTest +@testable import PrebidMobile + +class ArbitraryORTBServiceTests: XCTestCase { + + func testMerge_BothGlobalAndImpORTBAreEmpty() { + let sdkORTB: [String: Any] = [ + "user": ["id": "user123"] + ] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: nil + ) + + let expectedORTB = """ + { + "user": { + "id": "user123" + } + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithImp_emptyGlobal() { + let impORTB = "{\"banner\": {\"format\": [{\"w\": 300, \"h\": 250}]}}" + let sdkORTB = "{\"imp\": [{\"id\": \"1\", \"banner\": {\"format\": [{\"w\": 320, \"h\": 50}]}}]}" + let expectedORTB = "{\"imp\": [{\"id\": \"1\", \"banner\": {\"format\": [{\"w\": 320, \"h\": 50},{\"w\": 300, \"h\": 250}]}}]}" + + let result = ArbitraryORTBService.merge( + sdkORTB: try! PBMFunctions.dictionaryFromJSONString(sdkORTB), + impORTB: impORTB, + globalORTB: nil + ) + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithImp_emptyExistingORTB() { + let impORTB = "{\"banner\": {\"format\": [{\"w\": 300, \"h\": 250}]}}" + let sdkORTB = "{}" + let expectedORTB = "{\"imp\": [{\"banner\": {\"format\": [{\"w\": 300, \"h\": 250}]}}]}" + + let result = ArbitraryORTBService.merge( + sdkORTB: try! PBMFunctions.dictionaryFromJSONString(sdkORTB), + impORTB: impORTB, + globalORTB: nil + ) + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGlobal_WithImpInGlobal() { + let globalORTB = """ + { + "imp": [ + { + "id": "globalImp1", + "banner": { "w": 728, "h": 90 } + } + ] + } + """ + + let sdkORTB: [String: Any] = [ + "imp": [ + ["id": "existingImp", "native": ["request": "placeholder"]] + ] + ] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + XCTAssertNotNil(result) + + let expectedORTB = """ + { + "imp": [ + { + "id": "existingImp", + "native": { "request" : "placeholder" } + }, + { + "id": "globalImp1", + "banner": { "w": 728, "h": 90 } + } + ] + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithImp_providedGlobalAndImpressionLevelImps() { + let impORTB = "{\"banner\": {\"format\": [{\"w\": 300, \"h\": 250}]}}" + + let globalORTB = """ + { + "imp": [ + {"banner": {"format": [{"w": 728, "h": 90}]},"id": "imp1"}, + {"id": "imp2", "video": {"mimes": ["video/mp4"]}} + ] + } + """ + + let sdkORTB = """ + { + "imp": [ + {"banner": {"format": [{"w": 320, "h": 50}]}, "id": "existing_imp"} + ], + "device": {"ua": "Mozilla/5.0"} + } + """ + + // Impression-level config should update existing imp property. + // Global-level config should add more imps to imp property. + let expectedORTB = """ + { + "device": {"ua": "Mozilla/5.0"}, + "imp": [ + {"id": "existing_imp", "banner": {"format": [{"w": 320, "h": 50},{"w": 300, "h": 250}]}}, + {"id": "imp1", "banner": {"format": [{"w": 728, "h": 90}]}}, + {"id": "imp2", "video": {"mimes": ["video/mp4"]}}, + ] + } + """ + + let result = ArbitraryORTBService.merge( + sdkORTB: try! PBMFunctions.dictionaryFromJSONString(sdkORTB), + impORTB: impORTB, + globalORTB: globalORTB + ) + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithImp_BothGlobalAndImpConfigProvided_WithEmptyExistingORTB() { + let impORTB = """ + { + "id": "imp1", + "native": { "request": "placeholder" } + } + """ + + let globalORTB = """ + { + "imp": [{"id": "global_imp", "banner": {"format": [{"w": 320, "h": 50}]}}] + } + """ + + let sdkORTB: [String: Any] = [:] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: impORTB, + globalORTB: globalORTB + ) + + XCTAssertNotNil(result) + + let expectedORTB = """ + { + "imp": [ + { + "id": "imp1", + "native": { "request": "placeholder" } + }, + { + "id": "global_imp", + "banner": {"format": [{"w": 320, "h": 50}]}, + } + ] + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGlobal_AppObject_NoConflicts() { + let globalORTB = """ + { + "app": { + "storeurl": "https://example.com/app" + } + } + """ + let sdkORTB: [String: Any] = [ + "app": [ + "name": "TestApp", + "bundle": "com.example.testapp" + ] + ] + + let expectedORTB = """ + { + "app": { + "name": "TestApp", + "bundle": "com.example.testapp", + "storeurl": "https://example.com/app" + } + } + """ + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGlobal_AppObject_WithConflicts() { + let globalORTB = """ + { + "app": { + "version": "2.0" + } + } + """ + let sdkORTB: [String: Any] = [ + "app": [ + "name": "TestApp", + "version": "1.0" + ] + ] + + let expectedORTB = """ + { + "app": { + "name": "TestApp", + "version": "2.0" + } + } + """ + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGlobal_UserObject_NoConflicts() { + let globalORTB = """ + { + "user": { + "id": "12345", + "buyeruid": "buyer123" + } + } + """ + let sdkORTB: [String: Any] = [ + "user": [ + "geo": [ + "lat": 37, + "lon": -122 + ] + ] + ] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + let expectedORTB = """ + { + "user": { + "id": "12345", + "buyeruid": "buyer123", + "geo": { + "lat": 37, + "lon": -122 + } + } + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGlobal_UserObject_WithConflicts() { + let globalORTB = """ + { + "user": { + "id": "override123", + "geo": { + "country": "CA" + } + } + } + """ + let sdkORTB: [String: Any] = [ + "user": [ + "id": "12345", + "geo": [ + "country": "US" + ] + ] + ] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + let expectedORTB = """ + { + "user": { + "id": "override123", + "geo": { + "country": "US" + } + } + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGlobal_UserObject_ProtectedFieldsNotCopied() { + let globalORTB = """ + { + "user": { + "id": "12345", + "ext": { + "consent": "someConsentString", + "otherField": "allowedValue" + }, + "geo": { + "country": "US" + } + } + } + """ + let sdkORTB: [String: Any] = [ + "user": [ + "geo": [ + "lat": 37, + "lon": -122 + ] + ] + ] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + let expectedORTB = """ + { + "user": { + "id": "12345", + "geo": { + "lat": 37, + "lon": -122 + }, + "ext": { + "otherField": "allowedValue" + } + } + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGlobal_SourceObject_NoConflicts() { + let globalORTB = """ + { + "source": { + "pchain": "pchainValue" + } + } + """ + let sdkORTB: [String: Any] = [ + "source": [ + "ext": [ + "tid": "transaction123" + ] + ] + ] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + let expectedORTB = """ + { + "source": { + "ext": { + "tid": "transaction123" + }, + "pchain": "pchainValue" + } + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGlobal_SourceObject_WithConflicts() { + let globalORTB = """ + { + "source": { + "tid": "globalTransaction", + "ext": { + "sourceType": "reseller" + } + } + } + """ + let sdkORTB: [String: Any] = [ + "source": [ + "tid": "existingTransaction", + "ext": [ + "sourceType": "direct" + ] + ] + ] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + let expectedORTB = """ + { + "source": { + "tid": "globalTransaction", + "ext": { + "sourceType": "reseller" + } + } + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGLobal_ExtObject_NoConflicts() { + let globalORTB = """ + { + "ext": { + "test": { + "new": "value" + }, + "prebid": { + "storedrequest": { + "id": "stored-request-id-placeholder" + } + } + } + } + """ + + let sdkORTB: [String: Any] = [ + "ext": [ + "prebid": [ + "existingConfig": "existingValue" + ] + ] + ] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + let expectedORTB = """ + { + "ext": { + "prebid": { + "existingConfig": "existingValue", + "storedrequest": { + "id": "stored-request-id-placeholder" + } + }, + "test": { + "new": "value" + } + } + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGlobal_ExtObject_WithConflicts() { + let globalORTB = """ + { + "ext": { + "prebid": { + "sharedField": "globalSharedValue", + "globalConfig": "globalValue" + } + } + } + """ + let sdkORTB: [String: Any] = [ + "ext": [ + "prebid": [ + "sharedField": "existingSharedValue", + "existingConfig": "existingValue" + ] + ] + ] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + let expectedORTB = """ + { + "ext": { + "prebid": { + "sharedField": "globalSharedValue", + "existingConfig": "existingValue", + "globalConfig": "globalValue" + } + } + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGlobal_DeviceObject_NoConflicts() { + let globalORTB = """ + { + "device": { + "test": 1 + } + } + """ + + let sdkORTB: [String: Any] = [ + "device": [ + "language": "en", + "connectiontype": 2 + ] + ] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + let expectedORTB = """ + { + "device": { + "language": "en", + "connectiontype": 2, + "test": 1 + } + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMergeORTBWithGlobal_DeviceObject_ProtectedFieldsNotCopied() { + let globalORTB = """ + { + "device": { + "ifa": "protectedValue", + "os": "Android", + "pxratio": 3.0 + } + } + """ + let sdkORTB: [String: Any] = [ + "device": [ + "os": "iOS", + "model": "iPhone" + ] + ] + + let result = ArbitraryORTBService.merge( + sdkORTB: sdkORTB, + impORTB: nil, + globalORTB: globalORTB + ) + + let expectedORTB = """ + { + "device": { + "os": "iOS", + "model": "iPhone" + } + } + """ + + XCTAssertEqual( + result as NSDictionary, + try? PBMFunctions.dictionaryFromJSONString(expectedORTB) as NSDictionary + ) + } + + func testMerge_WithEmptyORTBs() { + let result = ArbitraryORTBService.merge( + sdkORTB: [:], + impORTB: nil, + globalORTB: nil + ) + + XCTAssertTrue(result.isEmpty) + } +} diff --git a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift index 65464da38..e3bfc4620 100644 --- a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift @@ -801,6 +801,8 @@ class PrebidParameterBuilderTest: XCTestCase { } } + // MARK: Arbitrary ORTB (Deprecated API) + func testArbitraryORTBParams() { let gpid = "/12345/home_screen#identifier" let ortb = "{\"arbitraryparamkey1\":\"arbitraryparamvalue1\",\"imp\":[{}]}" diff --git a/PrebidMobileTests/UnitTests/CollectionExtensionTest.swift b/PrebidMobileTests/UnitTests/CollectionExtensionTest.swift index 566936759..07e50a86c 100644 --- a/PrebidMobileTests/UnitTests/CollectionExtensionTest.swift +++ b/PrebidMobileTests/UnitTests/CollectionExtensionTest.swift @@ -18,16 +18,7 @@ import XCTest class CollectionExtensionTest: XCTestCase { - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - func testGetObjectWithoutEmptyValues() { - //Test 1 let node1111: NSMutableDictionary = [:] let node111: NSMutableDictionary = [:] @@ -115,7 +106,6 @@ class CollectionExtensionTest: XCTestCase { node31.add(node313) let result10 = (node1 as! Dictionary).getObjectWithoutEmptyValues() XCTAssertEqual("{\"key3\":[{\"key312\":\"value312\"},[{\"key3131\":\"value3131\"}]]}", collectionToString(result10!)) - } func testSingleContainerIntToIntArray() { @@ -130,11 +120,199 @@ class CollectionExtensionTest: XCTestCase { XCTAssertTrue(intArray.contains(1) && intArray.contains(2) && intArray.contains(5)) } + func testDeepMergeValues() { + let dict1: [String: Any] = [ + "key1": "value1", + "key2": "value2", + "key4": [1,2,3], + "key5": 0.1, + "key6": "value6", + "key7": ["nested1": "nested_value1", "nested2": "nested_value2"], + "key8": ["nested1": "nested_value1", "nested2": ["deep_nested1": "deep_nested_value1", "deep_nested2": "deep_nested_value2"]] + ] + + let dict2: [String: Any] = [ + "key2": "newValue2", + "key3": "value3", + "key4": [2,3,4,5], + "key5": 1, + "key6": 1.0, + "key7": ["nested2": "another_nested_value2", "nested3": "nested_value3"], + "key8": ["nested1": "another_nested_value1", "nested2": ["deep_nested1": "new_nested1", "another_deep_nested2": "another_deep_nested_value2"]] + ] + + let result = dict1.deepMerging(with: dict2) + + XCTAssertEqual(result["key1"] as? String, "value1") + XCTAssertEqual(result["key2"] as? String, "newValue2") + XCTAssertEqual(result["key3"] as? String, "value3") + XCTAssertEqual(result["key4"] as? [Int], [1,2,3,4,5]) + XCTAssertEqual(result["key5"] as? Int, 1) + XCTAssertEqual(result["key6"] as? Double, 1.0) + XCTAssertEqual(result["key7"] as? [String: String], ["nested1": "nested_value1", "nested2": "another_nested_value2", "nested3": "nested_value3"]) + XCTAssert(NSDictionary(dictionary: result["key8"] as! [String: Any]).isEqual(to: ["nested1": "another_nested_value1", "nested2":["deep_nested1": "new_nested1", "deep_nested2": "deep_nested_value2", "another_deep_nested2": "another_deep_nested_value2"]] as [String : Any])) + } + + func testDeepMergeReplaceValues() { + let dict1: [String: Any] = [ + "key1": "value1", + "key2": "value2", + "key4": [1,2,3], + "key5": 0.1, + "key6": "value6", + "key7": ["nested1": "nested_value1", "nested2": "nested_value2"], + "key8": ["nested1": "nested_value1", "nested2": ["deep_nested1": "deep_nested_value1", "deep_nested2": "deep_nested_value2"]] + ] + + let dict2: [String: Any] = [ + "key2": "newValue2", + "key3": "value3", + "key4": [3,4,5], + "key5": 1, + "key6": 1.0, + "key7": ["nested2": "another_nested_value2", "nested3": "nested_value3"], + "key8": ["nested1": "another_nested_value1", "nested2": ["deep_nested1": "new_nested1", "another_deep_nested2": "another_deep_nested_value2"]] + ] + + let result = dict1.deepMerging(with: dict2, shouldReplace: true) + + XCTAssertEqual(result["key1"] as? String, "value1") + XCTAssertEqual(result["key2"] as? String, "newValue2") + XCTAssertEqual(result["key3"] as? String, "value3") + XCTAssertEqual(result["key4"] as? [Int], [3,4,5]) + XCTAssertEqual(result["key5"] as? Int, 1) + XCTAssertEqual(result["key6"] as? Double, 1.0) + XCTAssertEqual(result["key7"] as? [String: String], ["nested2": "another_nested_value2", "nested3": "nested_value3"]) + XCTAssert(NSDictionary(dictionary: result["key8"] as! [String: Any]).isEqual(to: ["nested1": "another_nested_value1", "nested2": ["deep_nested1": "new_nested1", "another_deep_nested2": "another_deep_nested_value2"]] as [String : Any])) + } + + func testDeepMergeEmptyDictionary() { + let dict1: [String: Any] = [:] + let dict2: [String: Any] = [:] + + let merged = dict1.deepMerging(with: dict2) + XCTAssertTrue(merged.isEmpty) + } + + func testDeepMergeOneEmptyDictionary() { + let dict1: [String: Any] = ["key1": "value1"] + let dict2: [String: Any] = [:] + + let merged = dict1.deepMerging(with: dict2) + XCTAssert(NSDictionary(dictionary: merged).isEqual(to: dict1)) + + let merged2 = dict2.deepMerging(with: dict1) + XCTAssert(NSDictionary(dictionary: merged2).isEqual(to: dict1)) + } + + func testDeepMergeIdenticalDictionaries() { + let dict1: [String: Any] = ["key1": "value1", "key2": "value2"] + let dict2: [String: Any] = ["key1": "value1", "key2": "value2"] + + let merged = dict1.deepMerging(with: dict2) + XCTAssert(NSDictionary(dictionary: merged).isEqual(to: dict1)) + } + + func testDeepMergeArraysWithDuplicates() { + let dict1: [String: Any] = ["key1": [1, 2, 3]] + let dict2: [String: Any] = ["key1": [3, 4, 5]] + + let merged = dict1.deepMerging(with: dict2) + let expected: [String: Any] = ["key1": [1, 2, 3, 4, 5]] + + XCTAssertEqual(merged["key1"] as? [Int], expected["key1"] as? [Int]) + } + + func testDeepMergeDifferentValueTypes() { + let dict1: [String: Any] = ["key1": 123] + let dict2: [String: Any] = ["key1": "value1"] + + let merged = dict1.deepMerging(with: dict2) + let expected: [String: Any] = ["key1": "value1"] + + XCTAssert(NSDictionary(dictionary: merged).isEqual(to: expected)) + } + + func testDeepMergeReplaceArrayWithDictionary() { + let dict1: [String: Any] = ["key1": [1, 2, 3]] + let dict2: [String: Any] = ["key1": ["nestedKey": "value1"]] + + let merged = dict1.deepMerging(with: dict2) + let expected: [String: Any] = ["key1": ["nestedKey": "value1"]] + + XCTAssert(NSDictionary(dictionary: merged).isEqual(to: expected)) + } + + func testDeepMergeNestedDictionariesWithArrays() { + let dict1: [String: Any] = ["key1": ["nestedKey": [1, 2]]] + let dict2: [String: Any] = ["key1": ["nestedKey": [3, 4]]] + + let merged = dict1.deepMerging(with: dict2) + let expected: [String: Any] = ["key1": ["nestedKey": [1, 2, 3, 4]]] + + XCTAssert(NSDictionary(dictionary: merged["key1"] as! [String: Any]).isEqual(to: expected["key1"] as! [String: Any])) + } + + func testDeepMergePrimitives() { + let dict1: [String: Any] = ["key1": 123] + let dict2: [String: Any] = ["key1": "value"] + + let merged = dict1.deepMerging(with: dict2) + XCTAssertEqual(merged["key1"] as? String, "value") + } + + func testDeepMergeDeepNestedArrays() { + let dict1: [String: Any] = [ + "key1": ["nestedKey1": [["a", "b"], ["c", "d"]]] + ] + let dict2: [String: Any] = [ + "key1": ["nestedKey1": [["e", "f"], ["g", "h"]]] + ] + + let merged = dict1.deepMerging(with: dict2) + let expected: [String: Any] = [ + "key1": ["nestedKey1": [["a", "b"], ["c", "d"], ["e", "f"], ["g", "h"]]] + ] + + XCTAssertEqual(merged as NSDictionary, expected as NSDictionary) + } + + func testDeepMergeDeepNestedDictionaries() { + let dict1: [String: Any] = [ + "key1": [ + "nestedKey1": [ + "deepNestedKey1": "value1" + ], + "nestedKey2": [1, 2, 3] + ] + ] + let dict2: [String: Any] = [ + "key1": [ + "nestedKey1": [ + "deepNestedKey2": "value2" + ], + "nestedKey2": [3, 4, 5] + ] + ] + + let merged = dict1.deepMerging(with: dict2) + let expected: [String: Any] = [ + "key1": [ + "nestedKey1": [ + "deepNestedKey1": "value1", + "deepNestedKey2": "value2" + ], + "nestedKey2": [1, 2, 3, 4, 5] + ] + ] + + XCTAssertEqual(merged as NSDictionary, expected as NSDictionary) + } + private func collectionToString(_ dict: [AnyHashable: Any]) -> String { let jsonData = try! JSONSerialization.data(withJSONObject: dict, options: []) let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue)! as String return jsonString } - } From 704b3f7e464d6e6f3ad5531e52809e0071037391 Mon Sep 17 00:00:00 2001 From: Jono Sligh <139150153+jsligh@users.noreply.github.com> Date: Thu, 19 Dec 2024 05:32:00 -0600 Subject: [PATCH 10/23] Update README.md (#1051) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0f36aa6f5..0132f551f 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,10 @@ Run the test script to run unit tests and integration tests. ## Carthage -`2.2.3` version is available to build PrebidMobile with Carthage. For that, please, put the following content to your `Cartfile`: +`2.3.0` version is available to build PrebidMobile with Carthage. For that, please, put the following content to your `Cartfile`: ``` -github "prebid/prebid-mobile-ios" == 2.2.3-carthage +github "prebid/prebid-mobile-ios" == 2.3.0-carthage ``` Run this command in order to build PrebidMobile with Carthage: From 424c60d33909c683cbbf94a52a41dfbf85f03bb3 Mon Sep 17 00:00:00 2001 From: Jonah Date: Thu, 19 Dec 2024 12:45:25 +0100 Subject: [PATCH 11/23] Make sure gppSID in InternalUserConsentDataManager is nil (#1053) * When user defaults returns an empty string and not nil make sure string is nil * fix: build correct xcframework https://forums.developer.apple.com/forums/thread/666335?answerId=707645022#707645022 --------- Co-authored-by: Jonah Hulselmans --- .../InternalUserConsentDataManager.m | 6 +++++- scripts/buildPrebidMobile.sh | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/PrebidMobile/ConfigurationAndTargeting/InternalUserConsentDataManager.m b/PrebidMobile/ConfigurationAndTargeting/InternalUserConsentDataManager.m index 0e6f9a042..64a219abe 100644 --- a/PrebidMobile/ConfigurationAndTargeting/InternalUserConsentDataManager.m +++ b/PrebidMobile/ConfigurationAndTargeting/InternalUserConsentDataManager.m @@ -40,7 +40,11 @@ + (NSString *)gppHDRString { + (NSMutableArray *)gppSID { NSString * gppSID = [NSUserDefaults.standardUserDefaults stringForKey:InternalUserConsentDataManager.IABGPP_GppSID]; - + + if ([gppSID length] == 0) { + gppSID = nil; + } + NSMutableArray *gppSIDStringArray = [[gppSID componentsSeparatedByString:@"_"] mutableCopy]; NSMutableArray *gppSIDIntArray = [NSMutableArray new]; diff --git a/scripts/buildPrebidMobile.sh b/scripts/buildPrebidMobile.sh index 4c6f73df6..bcd7f8e2e 100755 --- a/scripts/buildPrebidMobile.sh +++ b/scripts/buildPrebidMobile.sh @@ -60,6 +60,7 @@ do only_active_arch=NO \ defines_module=YES \ SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ -workspace PrebidMobile.xcworkspace \ -scheme "${schemes[$n]}" \ -configuration Release \ @@ -75,10 +76,10 @@ do only_active_arch=NO \ defines_module=YES \ SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ -workspace PrebidMobile.xcworkspace \ -scheme "${schemes[$n]}" \ -configuration Release \ - -arch x86_64 \ -sdk "iphonesimulator" \ -derivedDataPath $XCODE_BUILD_DIR \ -archivePath "$XCODE_ARCHIVE_DIR/${schemes[$n]}$POSTFIX_SIMULATOR.xcarchive" \ From c04a4156d8b16ab3a49940b11fbd249400bab8c8 Mon Sep 17 00:00:00 2001 From: Ankit Rajendra Thanekar Date: Thu, 19 Dec 2024 14:50:35 +0000 Subject: [PATCH 12/23] Make Parsing Methods Type Safe (#1040) * Make type safe * Type check before init * Check for passthrough response type * Remove Unnecessary change * Update PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.m Co-authored-by: David Robles * Update PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidExt.m Co-authored-by: David Robles * Update PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidExtPrebid.m Co-authored-by: David Robles * Update PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidResponseExtPrebid.m Co-authored-by: David Robles --------- Co-authored-by: Ankit Rajendra Thanekar Co-authored-by: David Robles --- .../Prebid/PBMCore/ORTB/Prebid/PBMORTBBidExt.m | 3 ++- .../PBMCore/ORTB/Prebid/PBMORTBBidExtPrebid.m | 2 +- .../ORTB/Prebid/PBMORTBBidResponseExtPrebid.m | 2 +- .../PrebidMobileRendering/Utilities/PBMFunctions.h | 3 ++- .../PrebidMobileRendering/Utilities/PBMFunctions.m | 13 ++++++++++++- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidExt.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidExt.m index ccc2bedb5..e19748c72 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidExt.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidExt.m @@ -43,7 +43,8 @@ - (instancetype)initWithJsonDictionary:(PBMJsonDictionary *)jsonDictionary { } #if DEBUG - NSArray * const passthroughDics = jsonDictionary[@"passthrough"]; + + NSArray *const passthroughDics = [PBMFunctions dictionariesForPassthrough:jsonDictionary[@"passthrough"]]; _passthrough = nil; if (passthroughDics) { NSMutableArray * const newPassthrough = [[NSMutableArray alloc] initWithCapacity:passthroughDics.count]; diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidExtPrebid.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidExtPrebid.m index e7f3535d2..f936e5e41 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidExtPrebid.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidExtPrebid.m @@ -36,7 +36,7 @@ - (instancetype)initWithJsonDictionary:(PBMJsonDictionary *)jsonDictionary { _meta = jsonDictionary[@"meta"]; _type = jsonDictionary[@"type"]; - NSArray * const passthroughDics = jsonDictionary[@"passthrough"]; + NSArray *const passthroughDics = [PBMFunctions dictionariesForPassthrough:jsonDictionary[@"passthrough"]]; _passthrough = nil; if (passthroughDics) { NSMutableArray * const newPassthrough = [[NSMutableArray alloc] initWithCapacity:passthroughDics.count]; diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidResponseExtPrebid.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidResponseExtPrebid.m index 0f4acc4cf..8eb8b7359 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidResponseExtPrebid.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/ORTB/Prebid/PBMORTBBidResponseExtPrebid.m @@ -22,7 +22,7 @@ - (instancetype)initWithJsonDictionary:(PBMJsonDictionary *)jsonDictionary { return nil; } - NSArray * const passthroughDics = jsonDictionary[@"passthrough"]; + NSArray *const passthroughDics = [PBMFunctions dictionariesForPassthrough:jsonDictionary[@"passthrough"]]; _passthrough = nil; if (passthroughDics) { NSMutableArray * const newPassthrough = [[NSMutableArray alloc] initWithCapacity:passthroughDics.count]; diff --git a/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.h b/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.h index de288064d..548d989f8 100644 --- a/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.h +++ b/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.h @@ -15,6 +15,7 @@ #import #import +#import "PBMConstants.h" NS_ASSUME_NONNULL_BEGIN @interface PBMFunctions : NSObject @@ -24,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN + (NSDictionary *)extractVideoAdParamsFromTheURLString:(NSString *)urlString forKeys:(NSArray *)keys; + (BOOL)canLoadVideoAdWithDomain:(NSString *)domain adUnitID:(nullable NSString *)adUnitID adUnitGroupID:(nullable NSString *)adUnitGroupID; + (void)checkCertificateChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler; - ++ (nullable NSArray *)dictionariesForPassthrough:(id)passthrough; //FIXME: move to private fucntions ?? #pragma mark - SDK Info diff --git a/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.m b/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.m index c43381e37..fc36b6cd9 100644 --- a/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.m +++ b/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.m @@ -17,7 +17,6 @@ #import "PBMFunctions+Private.h" #import "PBMFunctions+Testing.h" -#import "PBMConstants.h" #import "PBMError.h" #import "PrebidMobileSwiftHeaders.h" @@ -231,6 +230,18 @@ + (nullable NSString *)toStringJsonDictionary:(nonnull PBMJsonDictionary *)jsonD return [jsonString stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; } ++ (nullable NSArray *)dictionariesForPassthrough:(id)passthrough { + if ([passthrough isKindOfClass:[NSArray class]]) { + NSArray *response = passthrough; + return response; + } else if ([passthrough isKindOfClass:[PBMJsonDictionary class]]) { + NSDictionary *response = passthrough; + return @[response]; + } else { + return nil; + } +} + #pragma mark - SDK Info + (nonnull NSBundle *)bundleForSDK { From 41d87586601aacd9e1ffa7cd20aaae735352badd Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:22:08 +0200 Subject: [PATCH 13/23] Bump to 2.3.1 (#1076) * feat: bump to 2.3.1 * feat: update tests --- .../EventHandlers.xcodeproj/project.pbxproj | 24 +++++++-------- .../Sources/AdMobConstants.swift | 2 +- .../Sources/MAXConstants.swift | 2 +- PrebidMobile.podspec | 2 +- PrebidMobile.xcodeproj/project.pbxproj | 8 ++--- .../xcschemes/PrebidMobile.xcscheme | 3 +- .../xcschemes/PrebidMobileTests.xcscheme | 3 +- PrebidMobile/Constants.swift | 2 +- PrebidMobileAdMobAdapters.podspec | 4 +-- PrebidMobileGAMEventHandlers.podspec | 4 +-- PrebidMobileMAXAdapters.podspec | 4 +-- .../AdUnitTests/AdUnitTests.swift | 29 ++----------------- .../addendum/AdViewUtilsTests.swift | 6 ++-- 13 files changed, 35 insertions(+), 58 deletions(-) diff --git a/EventHandlers/EventHandlers.xcodeproj/project.pbxproj b/EventHandlers/EventHandlers.xcodeproj/project.pbxproj index 59a7bd111..14f7ec86a 100644 --- a/EventHandlers/EventHandlers.xcodeproj/project.pbxproj +++ b/EventHandlers/EventHandlers.xcodeproj/project.pbxproj @@ -1234,7 +1234,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2.3.0; + CURRENT_PROJECT_VERSION = 2.3.1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1248,7 +1248,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.3.0; + MARKETING_VERSION = 2.3.1; MODULEMAP_FILE = ""; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileGAMEventHandlers; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -1269,7 +1269,7 @@ CLANG_ENABLE_MODULES = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2.3.0; + CURRENT_PROJECT_VERSION = 2.3.1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1283,7 +1283,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.3.0; + MARKETING_VERSION = 2.3.1; MODULEMAP_FILE = ""; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileGAMEventHandlers; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -1303,7 +1303,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2.3.0; + CURRENT_PROJECT_VERSION = 2.3.1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1318,7 +1318,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.3.0; + MARKETING_VERSION = 2.3.1; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileMAXAdapters; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -1338,7 +1338,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2.3.0; + CURRENT_PROJECT_VERSION = 2.3.1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1353,7 +1353,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.3.0; + MARKETING_VERSION = 2.3.1; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileMAXAdapters; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; @@ -1415,7 +1415,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2.3.0; + CURRENT_PROJECT_VERSION = 2.3.1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -1429,7 +1429,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.3.0; + MARKETING_VERSION = 2.3.1; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileAdMobAdapters; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = None; @@ -1450,7 +1450,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2.3.0; + CURRENT_PROJECT_VERSION = 2.3.1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -1464,7 +1464,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.3.0; + MARKETING_VERSION = 2.3.1; PRODUCT_BUNDLE_IDENTIFIER = org.prebid.mobile.PrebidMobileAdMobAdapters; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = None; diff --git a/EventHandlers/PrebidMobileAdMobAdapters/Sources/AdMobConstants.swift b/EventHandlers/PrebidMobileAdMobAdapters/Sources/AdMobConstants.swift index 0c65b4dd6..424d3131a 100644 --- a/EventHandlers/PrebidMobileAdMobAdapters/Sources/AdMobConstants.swift +++ b/EventHandlers/PrebidMobileAdMobAdapters/Sources/AdMobConstants.swift @@ -18,5 +18,5 @@ import Foundation @objcMembers public class AdMobConstants: NSObject { public static let PrebidAdMobEventExtrasLabel = "PrebidAdMobCustomEventExtras" - public static let PrebidAdMobRewardedAdapterVersion = "2.0.0" + public static let PrebidAdMobRewardedAdapterVersion = "2.3.1" } diff --git a/EventHandlers/PrebidMobileMAXAdapters/Sources/MAXConstants.swift b/EventHandlers/PrebidMobileMAXAdapters/Sources/MAXConstants.swift index 6edafe372..eaa73a6ab 100644 --- a/EventHandlers/PrebidMobileMAXAdapters/Sources/MAXConstants.swift +++ b/EventHandlers/PrebidMobileMAXAdapters/Sources/MAXConstants.swift @@ -17,5 +17,5 @@ import Foundation @objcMembers public class MAXConstants: NSObject { - public static let PrebidMAXAdapterVersion = "2.0.0" + public static let PrebidMAXAdapterVersion = "2.3.1" } diff --git a/PrebidMobile.podspec b/PrebidMobile.podspec index d3df2e1c0..ede23acc5 100644 --- a/PrebidMobile.podspec +++ b/PrebidMobile.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "PrebidMobile" - s.version = "2.3.0" + s.version = "2.3.1" s.summary = "PrebidMobile is a lightweight framework that integrates directly with Prebid Server." s.description = <<-DESC diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index 38eb9de04..68a135034 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -4629,7 +4629,7 @@ CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; - CURRENT_PROJECT_VERSION = 2.3.0; + CURRENT_PROJECT_VERSION = 2.3.1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -4648,7 +4648,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.3.0; + MARKETING_VERSION = 2.3.1; MODULEMAP_FILE = PrebidMobile/BuildFiles/PrebidMobile.modulemap; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -4667,7 +4667,7 @@ CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; - CURRENT_PROJECT_VERSION = 2.3.0; + CURRENT_PROJECT_VERSION = 2.3.1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -4686,7 +4686,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); - MARKETING_VERSION = 2.3.0; + MARKETING_VERSION = 2.3.1; MODULEMAP_FILE = PrebidMobile/BuildFiles/PrebidMobile.modulemap; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; diff --git a/PrebidMobile.xcodeproj/xcshareddata/xcschemes/PrebidMobile.xcscheme b/PrebidMobile.xcodeproj/xcshareddata/xcschemes/PrebidMobile.xcscheme index 80bf8c513..ca877f5c7 100644 --- a/PrebidMobile.xcodeproj/xcshareddata/xcschemes/PrebidMobile.xcscheme +++ b/PrebidMobile.xcodeproj/xcshareddata/xcschemes/PrebidMobile.xcscheme @@ -52,7 +52,8 @@ + skipped = "NO" + parallelizable = "NO"> + skipped = "NO" + parallelizable = "NO"> Date: Mon, 6 Jan 2025 10:07:55 +0200 Subject: [PATCH 14/23] Prebid plugin render changes (#1068) * feat: ad view custom renderer prototype * feat: interstitial custom renderer prototype * feat: introduce PrebidMobileDisplayViewProtocol and PrebidMobileInterstitialControllerProtocol * feat: send data in ORTB request * fix: plugin register unit tests * tests: add request tests * fix: missing winning ad format * feat: enhance InternalTestApp examples * feat: add plugin renderer cases to demo apps * feat: minor corrections * feat: unregister plugin renderer * fix: resolve issues after rebasing * feat: make universal Renderer protocol * feat: change data property type & use default banner view if none provided * feat: remove isSupportRendering * feat: store SDK renderer in plugin array & register plugin during SDK initialization * feat: restore .xcodeproj * feat: add log message in case default renderer wasn't found in registry --- .../Sources/PrebidAdMobBannerAdapter.swift | 16 +- .../PrebidAdMobInterstitialAdapter.swift | 24 ++- .../Sources/PrebidAdMobRewardedAdapter.swift | 20 +- .../PrebidAdMobVideoInterstitialAdapter.swift | 26 ++- ...dMAXMediationAdapter+MAAdViewAdapter.swift | 28 ++- ...nterstitialAdapter+MARewardedAdapter.swift | 34 ++-- .../PrebidDemo.xcodeproj/project.pbxproj | 72 +++++++ ...isplayBannerPluginRendererViewController.h | 26 +++ ...isplayBannerPluginRendererViewController.m | 77 ++++++++ ...InterstitialPluginRendererViewController.h | 26 +++ ...InterstitialPluginRendererViewController.m | 71 +++++++ .../InApp/SampleRenderers/SampleAdView.h | 35 ++++ .../InApp/SampleRenderers/SampleAdView.m | 89 +++++++++ .../SampleInterstitialController.h | 33 ++++ .../SampleInterstitialController.m | 89 +++++++++ .../InApp/SampleRenderers/SampleRenderer.h | 31 +++ .../InApp/SampleRenderers/SampleRenderer.m | 60 ++++++ .../IntegrationCase/IntegrationCaseManager.m | 22 +++ .../Utils/UIApplication+TopViewController.h | 26 +++ .../Utils/UIApplication+TopViewController.m | 66 +++++++ ...ayBannerCustomRendererViewController.swift | 66 +++++++ ...ayBannerPluginRendererViewController.swift | 72 +++++++ ...rstitialPluginRendererViewController.swift | 64 ++++++ .../In-App/SampleRenderers/SampleAdView.swift | 97 ++++++++++ .../SampleInterstitialController.swift | 108 +++++++++++ .../SampleRenderers/SampleRenderer.swift | 59 ++++++ .../Extensions/UIApplication+Extensions.swift | 47 ++++- .../IntegrationCaseManager.swift | 18 ++ .../BannerAdsTest.swift | 27 ++- .../PrebidDemoSwiftUITests/BaseAdsTest.swift | 1 - .../InterstitialAdsTest.swift | 38 +++- .../Utils/TestCases.swift | 2 + .../InternalTestApp.xcodeproj/project.pbxproj | 60 ++++-- .../Model/TestCasesManager.swift | 23 ++- .../CustomRendererBannerController.swift | 17 +- ...CustomRendererInterstitialController.swift | 16 +- .../Renderers/SampleAdView.swift | 136 +++++++++++++ .../SampleInterstitialController.swift | 124 ++++++++++++ .../Renderers/SampleModalViewController.swift | 60 ++++++ .../Renderers/SampleRenderer.swift | 55 ++++++ PrebidMobile.xcodeproj/project.pbxproj | 106 +++++----- .../BuildFiles/PrebidMobile.modulemap | 2 + .../BuildFiles/PrebidMobileSwiftHeaders.h | 2 + .../ConfigurationAndTargeting/Prebid.swift | 12 +- .../PrebidSDKInitializer.swift | 3 + .../ORTB/PBMORTBBidRequestExtPrebid.h | 4 +- .../ORTB/PBMORTBBidRequestExtPrebid.m | 6 +- .../PluginRenderer/PBMAdViewDelegate.swift | 14 -- .../PrebidMobilePluginRegister.swift | 135 +++++++------ ...rebidMobilePluginRenderer+Extensions.swift | 29 +++ .../PrebidMobilePluginRenderer.swift | 107 ++++++---- .../PluginRenderer/SampleCustomRenderer.swift | 83 -------- .../AdLoading/BannerAdLoaderDelegate.swift | 5 - .../GAM/AdLoading/PBMBannerAdLoader.m | 56 ++++-- .../GAM/AdLoading/PBMInterstitialAdLoader.h | 1 + .../GAM/AdLoading/PBMInterstitialAdLoader.m | 57 ++++-- .../Prebid/Integrations/GAM/BannerView.swift | 182 ++++++++++-------- .../GAM/BaseInterstitialAdUnit.swift | 16 +- .../GAM/BaseInterstitialAdUnitProtocol.swift | 6 +- .../Integrations/GAM/BaseRewardedAdUnit.swift | 5 +- .../GAM/InterstitialRenderingAdUnit.swift | 2 +- .../Integrations/GAM/RewardedAdUnit.swift | 2 +- .../Integrations/GAM/RewardedConfig.swift | 2 +- .../BannerEventHandlerStandalone.swift | 1 + .../DisplayViewInteractionDelegate.swift | 38 +++- .../DisplayViewLoadingDelegate.swift | 23 ++- .../InterstitialController.swift | 154 ++++++++------- ...stitialControllerInteractionDelegate.swift | 74 ++++++- ...nterstitialControllerLoadingDelegate.swift | 24 ++- .../PBMDisplayView+InternalState.h | 4 +- .../Prebid/PBMCacheRenderers/PBMDisplayView.h | 13 +- .../Prebid/PBMCacheRenderers/PBMDisplayView.m | 68 +++++-- .../PrebidMobileDisplayViewProtocol.h | 26 +++ ...MobileInterstitialControllerProtocol.swift | 29 +++ .../PBMCacheRenderers/PrebidRenderer.swift | 152 +++++---------- .../Prebid/PBMCore/AdUnitConfig.swift | 4 + .../Prebid/PBMCore/Bid.swift | 47 ++--- .../Prebid/PBMCore/BidResponse.swift | 8 - .../PBMCore/PBMPrebidParameterBuilder.m | 19 +- .../MockPrebidMobilePluginRenderer.swift | 55 ++++++ .../Tests/PBMORTBAbstractTest.swift | 5 +- .../PrebidParameterBuilderTest.swift | 31 +++ .../Tests/Prebid}/PluginRegisterTest.swift | 79 ++++---- .../RenderingTests/Tests/PrebidTest.swift | 10 + 84 files changed, 2851 insertions(+), 811 deletions(-) create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayBannerPluginRendererViewController.h create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayBannerPluginRendererViewController.m create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayInterstitialPluginRendererViewController.h create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayInterstitialPluginRendererViewController.m create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleAdView.h create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleAdView.m create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleInterstitialController.h create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleInterstitialController.m create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleRenderer.h create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleRenderer.m create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Utils/UIApplication+TopViewController.h create mode 100644 Example/PrebidDemo/PrebidDemoObjectiveC/Utils/UIApplication+TopViewController.m create mode 100644 Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayBannerCustomRendererViewController.swift create mode 100644 Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayBannerPluginRendererViewController.swift create mode 100644 Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayInterstitialPluginRendererViewController.swift create mode 100644 Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleAdView.swift create mode 100644 Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleInterstitialController.swift create mode 100644 Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleRenderer.swift rename InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/{ => OpenX}/CustomRenderer/CustomRendererBannerController.swift (96%) rename InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/{ => OpenX}/CustomRenderer/CustomRendererInterstitialController.swift (94%) create mode 100644 InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleAdView.swift create mode 100644 InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleInterstitialController.swift create mode 100644 InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleModalViewController.swift create mode 100644 InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleRenderer.swift delete mode 100644 PrebidMobile/PrebidMobileRendering/PluginRenderer/PBMAdViewDelegate.swift create mode 100644 PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRenderer+Extensions.swift delete mode 100644 PrebidMobile/PrebidMobileRendering/PluginRenderer/SampleCustomRenderer.swift create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidMobileDisplayViewProtocol.h create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidMobileInterstitialControllerProtocol.swift create mode 100644 PrebidMobileTests/RenderingTests/Mocks/MockPrebidMobilePluginRenderer.swift rename PrebidMobileTests/{UnitTests => RenderingTests/Tests/Prebid}/PluginRegisterTest.swift (50%) diff --git a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobBannerAdapter.swift b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobBannerAdapter.swift index 07822e3f3..f2b08544b 100644 --- a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobBannerAdapter.swift +++ b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobBannerAdapter.swift @@ -83,18 +83,18 @@ public class PrebidAdMobBannerAdapter: displayView?.interactionDelegate = self displayView?.loadingDelegate = self - displayView?.displayAd() + displayView?.loadAd() } // MARK: - DisplayViewLoadingDelegate - public func displayViewDidLoadAd(_ displayView: PBMDisplayView) { + public func displayViewDidLoadAd(_ displayViewManager: UIView) { if let handler = completionHandler { delegate = handler(self, nil) } } - public func displayView(_ displayView: PBMDisplayView, didFailWithError error: Error) { + public func displayView(_ displayViewManager: UIView, didFailWithError error: Error) { if let handler = completionHandler { delegate = handler(nil, error) } @@ -102,23 +102,23 @@ public class PrebidAdMobBannerAdapter: // MARK: - PBMDisplayViewInteractionDelegate - public func trackImpression(forDisplayView: PBMDisplayView) { + public func trackImpression(forDisplayView: UIView) { delegate?.reportImpression() } - public func viewControllerForModalPresentation(fromDisplayView: PBMDisplayView) -> UIViewController? { + public func viewControllerForModalPresentation(fromDisplayView: UIView) -> UIViewController? { return adConfiguration?.topViewController ?? UIApplication.shared.windows.first?.rootViewController } - public func didLeaveApp(from displayView: PBMDisplayView) { + public func didLeaveApp(from displayView: UIView) { delegate?.reportClick() } - public func willPresentModal(from displayView: PBMDisplayView) { + public func willPresentModal(from displayView: UIView) { delegate?.willPresentFullScreenView() } - public func didDismissModal(from displayView: PBMDisplayView) { + public func didDismissModal(from displayView: UIView) { delegate?.willDismissFullScreenView() delegate?.didDismissFullScreenView() } diff --git a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobInterstitialAdapter.swift b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobInterstitialAdapter.swift index d7c796d05..22cc31250 100644 --- a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobInterstitialAdapter.swift +++ b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobInterstitialAdapter.swift @@ -95,7 +95,7 @@ public class PrebidAdMobInterstitialAdapter: // MARK: - InterstitialControllerLoadingDelegate - public func interstitialControllerDidLoadAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidLoadAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { adAvailable = true if let handler = completionHandler { @@ -103,7 +103,10 @@ public class PrebidAdMobInterstitialAdapter: } } - public func interstitialController(_ interstitialController: InterstitialController, didFailWithError error: Error) { + public func interstitialController( + _ interstitialController: PrebidMobileInterstitialControllerProtocol, + didFailWithError error: Error + ) { adAvailable = false if let handler = completionHandler { @@ -113,28 +116,29 @@ public class PrebidAdMobInterstitialAdapter: // MARK: - InterstitialControllerInteractionDelegate - public func trackImpression(forInterstitialController: InterstitialController) { + public func trackImpression(forInterstitialController: PrebidMobileInterstitialControllerProtocol) { delegate?.reportImpression() } - public func viewControllerForModalPresentation(fromInterstitialController: InterstitialController) -> UIViewController? { + public func viewControllerForModalPresentation( + fromInterstitialController: PrebidMobileInterstitialControllerProtocol + ) -> UIViewController? { rootViewController } - public func interstitialControllerDidClickAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidClickAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { delegate?.reportClick() } - public func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidCloseAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { delegate?.willDismissFullScreenView() delegate?.didDismissFullScreenView() } - public func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) { + public func interstitialControllerDidDisplay(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { delegate?.willPresentFullScreenView() } - public func interstitialControllerDidComplete(_ interstitialController: InterstitialController) {} - - public func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) {} + public func interstitialControllerDidComplete(_ interstitialController: PrebidMobileInterstitialControllerProtocol) {} + public func interstitialControllerDidLeaveApp(_ interstitialController: PrebidMobileInterstitialControllerProtocol) {} } diff --git a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedAdapter.swift b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedAdapter.swift index 759310bcc..d7f1bf4df 100644 --- a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedAdapter.swift +++ b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobRewardedAdapter.swift @@ -113,7 +113,7 @@ public class PrebidAdMobRewardedAdapter: // MARK: - InterstitialControllerLoadingDelegate - public func interstitialControllerDidLoadAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidLoadAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { adAvailable = true if let handler = completionHandler { @@ -121,46 +121,46 @@ public class PrebidAdMobRewardedAdapter: } } - public func interstitialController(_ interstitialController: InterstitialController, didFailWithError error: Error) { + public func interstitialController(_ interstitialController: PrebidMobileInterstitialControllerProtocol, didFailWithError error: Error) { adAvailable = false } // MARK: - InterstitialControllerInteractionDelegate - public func trackImpression(forInterstitialController: InterstitialController) { + public func trackImpression(forInterstitialController: PrebidMobileInterstitialControllerProtocol) { delegate?.reportImpression() } - public func trackUserReward(_ interstitialController: InterstitialController, _ reward: PrebidReward) { + public func trackUserReward(_ interstitialController: PrebidMobileInterstitialControllerProtocol, _ reward: PrebidReward) { delegate?.didRewardUser() } - public func interstitialControllerDidClickAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidClickAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { delegate?.reportClick() } - public func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidCloseAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { adAvailable = false delegate?.willDismissFullScreenView() delegate?.didDismissFullScreenView() } - public func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) { + public func interstitialControllerDidDisplay(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { delegate?.willPresentFullScreenView() delegate?.didStartVideo() delegate?.didEndVideo() } - public func interstitialControllerDidComplete(_ interstitialController: InterstitialController) { + public func interstitialControllerDidComplete(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { adAvailable = false rootViewController = nil delegate?.didRewardUser() } - public func viewControllerForModalPresentation(fromInterstitialController: InterstitialController) -> UIViewController? { + public func viewControllerForModalPresentation(fromInterstitialController: PrebidMobileInterstitialControllerProtocol) -> UIViewController? { rootViewController } - public func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) {} + public func interstitialControllerDidLeaveApp(_ interstitialController: PrebidMobileInterstitialControllerProtocol) {} } diff --git a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobVideoInterstitialAdapter.swift b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobVideoInterstitialAdapter.swift index 0613cc6b7..e7affda19 100644 --- a/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobVideoInterstitialAdapter.swift +++ b/EventHandlers/PrebidMobileAdMobAdapters/Sources/PrebidAdMobVideoInterstitialAdapter.swift @@ -103,7 +103,9 @@ public class PrebidAdMobVideoInterstitialAdapter: // MARK: - InterstitialControllerLoadingDelegate - public func interstitialControllerDidLoadAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidLoadAd( + _ interstitialController: PrebidMobileInterstitialControllerProtocol + ) { adAvailable = true if let handler = completionHandler { @@ -111,7 +113,10 @@ public class PrebidAdMobVideoInterstitialAdapter: } } - public func interstitialController(_ interstitialController: InterstitialController, didFailWithError error: Error) { + public func interstitialController( + _ interstitialController: PrebidMobileInterstitialControllerProtocol, + didFailWithError error: Error + ) { adAvailable = false if let handler = completionHandler { @@ -121,28 +126,29 @@ public class PrebidAdMobVideoInterstitialAdapter: // MARK: - InterstitialControllerInteractionDelegate - public func trackImpression(forInterstitialController: InterstitialController) { + public func trackImpression(forInterstitialController: PrebidMobileInterstitialControllerProtocol) { delegate?.reportImpression() } - public func viewControllerForModalPresentation(fromInterstitialController: InterstitialController) -> UIViewController? { + public func viewControllerForModalPresentation( + fromInterstitialController: PrebidMobileInterstitialControllerProtocol + ) -> UIViewController? { rootViewController } - public func interstitialControllerDidClickAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidClickAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { delegate?.reportClick() } - public func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidCloseAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { delegate?.willDismissFullScreenView() delegate?.didDismissFullScreenView() } - public func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) { + public func interstitialControllerDidDisplay(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { delegate?.willPresentFullScreenView() } - public func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) {} - - public func interstitialControllerDidComplete(_ interstitialController: InterstitialController) {} + public func interstitialControllerDidLeaveApp(_ interstitialController: PrebidMobileInterstitialControllerProtocol) {} + public func interstitialControllerDidComplete(_ interstitialController: PrebidMobileInterstitialControllerProtocol) {} } diff --git a/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAAdViewAdapter.swift b/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAAdViewAdapter.swift index 67cb939a0..a0e7cc908 100644 --- a/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAAdViewAdapter.swift +++ b/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAAdViewAdapter.swift @@ -17,7 +17,10 @@ import Foundation import PrebidMobile import AppLovinSDK -extension PrebidMAXMediationAdapter: MAAdViewAdapter, DisplayViewLoadingDelegate, DisplayViewInteractionDelegate { +extension PrebidMAXMediationAdapter: + MAAdViewAdapter, + DisplayViewLoadingDelegate, + DisplayViewInteractionDelegate { // MARK: - MAAdViewAdapter @@ -59,41 +62,36 @@ extension PrebidMAXMediationAdapter: MAAdViewAdapter, DisplayViewLoadingDelegate displayView?.interactionDelegate = self displayView?.loadingDelegate = self - displayView?.displayAd() + displayView?.loadAd() } // MARK: - DisplayViewLoadingDelegate - public func displayViewDidLoadAd(_ displayView: PBMDisplayView) { + public func displayViewDidLoadAd(_ displayView: UIView) { bannerDelegate?.didLoadAd(forAdView: displayView) } - public func displayView(_ displayView: PBMDisplayView, didFailWithError error: Error) { + public func displayView(_ displayView: UIView, didFailWithError error: Error) { let maError = MAAdapterError(nsError: error) bannerDelegate?.didFailToLoadAdViewAdWithError(maError) } // MARK: DisplayViewInteractionDelegate - public func trackImpression(forDisplayView: PBMDisplayView) { - - } - - public func viewControllerForModalPresentation(fromDisplayView: PBMDisplayView) -> UIViewController? { + public func viewControllerForModalPresentation(fromDisplayView: UIView) -> UIViewController? { return UIApplication.shared.windows.first?.rootViewController } - public func didLeaveApp(from displayView: PBMDisplayView) { - - } - - public func willPresentModal(from displayView: PBMDisplayView) { + public func willPresentModal(from displayView: UIView) { bannerDelegate?.didClickAdViewAd() bannerDelegate?.didExpandAdViewAd() } - public func didDismissModal(from displayView: PBMDisplayView) { + public func didDismissModal(from displayView: UIView) { bannerDelegate?.didHideAdViewAd() bannerDelegate?.didCollapseAdViewAd() } + + public func trackImpression(forDisplayView: UIView) {} + public func didLeaveApp(from displayView: UIView) {} } diff --git a/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAInterstitialAdapter+MARewardedAdapter.swift b/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAInterstitialAdapter+MARewardedAdapter.swift index 3dd04e14b..749a1ea3b 100644 --- a/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAInterstitialAdapter+MARewardedAdapter.swift +++ b/EventHandlers/PrebidMobileMAXAdapters/Sources/PrebidMAXMediationAdapter+MAInterstitialAdapter+MARewardedAdapter.swift @@ -121,13 +121,16 @@ extension PrebidMAXMediationAdapter: MAInterstitialAdapter, // MARK: - InterstitialControllerLoadingDelegate - public func interstitialControllerDidLoadAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidLoadAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { interstitialAdAvailable = true interstitialDelegate?.didLoadInterstitialAd() rewardedDelegate?.didLoadRewardedAd() } - public func interstitialController(_ interstitialController: InterstitialController, didFailWithError error: Error) { + public func interstitialController( + _ interstitialController: PrebidMobileInterstitialControllerProtocol, + didFailWithError error: Error + ) { interstitialAdAvailable = false let maError = MAAdapterError(nsError: error) interstitialDelegate?.didFailToLoadInterstitialAdWithError(maError) @@ -136,45 +139,32 @@ extension PrebidMAXMediationAdapter: MAInterstitialAdapter, // MARK: - InterstitialControllerInteractionDelegate - public func trackImpression(forInterstitialController: InterstitialController) {} - - public func interstitialControllerDidClickAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidClickAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { interstitialDelegate?.didClickInterstitialAd() rewardedDelegate?.didClickRewardedAd() } - public func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidCloseAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { interstitialDelegate?.didHideInterstitialAd() rewardedDelegate?.didHideRewardedAd() } - public func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) {} - - public func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) { + public func interstitialControllerDidDisplay(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { interstitialDelegate?.didDisplayInterstitialAd() rewardedDelegate?.didDisplayRewardedAd() } - public func interstitialControllerDidComplete(_ interstitialController: InterstitialController) { + public func interstitialControllerDidComplete(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { interstitialAdAvailable = false rewardedDelegate?.didRewardUser(with: MAReward()) } public func viewControllerForModalPresentation( - fromInterstitialController: InterstitialController + fromInterstitialController: PrebidMobileInterstitialControllerProtocol ) -> UIViewController? { return UIApplication.shared.windows.first?.rootViewController } - public func trackUserReward( - _ interstitialController: InterstitialController, - _ reward: PrebidReward - ) { - let reward = MAReward( - amount: reward.count?.intValue ?? 0, - label: reward.type ?? "" - ) - - rewardedDelegate?.didRewardUser(with: reward) - } + public func trackImpression(forInterstitialController: PrebidMobileInterstitialControllerProtocol) {} + public func interstitialControllerDidLeaveApp(_ interstitialController: PrebidMobileInterstitialControllerProtocol) {} } diff --git a/Example/PrebidDemo/PrebidDemo.xcodeproj/project.pbxproj b/Example/PrebidDemo/PrebidDemo.xcodeproj/project.pbxproj index 31472ff3a..68c6df8dd 100644 --- a/Example/PrebidDemo/PrebidDemo.xcodeproj/project.pbxproj +++ b/Example/PrebidDemo/PrebidDemo.xcodeproj/project.pbxproj @@ -75,6 +75,17 @@ 534C615E2CB84DCB0026119A /* AdMobDisplayRewardedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C615D2CB84DCB0026119A /* AdMobDisplayRewardedViewController.m */; }; 534C61612CB850FD0026119A /* GAMDisplayRewardedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C61602CB850FD0026119A /* GAMDisplayRewardedViewController.m */; }; 534C61642CB851C10026119A /* MAXDisplayRewardedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 534C61632CB851C10026119A /* MAXDisplayRewardedViewController.m */; }; + 53514C962CFF97DB00A480C0 /* InAppDisplayBannerPluginRendererViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514C952CFF97DB00A480C0 /* InAppDisplayBannerPluginRendererViewController.swift */; }; + 53514C9E2CFF988E00A480C0 /* SampleAdView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514C982CFF988E00A480C0 /* SampleAdView.swift */; }; + 53514C9F2CFF988E00A480C0 /* SampleInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514C9B2CFF988E00A480C0 /* SampleInterstitialController.swift */; }; + 53514CA02CFF988E00A480C0 /* SampleRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514C992CFF988E00A480C0 /* SampleRenderer.swift */; }; + 53514CA32CFF9DD000A480C0 /* InAppDisplayInterstitialPluginRendererViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CA22CFF9DD000A480C0 /* InAppDisplayInterstitialPluginRendererViewController.swift */; }; + 53514CA72CFFA52A00A480C0 /* SampleAdView.m in Sources */ = {isa = PBXBuildFile; fileRef = 53514CA62CFFA52A00A480C0 /* SampleAdView.m */; }; + 53514CAA2CFFA62700A480C0 /* SampleRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 53514CA92CFFA62700A480C0 /* SampleRenderer.m */; }; + 53514CAD2CFFA99200A480C0 /* InAppDisplayBannerPluginRendererViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53514CAC2CFFA99200A480C0 /* InAppDisplayBannerPluginRendererViewController.m */; }; + 53514CB02CFFAB6700A480C0 /* SampleInterstitialController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53514CAF2CFFAB6700A480C0 /* SampleInterstitialController.m */; }; + 53514CB32CFFAD5E00A480C0 /* UIApplication+TopViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53514CB22CFFAD5E00A480C0 /* UIApplication+TopViewController.m */; }; + 53514CB92CFFAECB00A480C0 /* InAppDisplayInterstitialPluginRendererViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 53514CB82CFFAECB00A480C0 /* InAppDisplayInterstitialPluginRendererViewController.m */; }; 539928BF292534EB0078053C /* IntegrationCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 539928BE292534EA0078053C /* IntegrationCase.m */; }; 539928C2292535B70078053C /* AdFormatDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 539928C1292535B70078053C /* AdFormatDescriptor.m */; }; 539928C92925379C0078053C /* IntegrationKindDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 539928C82925379C0078053C /* IntegrationKindDescriptor.m */; }; @@ -293,6 +304,23 @@ 534C61602CB850FD0026119A /* GAMDisplayRewardedViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GAMDisplayRewardedViewController.m; sourceTree = ""; }; 534C61622CB851C10026119A /* MAXDisplayRewardedViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MAXDisplayRewardedViewController.h; sourceTree = ""; }; 534C61632CB851C10026119A /* MAXDisplayRewardedViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MAXDisplayRewardedViewController.m; sourceTree = ""; }; + 53514C952CFF97DB00A480C0 /* InAppDisplayBannerPluginRendererViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppDisplayBannerPluginRendererViewController.swift; sourceTree = ""; }; + 53514C982CFF988E00A480C0 /* SampleAdView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleAdView.swift; sourceTree = ""; }; + 53514C992CFF988E00A480C0 /* SampleRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleRenderer.swift; sourceTree = ""; }; + 53514C9B2CFF988E00A480C0 /* SampleInterstitialController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleInterstitialController.swift; sourceTree = ""; }; + 53514CA22CFF9DD000A480C0 /* InAppDisplayInterstitialPluginRendererViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppDisplayInterstitialPluginRendererViewController.swift; sourceTree = ""; }; + 53514CA52CFFA52A00A480C0 /* SampleAdView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SampleAdView.h; sourceTree = ""; }; + 53514CA62CFFA52A00A480C0 /* SampleAdView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleAdView.m; sourceTree = ""; }; + 53514CA82CFFA62700A480C0 /* SampleRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SampleRenderer.h; sourceTree = ""; }; + 53514CA92CFFA62700A480C0 /* SampleRenderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleRenderer.m; sourceTree = ""; }; + 53514CAB2CFFA99200A480C0 /* InAppDisplayBannerPluginRendererViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InAppDisplayBannerPluginRendererViewController.h; sourceTree = ""; }; + 53514CAC2CFFA99200A480C0 /* InAppDisplayBannerPluginRendererViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InAppDisplayBannerPluginRendererViewController.m; sourceTree = ""; }; + 53514CAE2CFFAB6700A480C0 /* SampleInterstitialController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SampleInterstitialController.h; sourceTree = ""; }; + 53514CAF2CFFAB6700A480C0 /* SampleInterstitialController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleInterstitialController.m; sourceTree = ""; }; + 53514CB12CFFAD5E00A480C0 /* UIApplication+TopViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIApplication+TopViewController.h"; sourceTree = ""; }; + 53514CB22CFFAD5E00A480C0 /* UIApplication+TopViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIApplication+TopViewController.m"; sourceTree = ""; }; + 53514CB72CFFAECB00A480C0 /* InAppDisplayInterstitialPluginRendererViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InAppDisplayInterstitialPluginRendererViewController.h; sourceTree = ""; }; + 53514CB82CFFAECB00A480C0 /* InAppDisplayInterstitialPluginRendererViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InAppDisplayInterstitialPluginRendererViewController.m; sourceTree = ""; }; 5354C0C62923E99800DB25EA /* TestUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = TestUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 539928BD292534BB0078053C /* IntegrationCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IntegrationCase.h; sourceTree = ""; }; 539928BE292534EA0078053C /* IntegrationCase.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IntegrationCase.m; sourceTree = ""; }; @@ -549,8 +577,11 @@ 53072B4E2920DF1000228462 /* In-App */ = { isa = PBXGroup; children = ( + 53514C972CFF981100A480C0 /* SampleRenderers */, 53072B7629226CFE00228462 /* InAppDisplayBannerViewController.swift */, + 53514C952CFF97DB00A480C0 /* InAppDisplayBannerPluginRendererViewController.swift */, 53072B7A292270EE00228462 /* InAppDisplayInterstitialViewController.swift */, + 53514CA22CFF9DD000A480C0 /* InAppDisplayInterstitialPluginRendererViewController.swift */, 5344E69E2922796000A1F582 /* InAppNativeViewController.swift */, 53072B7829226EF400228462 /* InAppVideoBannerViewController.swift */, 53072B802922753D00228462 /* InAppVideoInterstitialLandscapeViewController.swift */, @@ -734,6 +765,29 @@ path = Helpers; sourceTree = ""; }; + 53514C972CFF981100A480C0 /* SampleRenderers */ = { + isa = PBXGroup; + children = ( + 53514C992CFF988E00A480C0 /* SampleRenderer.swift */, + 53514C982CFF988E00A480C0 /* SampleAdView.swift */, + 53514C9B2CFF988E00A480C0 /* SampleInterstitialController.swift */, + ); + path = SampleRenderers; + sourceTree = ""; + }; + 53514CA42CFFA4F000A480C0 /* SampleRenderers */ = { + isa = PBXGroup; + children = ( + 53514CA82CFFA62700A480C0 /* SampleRenderer.h */, + 53514CA92CFFA62700A480C0 /* SampleRenderer.m */, + 53514CA52CFFA52A00A480C0 /* SampleAdView.h */, + 53514CA62CFFA52A00A480C0 /* SampleAdView.m */, + 53514CAE2CFFAB6700A480C0 /* SampleInterstitialController.h */, + 53514CAF2CFFAB6700A480C0 /* SampleInterstitialController.m */, + ); + path = SampleRenderers; + sourceTree = ""; + }; 539928BC292534A70078053C /* IntegrationCase */ = { isa = PBXGroup; children = ( @@ -871,12 +925,17 @@ 539928D629254EEC0078053C /* InApp */ = { isa = PBXGroup; children = ( + 53514CA42CFFA4F000A480C0 /* SampleRenderers */, 53F35F262926A3E1001C1183 /* InAppDisplayBannerViewController.h */, 53F35F272926A3E1001C1183 /* InAppDisplayBannerViewController.m */, + 53514CAB2CFFA99200A480C0 /* InAppDisplayBannerPluginRendererViewController.h */, + 53514CAC2CFFA99200A480C0 /* InAppDisplayBannerPluginRendererViewController.m */, 53F35F292926A58B001C1183 /* InAppVideoBannerViewController.h */, 53F35F2A2926A58B001C1183 /* InAppVideoBannerViewController.m */, 53F35F2C2926A6D6001C1183 /* InAppDisplayInterstitialViewController.h */, 53F35F2D2926A6D6001C1183 /* InAppDisplayInterstitialViewController.m */, + 53514CB72CFFAECB00A480C0 /* InAppDisplayInterstitialPluginRendererViewController.h */, + 53514CB82CFFAECB00A480C0 /* InAppDisplayInterstitialPluginRendererViewController.m */, 53F35F2F2926B532001C1183 /* InAppVideoInterstitialViewController.h */, 53F35F302926B532001C1183 /* InAppVideoInterstitialViewController.m */, 53F35F322926BAFE001C1183 /* InAppVideoInterstitialVerticalViewController.h */, @@ -1064,6 +1123,8 @@ isa = PBXGroup; children = ( 53F35F192926908B001C1183 /* PrebidDemoMacros.h */, + 53514CB12CFFAD5E00A480C0 /* UIApplication+TopViewController.h */, + 53514CB22CFFAD5E00A480C0 /* UIApplication+TopViewController.m */, ); path = Utils; sourceTree = ""; @@ -1507,9 +1568,13 @@ 53B03D8C291A6562000117E1 /* ExamplesViewController.swift in Sources */, 5344E6C32922AAE500A1F582 /* MAXVideoRewardedViewController.swift in Sources */, 53072B6929217D1800228462 /* GAMOriginalAPIVideoInstreamViewController.swift in Sources */, + 53514CA32CFF9DD000A480C0 /* InAppDisplayInterstitialPluginRendererViewController.swift in Sources */, 5344E6A92922815000A1F582 /* GAMVideoRewardedViewController.swift in Sources */, 534C61572CB7F4780026119A /* AdMobDisplayRewardedViewController.swift in Sources */, 53072B7B292270EE00228462 /* InAppDisplayInterstitialViewController.swift in Sources */, + 53514C9E2CFF988E00A480C0 /* SampleAdView.swift in Sources */, + 53514C9F2CFF988E00A480C0 /* SampleInterstitialController.swift in Sources */, + 53514CA02CFF988E00A480C0 /* SampleRenderer.swift in Sources */, 53072B5E2921522000228462 /* GAMOriginalAPIVideoRewardedViewController.swift in Sources */, 530D4AA6291A4BC600A2C796 /* AppDelegate.swift in Sources */, 53072B6C29217E8600228462 /* InstreamBaseViewController.swift in Sources */, @@ -1517,6 +1582,7 @@ 53072B812922753D00228462 /* InAppVideoInterstitialLandscapeViewController.swift in Sources */, 5344E6C12922A97200A1F582 /* MAXVideoInterstitialViewController.swift in Sources */, 53072B7D2922733800228462 /* InAppVideoInterstitialViewController.swift in Sources */, + 53514C962CFF97DB00A480C0 /* InAppDisplayBannerPluginRendererViewController.swift in Sources */, 534C614F2CB7F20A0026119A /* GAMDisplayRewardedViewController.swift in Sources */, 5344E6C52922AC2C00A1F582 /* MAXNativeViewController.swift in Sources */, 53B03DA5291BCCE1000117E1 /* GAMOriginalAPIDisplayBannerViewController.swift in Sources */, @@ -1577,11 +1643,13 @@ 539F960929DEEDE40061E7A5 /* GAMOriginalAPIMultiformatInterstitialViewController.m in Sources */, 53F35F3A2926BDB7001C1183 /* InAppVideoRewardedViewController.m in Sources */, 53A369012AB8CDEB00A03B3E /* GAMOriginalAPIMultiformatInAppNativeViewController.m in Sources */, + 53514CB02CFFAB6700A480C0 /* SampleInterstitialController.m in Sources */, 539928DB29254F2A0078053C /* BannerBaseViewController.m in Sources */, 53F35F57292792D6001C1183 /* SettingsViewController.m in Sources */, 534C61642CB851C10026119A /* MAXDisplayRewardedViewController.m in Sources */, 539928E22925505B0078053C /* InterstitialBaseViewController.m in Sources */, 53F35F432926C84E001C1183 /* MAXVideoBannerViewController.m in Sources */, + 53514CB92CFFAECB00A480C0 /* InAppDisplayInterstitialPluginRendererViewController.m in Sources */, 5399290A292665D80078053C /* GAMOriginalAPINativeViewController.m in Sources */, 539928F5292566AD0078053C /* GAMOriginalAPIDisplayBannerViewController.m in Sources */, 539F960629DEEA0A0061E7A5 /* GAMOriginalAPIMultiformatBannerViewController.m in Sources */, @@ -1591,11 +1659,14 @@ 53F35F492926CFE3001C1183 /* MAXVideoInterstitialViewController.m in Sources */, 53F35F03292671E7001C1183 /* GAMDisplayBannerViewController.m in Sources */, 533784CC2AC2CFF5009A8650 /* GAMOriginalAPIMultiformatNativeStylesViewController.m in Sources */, + 53514CAD2CFFA99200A480C0 /* InAppDisplayBannerPluginRendererViewController.m in Sources */, 539929012926488B0078053C /* GAMOriginalAPIVideoRewardedViewController.m in Sources */, + 53514CA72CFFA52A00A480C0 /* SampleAdView.m in Sources */, 53BA02C5292531D100BE6015 /* main.m in Sources */, 53F35F1C292692DC001C1183 /* AdMobDisplayInterstitialViewController.m in Sources */, 53BA02B7292531D000BE6015 /* SceneDelegate.m in Sources */, 53F35F312926B532001C1183 /* InAppVideoInterstitialViewController.m in Sources */, + 53514CB32CFFAD5E00A480C0 /* UIApplication+TopViewController.m in Sources */, 53F35F1829268C62001C1183 /* AdMobVideoBannerViewController.m in Sources */, 53F35F0929267A30001C1183 /* GAMDisplayInterstitialViewController.m in Sources */, 53F35F152926845C001C1183 /* AdMobDisplayBannerViewController.m in Sources */, @@ -1608,6 +1679,7 @@ 53A368FD2AB8CBB300A03B3E /* MultiformatBaseViewController.m in Sources */, 53F35F2B2926A58B001C1183 /* InAppVideoBannerViewController.m in Sources */, 53F35F4C2926D130001C1183 /* MAXVideoRewardedViewController.m in Sources */, + 53514CAA2CFFA62700A480C0 /* SampleRenderer.m in Sources */, 53F35F1F29269668001C1183 /* AdMobVideoInterstitialViewController.m in Sources */, 539928C92925379C0078053C /* IntegrationKindDescriptor.m in Sources */, 53F35F0C29267C65001C1183 /* GAMVideoInterstitialViewController.m in Sources */, diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayBannerPluginRendererViewController.h b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayBannerPluginRendererViewController.h new file mode 100644 index 000000000..61457c5a1 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayBannerPluginRendererViewController.h @@ -0,0 +1,26 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "BannerBaseViewController.h" + +@import PrebidMobile; + +NS_ASSUME_NONNULL_BEGIN + +@interface InAppDisplayBannerPluginRendererViewController : BannerBaseViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayBannerPluginRendererViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayBannerPluginRendererViewController.m new file mode 100644 index 000000000..91dd23ad0 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayBannerPluginRendererViewController.m @@ -0,0 +1,77 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "InAppDisplayBannerPluginRendererViewController.h" +#import "PrebidDemoMacros.h" +#import "SampleRenderer.h" + +NSString * const storedImpDisplayBannerPluginRendererInApp = @"prebid-demo-display-banner-320-50-custom-ad-view-renderer"; + +@interface InAppDisplayBannerPluginRendererViewController () + +// Prebid +@property (nonatomic) BannerView * prebidBannerView; +@property (nonatomic, strong) SampleRenderer * samplePluginRenderer; + +@end + +@implementation InAppDisplayBannerPluginRendererViewController + +- (void)loadView { + [super loadView]; + + [self createAd]; +} + +- (void)dealloc { + // Unregister plugin when you no longer needed + [Prebid unregisterPluginRenderer:self.samplePluginRenderer]; +} + +- (void)createAd { + // 1. Create a plugin renderer + self.samplePluginRenderer = [SampleRenderer new]; + + // 2. Register the plugin renderer + [Prebid registerPluginRenderer:self.samplePluginRenderer]; + + // 3. Create a BannerView + self.prebidBannerView = [[BannerView alloc] initWithFrame:CGRectMake(0, 0, self.adSize.width, self.adSize.height) + configID:storedImpDisplayBannerPluginRendererInApp + adSize:self.adSize]; + + // 4. Configure the BannerView + self.prebidBannerView.delegate = self; + self.prebidBannerView.adFormat = AdFormat.banner; + self.prebidBannerView.videoParameters.placement = PBPlacement.InBanner; + + // Add Prebid banner view to the app UI + [self.bannerView addSubview:self.prebidBannerView]; + + // 5. Load the banner ad + [self.prebidBannerView loadAd]; +} + +// MARK: - BannerViewDelegate + +- (UIViewController *)bannerViewPresentationController { + return self; +} + +- (void)bannerView:(BannerView *)bannerView didFailToReceiveAdWith:(NSError *)error { + PBMLogError(@"%@", error.localizedDescription); +} + +@end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayInterstitialPluginRendererViewController.h b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayInterstitialPluginRendererViewController.h new file mode 100644 index 000000000..29fd8d29f --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayInterstitialPluginRendererViewController.h @@ -0,0 +1,26 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "InterstitialBaseViewController.h" + +@import PrebidMobile; + +NS_ASSUME_NONNULL_BEGIN + +@interface InAppDisplayInterstitialPluginRendererViewController : InterstitialBaseViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayInterstitialPluginRendererViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayInterstitialPluginRendererViewController.m new file mode 100644 index 000000000..98de46a99 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/InAppDisplayInterstitialPluginRendererViewController.m @@ -0,0 +1,71 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "InAppDisplayInterstitialPluginRendererViewController.h" +#import "PrebidDemoMacros.h" +#import "SampleRenderer.h" + +NSString * const storedImpDisplayInterstitialPluginRendererInApp = @"prebid-demo-display-interstitial-320-480-custom-interstitial-renderer"; + +@interface InAppDisplayInterstitialPluginRendererViewController () + +// Prebid +@property (nonatomic) InterstitialRenderingAdUnit * renderingInterstitial; +@property (nonatomic, strong) SampleRenderer * samplePluginRenderer; + +@end + +@implementation InAppDisplayInterstitialPluginRendererViewController + +- (void)loadView { + [super loadView]; + + [self createAd]; +} + +- (void)dealloc { + // Unregister plugin when you no longer needed + [Prebid unregisterPluginRenderer:self.samplePluginRenderer]; +} + +- (void)createAd { + // 1. Create a plugin renderer + self.samplePluginRenderer = [SampleRenderer new]; + + // 2. Register the plugin renderer + [Prebid registerPluginRenderer:self.samplePluginRenderer]; + + // 3. Create a InterstitialRenderingAdUnit + self.renderingInterstitial = [[InterstitialRenderingAdUnit alloc] initWithConfigID:storedImpDisplayInterstitialPluginRendererInApp]; + + // 4. Configure the InterstitialRenderingAdUnit + self.renderingInterstitial.adFormats = [[NSSet alloc] initWithObjects:AdFormat.banner, nil]; + self.renderingInterstitial.delegate = self; + + // 5. Load the interstitial ad + [self.renderingInterstitial loadAd]; +} + +// MARK: - InterstitialAdUnitDelegate + +- (void)interstitialDidReceiveAd:(InterstitialRenderingAdUnit *)interstitial { + [self.renderingInterstitial showFrom:self]; +} + +- (void)interstitial:(InterstitialRenderingAdUnit *)interstitial didFailToReceiveAdWithError:(NSError *)error { + PBMLogError(@"%@", error.localizedDescription); +} + +@end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleAdView.h b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleAdView.h new file mode 100644 index 000000000..462dbf429 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleAdView.h @@ -0,0 +1,35 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +#import +#import + +@import PrebidMobile; + +NS_ASSUME_NONNULL_BEGIN + +/// An example showcasing the implementation of the `PrebidMobileDisplayViewProtocol`. +/// A sample view that is used for rendering ads. +@interface SampleAdView : UIView + +@property (nonatomic, weak, nullable) id interactionDelegate; +@property (nonatomic, weak, nullable) id loadingDelegate; +@property (nonatomic, strong) Bid *bid; + +- (void)loadAd; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleAdView.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleAdView.m new file mode 100644 index 000000000..b355d5b6c --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleAdView.m @@ -0,0 +1,89 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +#import "SampleAdView.h" + +@interface SampleAdView () + +@property (nonatomic, strong) WKWebView *webView; +@property (nonatomic, strong) UILabel *customRendererLabel; + +@end + +@implementation SampleAdView + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + [self setupView]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + self = [super initWithCoder:coder]; + if (self) { + [self setupView]; + } + return self; +} + +- (void)setupView { + self.webView = [[WKWebView alloc] init]; + self.webView.translatesAutoresizingMaskIntoConstraints = NO; + [self addSubview:self.webView]; + + self.customRendererLabel = [[UILabel alloc] init]; + self.customRendererLabel.translatesAutoresizingMaskIntoConstraints = NO; + self.customRendererLabel.text = @"Custom Renderer"; + self.customRendererLabel.textColor = [UIColor whiteColor]; + self.customRendererLabel.font = [UIFont boldSystemFontOfSize:10]; + self.customRendererLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; + self.customRendererLabel.textAlignment = NSTextAlignmentCenter; + self.customRendererLabel.layer.cornerRadius = 5; + self.customRendererLabel.layer.masksToBounds = YES; + self.customRendererLabel.numberOfLines = 0; + [self addSubview:self.customRendererLabel]; + + [NSLayoutConstraint activateConstraints:@[ + [self.webView.topAnchor constraintEqualToAnchor:self.topAnchor], + [self.webView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor], + [self.webView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor], + [self.webView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor], + + [self.customRendererLabel.topAnchor constraintEqualToAnchor:self.topAnchor constant:5], + [self.customRendererLabel.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:10], + [self.customRendererLabel.heightAnchor constraintEqualToConstant:40], + [self.customRendererLabel.widthAnchor constraintEqualToConstant:50] + ]]; +} + +- (void)loadAd { + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.bid.adm) { + [self.webView loadHTMLString:self.bid.adm baseURL:nil]; + [self.loadingDelegate displayViewDidLoadAd:self]; + } else { + NSString * errorMessage = @"Renderer did fail - there is no ADM in the response."; + NSError *error = [NSError errorWithDomain:@"SampleAdViewErrorDomain" + code:101 + userInfo:@{NSLocalizedDescriptionKey: errorMessage}]; + + [self.loadingDelegate displayView:self didFailWithError:error]; + } + }); +} + +@end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleInterstitialController.h b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleInterstitialController.h new file mode 100644 index 000000000..48890d3d1 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleInterstitialController.h @@ -0,0 +1,33 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +#import +#import + +@import PrebidMobile; + +NS_ASSUME_NONNULL_BEGIN + +/// An example showcasing the implementation of the `PrebidMobileInterstitialControllerProtocol`. +/// A sample controller that is used for rendering ads. +@interface SampleInterstitialController : NSObject + +@property (nonatomic, weak) id loadingDelegate; +@property (nonatomic, weak) id interactionDelegate; +@property (nonatomic, strong) Bid *bid; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleInterstitialController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleInterstitialController.m new file mode 100644 index 000000000..cdb3894c9 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleInterstitialController.m @@ -0,0 +1,89 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +#import "SampleInterstitialController.h" +#import "UIApplication+TopViewController.h" + +@interface SampleInterstitialController () + +@property (nonatomic, strong) UILabel *customRendererLabel; +@property (nonatomic, strong) WKWebView *webView; +@property (nonatomic, strong) UIViewController *interstitialViewController; + +@end + +@implementation SampleInterstitialController + +- (instancetype)init { + self = [super init]; + if (self) { + _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 300, 250)]; + _webView.translatesAutoresizingMaskIntoConstraints = NO; + _webView.backgroundColor = [UIColor blueColor]; + + _customRendererLabel = [[UILabel alloc] init]; + _customRendererLabel.text = @"Custom Renderer"; + _customRendererLabel.textAlignment = NSTextAlignmentCenter; + _customRendererLabel.font = [UIFont boldSystemFontOfSize:18]; + _customRendererLabel.translatesAutoresizingMaskIntoConstraints = NO; + + _interstitialViewController = [[UIViewController alloc] init]; + _interstitialViewController.view.backgroundColor = [UIColor whiteColor]; + [_interstitialViewController.view addSubview:self.customRendererLabel]; + [_interstitialViewController.view addSubview:self.webView]; + + [NSLayoutConstraint activateConstraints:@[ + [self.customRendererLabel.topAnchor constraintEqualToAnchor:self.interstitialViewController.view.safeAreaLayoutGuide.topAnchor constant:20], + [self.customRendererLabel.centerXAnchor constraintEqualToAnchor:self.interstitialViewController.view.centerXAnchor], + [self.webView.centerXAnchor constraintEqualToAnchor:self.interstitialViewController.view.centerXAnchor], + [self.webView.centerYAnchor constraintEqualToAnchor:self.interstitialViewController.view.centerYAnchor], + [self.webView.widthAnchor constraintEqualToConstant:300], + [self.webView.heightAnchor constraintEqualToConstant:250] + ]]; + } + return self; +} + +- (void)loadAd { + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.bid.adm) { + [self.webView loadHTMLString:self.bid.adm baseURL:nil]; + [self.loadingDelegate interstitialControllerDidLoadAd:self]; + } else { + NSString * errorMessage = @"Renderer did fail - there is no ADM in the response."; + NSError *error = [NSError errorWithDomain:@"SampleInterstitialControllerErrorDomain" + code:102 + userInfo:@{NSLocalizedDescriptionKey: errorMessage}]; + [self.loadingDelegate interstitialController:self didFailWithError:error]; + } + }); +} + +- (void)show { + dispatch_async(dispatch_get_main_queue(), ^{ + UIViewController *presentingController = [UIApplication.sharedApplication topViewController]; + if (presentingController) { + [presentingController presentViewController:self.interstitialViewController animated:YES completion:nil]; + } else { + NSString * errorMessage = @"Couldn't find a controller to present from."; + NSError *error = [NSError errorWithDomain:@"SampleInterstitialControllerErrorDomain" + code:103 + userInfo:@{NSLocalizedDescriptionKey: errorMessage}]; + [self.loadingDelegate interstitialController:self didFailWithError:error]; + } + }); +} + +@end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleRenderer.h b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleRenderer.h new file mode 100644 index 000000000..754d39b99 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleRenderer.h @@ -0,0 +1,31 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +#import +#import "SampleAdView.h" +#import "SampleInterstitialController.h" + +@import PrebidMobile; + +NS_ASSUME_NONNULL_BEGIN + +/// A custom implementation of the renderer. +@interface SampleRenderer : NSObject + +- (instancetype)init; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleRenderer.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleRenderer.m new file mode 100644 index 000000000..d612efaab --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Examples/InApp/SampleRenderers/SampleRenderer.m @@ -0,0 +1,60 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +#import "SampleRenderer.h" + +@implementation SampleRenderer + +@synthesize name; +@synthesize version; +@synthesize data; + +- (instancetype)init { + self = [super init]; + if (self) { + name = @"SampleRenderer"; + version = @"1.0.0"; + } + return self; +} + +- (UIView * _Nullable)createBannerViewWith:(CGRect)frame + bid:(Bid * _Nonnull)bid + adConfiguration:(AdUnitConfig * _Nonnull)adConfiguration + loadingDelegate:(id _Nonnull)loadingDelegate + interactionDelegate:(id _Nonnull)interactionDelegate { + SampleAdView *adView = [[SampleAdView alloc] initWithFrame:frame]; + + adView.interactionDelegate = interactionDelegate; + adView.loadingDelegate = loadingDelegate; + adView.bid = bid; + + return adView; +} + +- (id)createInterstitialControllerWithBid:(Bid *)bid + adConfiguration:(AdUnitConfig *)adConfiguration + loadingDelegate:(id)loadingDelegate + interactionDelegate:(id)interactionDelegate { + SampleInterstitialController *interstitialController = [[SampleInterstitialController alloc] init]; + + interstitialController.loadingDelegate = loadingDelegate; + interstitialController.interactionDelegate = interactionDelegate; + interstitialController.bid = bid; + + return interstitialController; +} + +@end diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/IntegrationCaseManager.m b/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/IntegrationCaseManager.m index b2f5959f5..c0893d4eb 100644 --- a/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/IntegrationCaseManager.m +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/IntegrationCase/IntegrationCaseManager.m @@ -30,8 +30,10 @@ #import "GAMOriginalAPIMultiformatNativeStylesViewController.h" #import "InAppDisplayBannerViewController.h" +#import "InAppDisplayBannerPluginRendererViewController.h" #import "InAppVideoBannerViewController.h" #import "InAppDisplayInterstitialViewController.h" +#import "InAppDisplayInterstitialPluginRendererViewController.h" #import "InAppVideoInterstitialViewController.h" #import "InAppVideoInterstitialVerticalViewController.h" #import "InAppVideoInterstitialLandscapeViewController.h" @@ -200,6 +202,16 @@ @implementation IntegrationCaseManager } ], + [ + [IntegrationCase alloc] + initWithTitle:@"In-App Display Banner Plugin Renderer 320x50" + integrationKind:IntegrationKindInApp + adFormat:AdFormatDisplayBanner + configurationClosure:^UIViewController *{ + return [[InAppDisplayBannerPluginRendererViewController alloc] init]; + } + ], + [ [IntegrationCase alloc] initWithTitle:@"In-App Video Banner 300x250" @@ -219,6 +231,16 @@ @implementation IntegrationCaseManager return [[InAppDisplayInterstitialViewController alloc] init]; } ], + + [ + [IntegrationCase alloc] + initWithTitle:@"In-App Display Interstitial Plugin Renderer 320x480" + integrationKind:IntegrationKindInApp + adFormat:AdFormatDisplayInterstitial + configurationClosure:^UIViewController *{ + return [[InAppDisplayInterstitialPluginRendererViewController alloc] init]; + } + ], [ [IntegrationCase alloc] diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Utils/UIApplication+TopViewController.h b/Example/PrebidDemo/PrebidDemoObjectiveC/Utils/UIApplication+TopViewController.h new file mode 100644 index 000000000..6c0cfd574 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Utils/UIApplication+TopViewController.h @@ -0,0 +1,26 @@ +/* Copyright 2019-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIApplication (TopViewController) + +- (UIViewController * _Nullable)topViewController; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Example/PrebidDemo/PrebidDemoObjectiveC/Utils/UIApplication+TopViewController.m b/Example/PrebidDemo/PrebidDemoObjectiveC/Utils/UIApplication+TopViewController.m new file mode 100644 index 000000000..ebe70a044 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoObjectiveC/Utils/UIApplication+TopViewController.m @@ -0,0 +1,66 @@ +/* Copyright 2019-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "UIApplication+TopViewController.h" + +@implementation UIApplication (TopViewController) + +- (UIViewController * _Nullable)topViewController { + UIWindow *keyWindow = nil; + + for (UIScene *scene in self.connectedScenes) { + if ([scene isKindOfClass:[UIWindowScene class]]) { + UIWindowScene *windowScene = (UIWindowScene *)scene; + for (UIWindow *window in windowScene.windows) { + if (window.isKeyWindow) { + keyWindow = window; + break; + } + } + } + if (keyWindow) break; + } + + if (!keyWindow) { + return nil; + } + + UIViewController *rootViewController = keyWindow.rootViewController; + if (!rootViewController) { + return nil; + } + + return [self findTopMostViewControllerFrom:rootViewController]; +} + +- (UIViewController *)findTopMostViewControllerFrom:(UIViewController *)viewController { + if (viewController.presentedViewController) { + return [self findTopMostViewControllerFrom:viewController.presentedViewController]; + } + + if ([viewController isKindOfClass:[UINavigationController class]]) { + UINavigationController *navigationController = (UINavigationController *)viewController; + return [self findTopMostViewControllerFrom:navigationController.visibleViewController]; + } + + if ([viewController isKindOfClass:[UITabBarController class]]) { + UITabBarController *tabBarController = (UITabBarController *)viewController; + return [self findTopMostViewControllerFrom:tabBarController.selectedViewController]; + } + + return viewController; +} + +@end diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayBannerCustomRendererViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayBannerCustomRendererViewController.swift new file mode 100644 index 000000000..1020b526a --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayBannerCustomRendererViewController.swift @@ -0,0 +1,66 @@ +/* Copyright 2019-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import PrebidMobile + +fileprivate let storedImpDisplayBanner = "prebid-demo-display-banner-320-50-custom-ad-view-renderer" + +class InAppDisplayBannerPluginRendererViewController: BannerBaseViewController, BannerViewDelegate { + + // Prebid + private var prebidBannerView: BannerView! + + override func loadView() { + super.loadView() + + createAd() + } + + func createAd() { + // 1. Create a plugin renderer + let samplePluginRenderer = SampleAdViewRenderer() + + // 2. Register the plugin renderer + Prebid.registerPluginRenderer(samplePluginRenderer) + + // 3. Create a BannerView + prebidBannerView = BannerView( + frame: CGRect(origin: .zero, size: adSize), + configID: storedImpDisplayBanner, adSize: adSize + ) + + // 4. Configure the BannerView + prebidBannerView.delegate = self + prebidBannerView.adFormat = .banner + prebidBannerView.videoParameters.placement = .InBanner + + // Add Prebid banner view to the app UI + bannerView.addSubview(prebidBannerView) + + // 5. Load the banner ad + prebidBannerView.loadAd() + } + + // MARK: - BannerViewDelegate + + func bannerViewPresentationController() -> UIViewController? { + self + } + + func bannerView(_ bannerView: BannerView, didFailToReceiveAdWith error: Error) { + PrebidDemoLogger.shared.error("Banner view did fail to receive ad with error: \(error)") + } +} diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayBannerPluginRendererViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayBannerPluginRendererViewController.swift new file mode 100644 index 000000000..7797e5a37 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayBannerPluginRendererViewController.swift @@ -0,0 +1,72 @@ +/* Copyright 2019-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import PrebidMobile + +fileprivate let storedImpDisplayBanner = "prebid-demo-display-banner-320-50-custom-ad-view-renderer" + +class InAppDisplayBannerPluginRendererViewController: + BannerBaseViewController, + BannerViewDelegate { + + // Prebid + private var prebidBannerView: BannerView! + private let samplePluginRenderer = SampleRenderer() + + override func loadView() { + super.loadView() + + createAd() + } + + deinit { + // Unregister plugin when you no longer needed + Prebid.unregisterPluginRenderer(samplePluginRenderer) + } + + func createAd() { + // 1. Register the plugin renderer + Prebid.registerPluginRenderer(samplePluginRenderer) + + // 2. Create a BannerView + prebidBannerView = BannerView( + frame: CGRect(origin: .zero, size: adSize), + configID: storedImpDisplayBanner, + adSize: adSize + ) + + // 3. Configure the BannerView + prebidBannerView.delegate = self + prebidBannerView.adFormat = .banner + prebidBannerView.videoParameters.placement = .InBanner + + // Add Prebid banner view to the app UI + bannerView.addSubview(prebidBannerView) + + // 4. Load the banner ad + prebidBannerView.loadAd() + } + + // MARK: - BannerViewDelegate + + func bannerViewPresentationController() -> UIViewController? { + self + } + + func bannerView(_ bannerView: BannerView, didFailToReceiveAdWith error: Error) { + PrebidDemoLogger.shared.error("Banner view did fail to receive ad with error: \(error)") + } +} diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayInterstitialPluginRendererViewController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayInterstitialPluginRendererViewController.swift new file mode 100644 index 000000000..c4718dc40 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/InAppDisplayInterstitialPluginRendererViewController.swift @@ -0,0 +1,64 @@ +/* Copyright 2019-2022 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import PrebidMobile + +fileprivate let storedImpDisplayInterstitial = "prebid-demo-display-interstitial-320-480-custom-interstitial-renderer" + +class InAppDisplayInterstitialPluginRendererViewController: + UIViewController, + InterstitialAdUnitDelegate { + + // Prebid + private var renderingInterstitial: InterstitialRenderingAdUnit! + private let samplePluginRenderer = SampleRenderer() + + override func loadView() { + super.loadView() + + createAd() + } + + deinit { + // Unregister plugin when you no longer needed + Prebid.unregisterPluginRenderer(samplePluginRenderer) + } + + func createAd() { + // 1. Register the plugin renderer + Prebid.registerPluginRenderer(samplePluginRenderer) + + // 2. Create a InterstitialRenderingAdUnit + renderingInterstitial = InterstitialRenderingAdUnit(configID: storedImpDisplayInterstitial) + + // 3. Configure the InterstitialRenderingAdUnit + renderingInterstitial.adFormats = [.banner] + renderingInterstitial.delegate = self + + // 4. Load the interstitial ad + renderingInterstitial.loadAd() + } + + // MARK: - InterstitialAdUnitDelegate + + func interstitialDidReceiveAd(_ interstitial: InterstitialRenderingAdUnit) { + interstitial.show(from: self) + } + + func interstitial(_ interstitial: InterstitialRenderingAdUnit, didFailToReceiveAdWithError error: Error?) { + PrebidDemoLogger.shared.error("Interstitial Rendering ad unit did fail to receive ad with error: \(error)") + } +} diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleAdView.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleAdView.swift new file mode 100644 index 000000000..9e5ef79a5 --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleAdView.swift @@ -0,0 +1,97 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import UIKit +import WebKit +import PrebidMobile + +/// An example showcasing the implementation of the `PrebidMobileDisplayViewProtocol`. +/// A sample view that is used for rendering ads. +class SampleAdView: UIView, PrebidMobileDisplayViewProtocol { + + enum SampleError: LocalizedError { + case noAdm + + var errorDescription: String? { + switch self { + case .noAdm: + return "Renderer did fail - there is no ADM in the response." + } + } + } + + weak var interactionDelegate: DisplayViewInteractionDelegate? + weak var loadingDelegate: DisplayViewLoadingDelegate? + + var bid: Bid? + + private let webView: WKWebView = { + let webView = WKWebView() + webView.translatesAutoresizingMaskIntoConstraints = false + return webView + }() + + private let customRendererLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.text = "Custom Renderer" + label.textColor = .white + label.font = UIFont.boldSystemFont(ofSize: 10) + label.backgroundColor = UIColor.black.withAlphaComponent(0.5) + label.textAlignment = .center + label.layer.cornerRadius = 5 + label.layer.masksToBounds = true + label.numberOfLines = 0 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + setupView() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupView() + } + + func loadAd() { + DispatchQueue.main.async { + if let adm = self.bid?.adm { + self.webView.loadHTMLString(adm, baseURL: nil) + self.loadingDelegate?.displayViewDidLoadAd(self) + } else { + self.loadingDelegate?.displayView(self, didFailWithError: SampleError.noAdm) + } + } + } + + private func setupView() { + addSubview(webView) + addSubview(customRendererLabel) + + NSLayoutConstraint.activate([ + webView.topAnchor.constraint(equalTo: topAnchor), + webView.leadingAnchor.constraint(equalTo: leadingAnchor), + webView.trailingAnchor.constraint(equalTo: trailingAnchor), + webView.bottomAnchor.constraint(equalTo: bottomAnchor), + + customRendererLabel.topAnchor.constraint(equalTo: topAnchor, constant: 5), + customRendererLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + customRendererLabel.heightAnchor.constraint(equalToConstant: 40), + customRendererLabel.widthAnchor.constraint(equalToConstant: 50) + ]) + } +} diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleInterstitialController.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleInterstitialController.swift new file mode 100644 index 000000000..f591900ab --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleInterstitialController.swift @@ -0,0 +1,108 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import UIKit +import WebKit +import PrebidMobile + +/// An example showcasing the implementation of the `PrebidMobileInterstitialControllerProtocol`. +/// A sample controller that is used for rendering ads. +class SampleInterstitialController: + NSObject, + PrebidMobileInterstitialControllerProtocol { + + enum SampleError: LocalizedError { + case noAdm + case noAvailableController + + var errorDescription: String? { + switch self { + case .noAdm: + return "Renderer did fail - there is no ADM in the response." + case .noAvailableController: + return "Coudn't find a controller to present from." + } + } + } + + weak var loadingDelegate: InterstitialControllerLoadingDelegate? + weak var interactionDelegate: InterstitialControllerInteractionDelegate? + + var bid: Bid? + + private let webView: WKWebView = { + let webView = WKWebView(frame: CGRect(origin: .zero, size: CGSize(width: 300, height: 250))) + webView.translatesAutoresizingMaskIntoConstraints = false + webView.backgroundColor = .blue + return webView + }() + + private let customRendererLabel: UILabel = { + let label = UILabel() + label.text = "Custom Renderer" + label.textAlignment = .center + label.font = UIFont.boldSystemFont(ofSize: 18) + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private lazy var interstitialViewController: UIViewController = { + let controller = UIViewController() + + controller.view.backgroundColor = .white + controller.view.addSubview(customRendererLabel) + controller.view.addSubview(webView) + + NSLayoutConstraint.activate([ + customRendererLabel.topAnchor.constraint(equalTo: controller.view.safeAreaLayoutGuide.topAnchor, constant: 20), + customRendererLabel.centerXAnchor.constraint(equalTo: controller.view.centerXAnchor), + webView.centerXAnchor.constraint(equalTo: controller.view.centerXAnchor), + webView.centerYAnchor.constraint(equalTo: controller.view.centerYAnchor), + webView.widthAnchor.constraint(equalToConstant: 300), + webView.heightAnchor.constraint(equalToConstant: 250), + ]) + + return controller + }() + + func loadAd() { + DispatchQueue.main.async { + guard let adm = self.bid?.adm else { + self.loadingDelegate?.interstitialController(self, didFailWithError: SampleError.noAdm) + return + } + + self.webView.loadHTMLString(adm, baseURL: nil) + self.loadingDelegate?.interstitialControllerDidLoadAd(self) + } + } + + func show() { + DispatchQueue.main.async { + guard let presentingController = UIApplication.shared.topViewController else { + self.loadingDelegate?.interstitialController( + self, + didFailWithError: SampleError.noAvailableController + ) + return + } + + presentingController.present( + self.interstitialViewController, + animated: true + ) + } + } +} diff --git a/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleRenderer.swift b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleRenderer.swift new file mode 100644 index 000000000..9c1d5a41a --- /dev/null +++ b/Example/PrebidDemo/PrebidDemoSwift/Examples/In-App/SampleRenderers/SampleRenderer.swift @@ -0,0 +1,59 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import PrebidMobile + +/// A custom implementation of the renderer. +public class SampleRenderer: NSObject, PrebidMobilePluginRenderer { + + public let name = "SampleRenderer" + public let version = "1.0.0" + public var data: [String: Any]? + + /// This method creates an instance of `SampleAdView`, which is a custom view used to display the ad. + public func createBannerView( + with frame: CGRect, + bid: Bid, + adConfiguration: AdUnitConfig, + loadingDelegate: DisplayViewLoadingDelegate, + interactionDelegate: DisplayViewInteractionDelegate + ) -> (UIView & PrebidMobileDisplayViewProtocol)? { + let adView = SampleAdView(frame: frame) + + adView.interactionDelegate = interactionDelegate + adView.loadingDelegate = loadingDelegate + adView.bid = bid + + return adView + } + + /// This method creates an instance of `SampleInterstitialController`, + /// a custom controller used to display interstitial ads. + public func createInterstitialController( + bid: Bid, + adConfiguration: AdUnitConfig, + loadingDelegate: InterstitialControllerLoadingDelegate, + interactionDelegate: InterstitialControllerInteractionDelegate + ) -> PrebidMobileInterstitialControllerProtocol? { + let interstitialController = SampleInterstitialController() + + interstitialController.loadingDelegate = loadingDelegate + interstitialController.interactionDelegate = interactionDelegate + interstitialController.bid = bid + + return interstitialController + } +} diff --git a/Example/PrebidDemo/PrebidDemoSwift/Extensions/UIApplication+Extensions.swift b/Example/PrebidDemo/PrebidDemoSwift/Extensions/UIApplication+Extensions.swift index a4302a61c..a92d6ce19 100644 --- a/Example/PrebidDemo/PrebidDemoSwift/Extensions/UIApplication+Extensions.swift +++ b/Example/PrebidDemo/PrebidDemoSwift/Extensions/UIApplication+Extensions.swift @@ -16,16 +16,51 @@ import UIKit extension UIApplication { + func getKeyWindow() -> UIWindow? { - for scene in UIApplication.shared.connectedScenes { - if let windowScene = scene as? UIWindowScene { - for window in windowScene.windows { - if window.isKeyWindow { - return window - } + for scene in UIApplication.shared.connectedScenes { + if let windowScene = scene as? UIWindowScene { + for window in windowScene.windows { + if window.isKeyWindow { + return window } } } + } + return nil } + + var topViewController: UIViewController? { + guard let keyWindow = connectedScenes + .compactMap({ $0 as? UIWindowScene }) + .flatMap({ $0.windows }) + .first(where: { $0.isKeyWindow }) else { + return nil + } + + guard let rootViewController = keyWindow.rootViewController else { + return nil + } + + return findTopMostViewController(from: rootViewController) + } + + private func findTopMostViewController(from viewController: UIViewController) -> UIViewController { + if let presentedViewController = viewController.presentedViewController { + return findTopMostViewController(from: presentedViewController) + } + + if let navigationController = viewController as? UINavigationController, + let visibleViewController = navigationController.visibleViewController { + return findTopMostViewController(from: visibleViewController) + } + + if let tabBarController = viewController as? UITabBarController, + let selectedViewController = tabBarController.selectedViewController { + return findTopMostViewController(from: selectedViewController) + } + + return viewController + } } diff --git a/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/IntegrationCaseManager.swift b/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/IntegrationCaseManager.swift index 1635cec7c..db99edeb8 100644 --- a/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/IntegrationCaseManager.swift +++ b/Example/PrebidDemo/PrebidDemoSwift/IntegrationCase/IntegrationCaseManager.swift @@ -142,6 +142,15 @@ struct IntegrationCaseManager { } ), + IntegrationCase( + title: "In-App Display Banner Plugin Renderer 320x50", + integrationKind: .inApp, + adFormat: .displayBanner, + configurationClosure: { + InAppDisplayBannerPluginRendererViewController() + } + ), + IntegrationCase( title: "In-App Video Banner 300x250", integrationKind: .inApp, @@ -160,6 +169,15 @@ struct IntegrationCaseManager { } ), + IntegrationCase( + title: "In-App Display Interstitial Plugin Renderer 320x480", + integrationKind: .inApp, + adFormat: .displayInterstitial, + configurationClosure: { + InAppDisplayInterstitialPluginRendererViewController() + } + ), + IntegrationCase( title: "In-App Video Interstitial 320x480", integrationKind: .inApp, diff --git a/Example/PrebidDemo/PrebidDemoSwiftUITests/BannerAdsTest.swift b/Example/PrebidDemo/PrebidDemoSwiftUITests/BannerAdsTest.swift index f599fdf52..61e9930be 100644 --- a/Example/PrebidDemo/PrebidDemoSwiftUITests/BannerAdsTest.swift +++ b/Example/PrebidDemo/PrebidDemoSwiftUITests/BannerAdsTest.swift @@ -17,11 +17,23 @@ import XCTest class BannerAdsTest: BaseAdsTest { + private var labelText: String? + + override func tearDown() { + labelText = nil + } + public func testInAppBannerAd() { testAd(testCase: testCases.inAppDisplayBannerCase) } + public func testInAppBannerAdCustomRenderer() { + labelText = "Custom Renderer" + testAd(testCase: testCases.inAppDisplayBannerCustomRendererCase) + } + public func testGamOriginalBannerAd() { + labelText = "Test mode" testAd(testCase: testCases.gamOriginalDisplayBannerCase) } @@ -34,11 +46,16 @@ class BannerAdsTest: BaseAdsTest { } override func checkAd(testCase: String) { - XCTAssert(app.webViews.element.waitForExistence(timeout: 10), assertFailedMessage(testCase: testCase,reason: "Banner Web View is not displayed")) - if testCase == testCases.gamOriginalDisplayBannerCase { - XCTAssert(app.staticTexts["Test mode"].waitForExistence(timeout: 10)) + XCTAssert( + app.webViews.element.waitForExistence(timeout: 10), + assertFailedMessage(testCase: testCase,reason: "Banner Web View is not displayed") + ) + + if let labelText { + XCTAssert( + app.staticTexts[labelText].waitForExistence(timeout: 10), + assertFailedMessage(testCase: testCase, reason: "`\(labelText)` is not displayed") + ) } } - - } diff --git a/Example/PrebidDemo/PrebidDemoSwiftUITests/BaseAdsTest.swift b/Example/PrebidDemo/PrebidDemoSwiftUITests/BaseAdsTest.swift index 914a37d02..5c8eba5e9 100644 --- a/Example/PrebidDemo/PrebidDemoSwiftUITests/BaseAdsTest.swift +++ b/Example/PrebidDemo/PrebidDemoSwiftUITests/BaseAdsTest.swift @@ -42,5 +42,4 @@ class BaseAdsTest: XCTestCase { app.searchFields.element.typeText(testCase) app.tables.element(boundBy: 0).cells.element(boundBy: 0).tap() } - } diff --git a/Example/PrebidDemo/PrebidDemoSwiftUITests/InterstitialAdsTest.swift b/Example/PrebidDemo/PrebidDemoSwiftUITests/InterstitialAdsTest.swift index 344557124..5a3e8b062 100644 --- a/Example/PrebidDemo/PrebidDemoSwiftUITests/InterstitialAdsTest.swift +++ b/Example/PrebidDemo/PrebidDemoSwiftUITests/InterstitialAdsTest.swift @@ -17,25 +17,57 @@ import XCTest class InterstitialAdsTest: BaseAdsTest { + private var closeButton: String? + private var labelText: String? + + override func tearDown() { + closeButton = nil + labelText = nil + } + public func testInAppInterstitialAd() { + closeButton = "PBMCloseButton" testAd(testCase: testCases.inAppDisplayInterstitialCase) } + public func testInAppInterstitialAdCustomRenderer() { + labelText = "Custom Renderer" + testAd(testCase: testCases.inAppDisplayInterstitialCustomRendererCase) + } + public func testGamOriginalInterstitialAd() { + closeButton = "Close Advertisement" testAd(testCase: testCases.gamOriginalDisplayInterstitialCase) } public func testGamRenderingInterstitialAd() { + closeButton = "PBMCloseButton" testAd(testCase: testCases.gamDisplayInterstitialCase) } public func testAdMobInterstitialAd() { + closeButton = "PBMCloseButton" testAd(testCase: testCases.adMobDisplayInterstitialCase) } override func checkAd(testCase: String) { - XCTAssert(app.webViews.element.waitForExistence(timeout: 10),assertFailedMessage(testCase: testCase,reason: "Interstitial Web View is not displayed")) - let closeButton = testCase == testCases.gamOriginalDisplayInterstitialCase ? "Close Advertisement" : "PBMCloseButton" - XCTAssert(app.buttons[closeButton].waitForExistence(timeout: 10), assertFailedMessage(testCase: testCase,reason: "Close button is not displayed")) + XCTAssert( + app.webViews.element.waitForExistence(timeout: 10), + assertFailedMessage(testCase: testCase,reason: "Interstitial Web View is not displayed") + ) + + if let closeButton { + XCTAssert( + app.buttons[closeButton].waitForExistence(timeout: 10), + assertFailedMessage(testCase: testCase, reason: "Close button is not displayed") + ) + } + + if let labelText { + XCTAssert( + app.staticTexts[labelText].waitForExistence(timeout: 10), + assertFailedMessage(testCase: testCase, reason: "`\(labelText)` is not displayed") + ) + } } } diff --git a/Example/PrebidDemo/PrebidDemoSwiftUITests/Utils/TestCases.swift b/Example/PrebidDemo/PrebidDemoSwiftUITests/Utils/TestCases.swift index 7c13316bb..a6c20e659 100644 --- a/Example/PrebidDemo/PrebidDemoSwiftUITests/Utils/TestCases.swift +++ b/Example/PrebidDemo/PrebidDemoSwiftUITests/Utils/TestCases.swift @@ -28,9 +28,11 @@ struct TestCases { // In-App Rendering Api let inAppDisplayBannerCase = "In-App Display Banner 320x50" + let inAppDisplayBannerCustomRendererCase = "In-App Display Banner Plugin Renderer 320x50" let inAppNativeCase = "In-App Native" let inAppVideoBannerCase = "In-App Video Banner 300x250" let inAppDisplayInterstitialCase = "In-App Display Interstitial 320x480" + let inAppDisplayInterstitialCustomRendererCase = "In-App Display Interstitial Plugin Renderer 320x480" let inAppVideoInterstitialCase = "In-App Video Interstitial 320x480" let inAppVideoRewardedCase = "In-App Video Rewarded 320x480" diff --git a/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj b/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj index 9b7ee2550..bae98f082 100644 --- a/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj +++ b/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj @@ -25,12 +25,20 @@ 34E5698A237DB96900B47B01 /* UtilitiesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E56989237DB96900B47B01 /* UtilitiesViewController.swift */; }; 34FC0CF424ADD3A80045553E /* PrebidInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34FC0CF324ADD3A80045553E /* PrebidInterstitialController.swift */; }; 34FC0CF624ADD5640045553E /* PrebidGAMRewardedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34FC0CF524ADD5640045553E /* PrebidGAMRewardedController.swift */; }; - 3C28C9D82C35713B00D0A7DB /* CustomRendererInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C28C9D72C35713B00D0A7DB /* CustomRendererInterstitialController.swift */; }; - 3C28C9D92C35713B00D0A7DB /* CustomRendererInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C28C9D72C35713B00D0A7DB /* CustomRendererInterstitialController.swift */; }; - 3CC4A3E92C11F96800B97128 /* CustomRendererBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CC4A3E82C11F96800B97128 /* CustomRendererBannerController.swift */; }; - 3CC4A3EA2C11F96800B97128 /* CustomRendererBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CC4A3E82C11F96800B97128 /* CustomRendererBannerController.swift */; }; 457FD34BA45C3840CC31A8F5 /* Pods_InternalTestApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C11D97034E668BBFDD0DDCA /* Pods_InternalTestApp.framework */; }; 530E722C292FACB20025B44D /* UIImageView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 530E722B292FACB20025B44D /* UIImageView+Extensions.swift */; }; + 53514CE12D08416B00A480C0 /* CustomRendererInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CDF2D08416B00A480C0 /* CustomRendererInterstitialController.swift */; }; + 53514CE22D08416B00A480C0 /* SampleRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CD92D08416B00A480C0 /* SampleRenderer.swift */; }; + 53514CE32D08416B00A480C0 /* SampleModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CDC2D08416B00A480C0 /* SampleModalViewController.swift */; }; + 53514CE52D08416B00A480C0 /* SampleInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CDA2D08416B00A480C0 /* SampleInterstitialController.swift */; }; + 53514CE62D08416B00A480C0 /* SampleAdView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CD82D08416B00A480C0 /* SampleAdView.swift */; }; + 53514CE72D08416B00A480C0 /* CustomRendererBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CDE2D08416B00A480C0 /* CustomRendererBannerController.swift */; }; + 53514CE82D08416B00A480C0 /* CustomRendererInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CDF2D08416B00A480C0 /* CustomRendererInterstitialController.swift */; }; + 53514CE92D08416B00A480C0 /* SampleRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CD92D08416B00A480C0 /* SampleRenderer.swift */; }; + 53514CEA2D08416B00A480C0 /* SampleModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CDC2D08416B00A480C0 /* SampleModalViewController.swift */; }; + 53514CEC2D08416B00A480C0 /* SampleInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CDA2D08416B00A480C0 /* SampleInterstitialController.swift */; }; + 53514CED2D08416B00A480C0 /* SampleAdView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CD82D08416B00A480C0 /* SampleAdView.swift */; }; + 53514CEE2D08416B00A480C0 /* CustomRendererBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CDE2D08416B00A480C0 /* CustomRendererBannerController.swift */; }; 5397BD142936185400ABDA22 /* PrebidOriginalAPIDisplayBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5397BD132936185400ABDA22 /* PrebidOriginalAPIDisplayBannerController.swift */; }; 5397BD26293760F500ABDA22 /* PrebidOriginalAPIDisplayInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5397BD25293760F500ABDA22 /* PrebidOriginalAPIDisplayInterstitialController.swift */; }; 5397BD2A29376C9700ABDA22 /* PrebidOriginalAPIVideoRewardedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5397BD2929376C9700ABDA22 /* PrebidOriginalAPIVideoRewardedController.swift */; }; @@ -378,9 +386,13 @@ 34FC0CF524ADD5640045553E /* PrebidGAMRewardedController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidGAMRewardedController.swift; sourceTree = ""; }; 35F94D131F93F85D00CF46DB /* InternalTestAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InternalTestAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 35F94D171F93F85D00CF46DB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 3C28C9D72C35713B00D0A7DB /* CustomRendererInterstitialController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRendererInterstitialController.swift; sourceTree = ""; }; - 3CC4A3E82C11F96800B97128 /* CustomRendererBannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRendererBannerController.swift; sourceTree = ""; }; 530E722B292FACB20025B44D /* UIImageView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Extensions.swift"; sourceTree = ""; }; + 53514CD82D08416B00A480C0 /* SampleAdView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleAdView.swift; sourceTree = ""; }; + 53514CD92D08416B00A480C0 /* SampleRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleRenderer.swift; sourceTree = ""; }; + 53514CDA2D08416B00A480C0 /* SampleInterstitialController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleInterstitialController.swift; sourceTree = ""; }; + 53514CDC2D08416B00A480C0 /* SampleModalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleModalViewController.swift; sourceTree = ""; }; + 53514CDE2D08416B00A480C0 /* CustomRendererBannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRendererBannerController.swift; sourceTree = ""; }; + 53514CDF2D08416B00A480C0 /* CustomRendererInterstitialController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRendererInterstitialController.swift; sourceTree = ""; }; 5397BD132936185400ABDA22 /* PrebidOriginalAPIDisplayBannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidOriginalAPIDisplayBannerController.swift; sourceTree = ""; }; 5397BD25293760F500ABDA22 /* PrebidOriginalAPIDisplayInterstitialController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidOriginalAPIDisplayInterstitialController.swift; sourceTree = ""; }; 5397BD2929376C9700ABDA22 /* PrebidOriginalAPIVideoRewardedController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidOriginalAPIVideoRewardedController.swift; sourceTree = ""; }; @@ -641,7 +653,6 @@ isa = PBXGroup; children = ( 92E92500276B2986002B57F3 /* AdMob */, - 3CC4A3E72C11F93A00B97128 /* CustomRenderer */, 3493021A2473F2FB004A6086 /* GAM */, 9289873D28003BFE0062BFAA /* MAX */, 3461835425A6020500783A2C /* NativeAdViewBox.swift */, @@ -669,6 +680,7 @@ 3493022024740BD8004A6086 /* OpenX */ = { isa = PBXGroup; children = ( + 53514CE02D08416B00A480C0 /* CustomRenderer */, 3493022124740BEE004A6086 /* PrebidBannerController.swift */, 34FC0CF324ADD3A80045553E /* PrebidInterstitialController.swift */, 34BA95FF249CC118006AE372 /* PrebidRewardedController.swift */, @@ -698,11 +710,23 @@ path = PrebidMobileDemoRenderingUITests; sourceTree = ""; }; - 3CC4A3E72C11F93A00B97128 /* CustomRenderer */ = { + 53514CDD2D08416B00A480C0 /* Renderers */ = { isa = PBXGroup; children = ( - 3CC4A3E82C11F96800B97128 /* CustomRendererBannerController.swift */, - 3C28C9D72C35713B00D0A7DB /* CustomRendererInterstitialController.swift */, + 53514CD92D08416B00A480C0 /* SampleRenderer.swift */, + 53514CD82D08416B00A480C0 /* SampleAdView.swift */, + 53514CDA2D08416B00A480C0 /* SampleInterstitialController.swift */, + 53514CDC2D08416B00A480C0 /* SampleModalViewController.swift */, + ); + path = Renderers; + sourceTree = ""; + }; + 53514CE02D08416B00A480C0 /* CustomRenderer */ = { + isa = PBXGroup; + children = ( + 53514CDD2D08416B00A480C0 /* Renderers */, + 53514CDE2D08416B00A480C0 /* CustomRendererBannerController.swift */, + 53514CDF2D08416B00A480C0 /* CustomRendererInterstitialController.swift */, ); path = CustomRenderer; sourceTree = ""; @@ -1552,7 +1576,6 @@ 53A651852AC200AA000D42E4 /* PrebidOriginalAPIMultiformatNativeStylesController.swift in Sources */, 53ED2FD729798104007D13EE /* UtilitiesViewController.swift in Sources */, 53ED2FD829798104007D13EE /* AdMobNativeAdView.swift in Sources */, - 3CC4A3EA2C11F96800B97128 /* CustomRendererBannerController.swift in Sources */, 53ED2FD929798104007D13EE /* PrebidGAMInterstitialController.swift in Sources */, 53ED2FDA29798104007D13EE /* PrebidMAXRewardedController.swift in Sources */, 53ED2FDB29798104007D13EE /* PrebidMAXNativeController.swift in Sources */, @@ -1610,6 +1633,12 @@ 53ED300B29798104007D13EE /* PrebidMAXInterstitialController.swift in Sources */, 53ED300C29798104007D13EE /* DummyTableViewCell.swift in Sources */, 53ED300D29798104007D13EE /* AboutViewController.swift in Sources */, + 53514CE12D08416B00A480C0 /* CustomRendererInterstitialController.swift in Sources */, + 53514CE22D08416B00A480C0 /* SampleRenderer.swift in Sources */, + 53514CE32D08416B00A480C0 /* SampleModalViewController.swift in Sources */, + 53514CE52D08416B00A480C0 /* SampleInterstitialController.swift in Sources */, + 53514CE62D08416B00A480C0 /* SampleAdView.swift in Sources */, + 53514CE72D08416B00A480C0 /* CustomRendererBannerController.swift in Sources */, 53ED300E29798104007D13EE /* NativeAdViewBoxProtocol.swift in Sources */, 53ED300F29798104007D13EE /* FormViewController+RowBuildHelpers.swift in Sources */, 53ED301029798104007D13EE /* PrebidGAMBannerController.swift in Sources */, @@ -1617,7 +1646,6 @@ 53ED301229798104007D13EE /* ReactiveSdkInitFlag.swift in Sources */, 53ED301329798104007D13EE /* PrebidOriginalAPINativeBannerController.swift in Sources */, 53ED301429798104007D13EE /* TestCasesSectionsViewController.swift in Sources */, - 3C28C9D92C35713B00D0A7DB /* CustomRendererInterstitialController.swift in Sources */, 53ED301529798104007D13EE /* AppSettingsKeys.swift in Sources */, 53ED301729798104007D13EE /* NativeEventTracker+Extensions.swift in Sources */, 53ED301829798104007D13EE /* PrebidRewardedController.swift in Sources */, @@ -1648,7 +1676,6 @@ 53A651842AC200AA000D42E4 /* PrebidOriginalAPIMultiformatNativeStylesController.swift in Sources */, 34E5698A237DB96900B47B01 /* UtilitiesViewController.swift in Sources */, 92ABD58C279DA7E200AFDFF6 /* AdMobNativeAdView.swift in Sources */, - 3CC4A3E92C11F96800B97128 /* CustomRendererBannerController.swift in Sources */, 34BA95FE249CBF05006AE372 /* PrebidGAMInterstitialController.swift in Sources */, 92221CE52804BBB6005DF671 /* PrebidMAXRewardedController.swift in Sources */, 92986537280604E4007A2F34 /* PrebidMAXNativeController.swift in Sources */, @@ -1706,6 +1733,12 @@ 92221CE12804AF5B005DF671 /* PrebidMAXInterstitialController.swift in Sources */, 5B8EB5EF22676914003CC15C /* DummyTableViewCell.swift in Sources */, 5B3EEDCB2101D1AF00BAA0C4 /* AboutViewController.swift in Sources */, + 53514CE82D08416B00A480C0 /* CustomRendererInterstitialController.swift in Sources */, + 53514CE92D08416B00A480C0 /* SampleRenderer.swift in Sources */, + 53514CEA2D08416B00A480C0 /* SampleModalViewController.swift in Sources */, + 53514CEC2D08416B00A480C0 /* SampleInterstitialController.swift in Sources */, + 53514CED2D08416B00A480C0 /* SampleAdView.swift in Sources */, + 53514CEE2D08416B00A480C0 /* CustomRendererBannerController.swift in Sources */, 92F9A5D127A15BD1007B0B17 /* NativeAdViewBoxProtocol.swift in Sources */, 341CC3142562C9D000186F29 /* FormViewController+RowBuildHelpers.swift in Sources */, 3493021C2473F408004A6086 /* PrebidGAMBannerController.swift in Sources */, @@ -1713,7 +1746,6 @@ 346C6F5725DFAB7E00457DD3 /* ReactiveSdkInitFlag.swift in Sources */, 5397BD2C2937764100ABDA22 /* PrebidOriginalAPINativeBannerController.swift in Sources */, 5B3EEDE22101EDA200BAA0C4 /* TestCasesSectionsViewController.swift in Sources */, - 3C28C9D82C35713B00D0A7DB /* CustomRendererInterstitialController.swift in Sources */, ACC41ACD2444EADB00B9A3A7 /* AppSettingsKeys.swift in Sources */, 92C4E60D27A2F6D700738370 /* NativeEventTracker+Extensions.swift in Sources */, 34BA9600249CC118006AE372 /* PrebidRewardedController.swift in Sources */, diff --git a/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift b/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift index f05b106ae..781ad0c1a 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift @@ -583,7 +583,7 @@ struct TestCaseManager { setupCustomParams(for: bannerController.prebidConfigId) }), - TestCase(title: "Banner 320x50 (CustomRenderer)", + TestCase(title: "Banner 320x50 (In-App, Custom Renderer)", tags: [.banner, .inapp, .server], exampleVCStoryboardID: "AdapterViewController", configurationClosure: { vc in @@ -593,9 +593,7 @@ struct TestCaseManager { let bannerController = CustomRendererBannerController(rootController: adapterVC) bannerController.adSizes = [CGSize(width: 320, height: 50)] - - bannerController.prebidConfigId = "prebid-ita-banner-320-50-meta-custom-renderer"; - + bannerController.prebidConfigId = "prebid-demo-display-banner-320-50-custom-ad-view-renderer" adapterVC.setup(adapter: bannerController) setupCustomParams(for: bannerController.prebidConfigId) @@ -1030,6 +1028,23 @@ struct TestCaseManager { setupCustomParams(for: interstitialController.prebidConfigId) }), + TestCase(title: "Display Interstitial 320x480 (In-App, Custom Renderer)", + tags: [.interstitial, .inapp, .server], + exampleVCStoryboardID: "AdapterViewController", + configurationClosure: { vc in + guard let adapterVC = vc as? AdapterViewController else { + return + } + + let interstitialController = CustomRendererInterstitialController(rootController: adapterVC) + interstitialController.adFormats = [.banner] + interstitialController.prebidConfigId = "prebid-demo-display-interstitial-320-480-custom-interstitial-renderer" + + adapterVC.setup(adapter: interstitialController) + + setupCustomParams(for: interstitialController.prebidConfigId) + }), + TestCase(title: "Display Interstitial 320x480 (In-App) [noBids]", tags: [.interstitial, .inapp, .server], exampleVCStoryboardID: "AdapterViewController", diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/CustomRenderer/CustomRendererBannerController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/CustomRendererBannerController.swift similarity index 96% rename from InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/CustomRenderer/CustomRendererBannerController.swift rename to InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/CustomRendererBannerController.swift index 8370e7b31..003fa36fd 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/CustomRenderer/CustomRendererBannerController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/CustomRendererBannerController.swift @@ -16,7 +16,11 @@ import UIKit import PrebidMobile -class CustomRendererBannerController: NSObject, AdaptedController, PrebidConfigurableBannerController, BannerViewDelegate { +class CustomRendererBannerController: + NSObject, + AdaptedController, + PrebidConfigurableBannerController, + BannerViewDelegate { var refreshInterval: TimeInterval = 0 @@ -39,7 +43,7 @@ class CustomRendererBannerController: NSObject, AdaptedController, PrebidConfigu private let reloadButton = ThreadCheckingButton() private let stopRefreshButton = ThreadCheckingButton() - private let sampleCustomRenderer = SampleCustomRenderer() + private let sampleCustomRenderer = SampleRenderer() let lastLoadedAdSizeLabel = UILabel() private let configIdLabel = UILabel() @@ -47,6 +51,7 @@ class CustomRendererBannerController: NSObject, AdaptedController, PrebidConfigu required init(rootController: AdapterViewController) { super.init() self.rootController = rootController + Prebid.registerPluginRenderer(sampleCustomRenderer) reloadButton.addTarget(self, action: #selector(reload), for: .touchUpInside) @@ -70,9 +75,11 @@ class CustomRendererBannerController: NSObject, AdaptedController, PrebidConfigu let size = adSizes[0] - adBannerView = BannerView(frame: CGRect(origin: .zero, size: size), - configID: prebidConfigId, - adSize: size) + adBannerView = BannerView( + frame: CGRect(origin: .zero, size: size), + configID: prebidConfigId, + adSize: size + ) if (refreshInterval > 0) { adBannerView?.refreshInterval = refreshInterval diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/CustomRenderer/CustomRendererInterstitialController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/CustomRendererInterstitialController.swift similarity index 94% rename from InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/CustomRenderer/CustomRendererInterstitialController.swift rename to InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/CustomRendererInterstitialController.swift index 42d8d7058..943c76b34 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/CustomRenderer/CustomRendererInterstitialController.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/CustomRendererInterstitialController.swift @@ -17,8 +17,11 @@ import UIKit import GoogleMobileAds import PrebidMobile -class CustomRendererInterstitialController: NSObject, AdaptedController, - PrebidConfigurableController, InterstitialAdUnitDelegate { +class CustomRendererInterstitialController: + NSObject, + AdaptedController, + PrebidConfigurableController, + InterstitialAdUnitDelegate { var prebidConfigId = "" var storedAuctionResponse: String? @@ -26,6 +29,7 @@ class CustomRendererInterstitialController: NSObject, AdaptedController, var adFormats: Set? private var interstitialController : InterstitialRenderingAdUnit? + private let sampleCustomRenderer = SampleRenderer() private weak var adapterViewController: AdapterViewController? @@ -35,7 +39,6 @@ class CustomRendererInterstitialController: NSObject, AdaptedController, private let interstitialDidDismissAdButton = EventReportContainer() private let interstitialWillLeaveApplicationButton = EventReportContainer() private let interstitialDidClickAdButton = EventReportContainer() - private let sampleCustomRenderer = SampleCustomRenderer() private let configIdLabel = UILabel() @@ -74,8 +77,11 @@ class CustomRendererInterstitialController: NSObject, AdaptedController, Prebid.shared.storedAuctionResponse = storedAuctionResponse } - interstitialController = InterstitialRenderingAdUnit(configID: prebidConfigId, - minSizePercentage: CGSize(width: 30, height: 30)) + interstitialController = InterstitialRenderingAdUnit( + configID: prebidConfigId, + minSizePercentage: CGSize(width: 30, height: 30) + ) + interstitialController?.delegate = self // Custom video configuarion diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleAdView.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleAdView.swift new file mode 100644 index 000000000..74a3bc98a --- /dev/null +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleAdView.swift @@ -0,0 +1,136 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import UIKit +import WebKit +import PrebidMobile +import SafariServices + +class SampleAdView: UIView, PrebidMobileDisplayViewProtocol { + + enum SampleError: LocalizedError { + case noAdm + + var errorDescription: String? { + switch self { + case .noAdm: + return "Renderer did fail - there is no ADM in the response." + } + } + } + + weak var interactionDelegate: DisplayViewInteractionDelegate? + weak var loadingDelegate: DisplayViewLoadingDelegate? + + var bid: Bid? + + private let webView: WKWebView = { + let webView = WKWebView() + webView.translatesAutoresizingMaskIntoConstraints = false + return webView + }() + + private let customRendererLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.text = "Custom Renderer" + label.textColor = .white + label.font = UIFont.boldSystemFont(ofSize: 10) + label.backgroundColor = UIColor.black.withAlphaComponent(0.5) + label.textAlignment = .center + label.layer.cornerRadius = 5 + label.layer.masksToBounds = true + label.numberOfLines = 0 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + setupView() + webView.navigationDelegate = self + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupView() + webView.navigationDelegate = self + } + + func loadAd() { + DispatchQueue.main.async { + if let adm = self.bid?.adm { + self.webView.loadHTMLString(adm, baseURL: nil) + self.loadingDelegate?.displayViewDidLoadAd(self) + } else { + self.loadingDelegate?.displayView(self, didFailWithError: SampleError.noAdm) + } + } + } + + private func setupView() { + addSubview(webView) + addSubview(customRendererLabel) + + NSLayoutConstraint.activate([ + webView.topAnchor.constraint(equalTo: topAnchor), + webView.leadingAnchor.constraint(equalTo: leadingAnchor), + webView.trailingAnchor.constraint(equalTo: trailingAnchor), + webView.bottomAnchor.constraint(equalTo: bottomAnchor), + + customRendererLabel.topAnchor.constraint(equalTo: topAnchor, constant: 5), + customRendererLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + customRendererLabel.heightAnchor.constraint(equalToConstant: 40), + customRendererLabel.widthAnchor.constraint(equalToConstant: 50) + ]) + } +} + +extension SampleAdView: WKNavigationDelegate { + + func webView( + _ webView: WKWebView, + decidePolicyFor navigationAction: WKNavigationAction, + decisionHandler: @MainActor @Sendable @escaping (WKNavigationActionPolicy) -> Void + ) { + guard let url = navigationAction.request.url, + navigationAction.navigationType == .linkActivated else { + decisionHandler(.allow) + return + } + + guard let presentingVC = interactionDelegate? + .viewControllerForModalPresentation(fromDisplayView: self) else { + decisionHandler(.allow) + return + } + + let safariVC = SFSafariViewController(url: url) + safariVC.delegate = self + interactionDelegate?.willPresentModal(from: self) + presentingVC.present(safariVC, animated: true) { [weak self] in + guard let self = self else { return } + self.interactionDelegate?.didDismissModal(from: self) + } + + decisionHandler(.cancel) + } +} + +extension SampleAdView: SFSafariViewControllerDelegate { + + func safariViewControllerWillOpenInBrowser(_ controller: SFSafariViewController) { + interactionDelegate?.didLeaveApp(from: self) + } +} diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleInterstitialController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleInterstitialController.swift new file mode 100644 index 000000000..39e328d00 --- /dev/null +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleInterstitialController.swift @@ -0,0 +1,124 @@ +/*   Copyright 2018-2021 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import UIKit +import WebKit +import PrebidMobile + +class SampleInterstitialController: NSObject, PrebidMobileInterstitialControllerProtocol { + + enum SampleError: LocalizedError { + case noAdm + case noAvailableController + + var errorDescription: String? { + switch self { + case .noAdm: + return "Renderer did fail - there is no ADM in the response." + case .noAvailableController: + return "Coudn't find a controller to present from." + } + } + } + + weak var loadingDelegate: InterstitialControllerLoadingDelegate? + weak var interactionDelegate: InterstitialControllerInteractionDelegate? + + var bid: Bid? + + private var webView: WKWebView { + interstitialViewController.webView + } + + private lazy var interstitialViewController: SampleModalViewController = { + let viewController = SampleModalViewController() + + viewController.onDismiss = { [weak self] in + guard let self else { return } + self.interactionDelegate?.interstitialControllerDidCloseAd(self) + } + + return viewController + }() + + private var topViewController: UIViewController? { + var topController = UIApplication.shared.keyWindow?.rootViewController + while let presentedController = topController?.presentedViewController { + topController = presentedController + } + return topController + } + + override init() { + super.init() + webView.navigationDelegate = self + } + + func loadAd() { + DispatchQueue.main.async { + guard let adm = self.bid?.adm else { + self.loadingDelegate?.interstitialController(self, didFailWithError: SampleError.noAdm) + return + } + + + self.webView.loadHTMLString(adm, baseURL: nil) + self.loadingDelegate?.interstitialControllerDidLoadAd(self) + } + } + + func show() { + DispatchQueue.main.async { + guard let presentingController = self.topViewController else { + self.loadingDelegate?.interstitialController( + self, + didFailWithError: SampleError.noAvailableController + ) + return + } + + presentingController.present( + self.interstitialViewController, + animated: true + ) + } + } +} + +extension SampleInterstitialController: WKNavigationDelegate { + + func webView( + _ webView: WKWebView, + decidePolicyFor navigationAction: WKNavigationAction, + decisionHandler: @escaping (WKNavigationActionPolicy) -> Void + ) { + guard let url = navigationAction.request.url, + navigationAction.navigationType == .linkActivated else { + decisionHandler(.allow) + return + } + + interactionDelegate?.interstitialControllerDidClickAd(self) + + guard UIApplication.shared.canOpenURL(url) else { + decisionHandler(.allow) + return + } + + interactionDelegate?.interstitialControllerDidLeaveApp(self) + UIApplication.shared.open(url, options: [:], completionHandler: nil) + decisionHandler(.cancel) + } +} diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleModalViewController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleModalViewController.swift new file mode 100644 index 000000000..7e4e6dcc4 --- /dev/null +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleModalViewController.swift @@ -0,0 +1,60 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import WebKit + +class SampleModalViewController: UIViewController { + + var onDismiss: (() -> Void)? + + let webView: WKWebView = { + let webView = WKWebView(frame: CGRect(origin: .zero, size: CGSize(width: 300, height: 250))) + webView.translatesAutoresizingMaskIntoConstraints = false + webView.backgroundColor = .blue + return webView + }() + + private let customRendererLabel: UILabel = { + let label = UILabel() + label.text = "Custom Renderer" + label.textAlignment = .center + label.font = UIFont.boldSystemFont(ofSize: 18) + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .white + view.addSubview(customRendererLabel) + view.addSubview(webView) + + NSLayoutConstraint.activate([ + customRendererLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), + customRendererLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), + webView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + webView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + webView.widthAnchor.constraint(equalToConstant: 300), + webView.heightAnchor.constraint(equalToConstant: 250), + ]) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + onDismiss?() + } +} diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleRenderer.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleRenderer.swift new file mode 100644 index 000000000..15f4378c6 --- /dev/null +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OpenX/CustomRenderer/Renderers/SampleRenderer.swift @@ -0,0 +1,55 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit +import PrebidMobile + +public class SampleRenderer: NSObject, PrebidMobilePluginRenderer { + + public let name = "SampleRenderer" + public let version = "1.0.0" + public var data: [String: Any]? + + public func createBannerView( + with frame: CGRect, + bid: Bid, + adConfiguration: AdUnitConfig, + loadingDelegate: DisplayViewLoadingDelegate, + interactionDelegate: DisplayViewInteractionDelegate + ) -> (UIView & PrebidMobileDisplayViewProtocol)? { + let bannerView = SampleAdView(frame: frame) + + bannerView.interactionDelegate = interactionDelegate + bannerView.loadingDelegate = loadingDelegate + bannerView.bid = bid + + return bannerView + } + + public func createInterstitialController( + bid: Bid, + adConfiguration: AdUnitConfig, + loadingDelegate: InterstitialControllerLoadingDelegate, + interactionDelegate: InterstitialControllerInteractionDelegate + ) -> PrebidMobileInterstitialControllerProtocol? { + let interstitialController = SampleInterstitialController() + + interstitialController.loadingDelegate = loadingDelegate + interstitialController.interactionDelegate = interactionDelegate + interstitialController.bid = bid + + return interstitialController + } +} diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index 68a135034..045327e3c 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -106,6 +106,7 @@ 535145E12CCB758800D40B19 /* NSObject+PBMExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 535145DE2CCB758800D40B19 /* NSObject+PBMExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 53514CC62D01B5B900A480C0 /* InterstitialRenderingAdUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CC52D01B5AF00A480C0 /* InterstitialRenderingAdUnitTest.swift */; }; 53514CC82D01B5F900A480C0 /* RewardedAdUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CC72D01B5F200A480C0 /* RewardedAdUnitTest.swift */; }; + 53514CF22D0851DE00A480C0 /* InterstitialControllerInteractionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CF12D0851DE00A480C0 /* InterstitialControllerInteractionDelegate.swift */; }; 5355ACA929C454070014F16E /* VAST_with_empty_companion.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5355ACA829C454070014F16E /* VAST_with_empty_companion.xml */; }; 5355ACAB29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355ACAA29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift */; }; 536A39262A84C50F00B1CCEA /* StringExtensionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536A39252A84C50F00B1CCEA /* StringExtensionsTest.swift */; }; @@ -144,10 +145,19 @@ 53BDBF89293F5EFF004B6DE8 /* InternalUserConsentDataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 53BDBF88293F5EFF004B6DE8 /* InternalUserConsentDataManager.m */; }; 53BDBF8A293F605B004B6DE8 /* InternalUserConsentDataManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 53BDBF87293F5EF6004B6DE8 /* InternalUserConsentDataManager.h */; }; 53BDBF8C293F6573004B6DE8 /* InternalUserConsentDataManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BDBF8B293F6573004B6DE8 /* InternalUserConsentDataManagerTests.swift */; }; + 53BFCD112CF9156000A3287A /* PrebidMobileInterstitialControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BFCD102CF9155E00A3287A /* PrebidMobileInterstitialControllerProtocol.swift */; }; + 53BFCD1F2CFDC60100A3287A /* PrebidMobileDisplayViewProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 53BFCD1E2CFDC60100A3287A /* PrebidMobileDisplayViewProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 53BFCD312CFE435D00A3287A /* PrebidMobilePluginRenderer+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BFCD302CFE435D00A3287A /* PrebidMobilePluginRenderer+Extensions.swift */; }; + 53BFCD332CFF30C800A3287A /* MockPrebidMobilePluginRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53BFCD322CFF30C800A3287A /* MockPrebidMobilePluginRenderer.swift */; }; 53C8FE1729C0851300ED9230 /* ClickbrowserType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53C8FE1629C0851300ED9230 /* ClickbrowserType.swift */; }; 53C925022990FB30009E6F94 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53C925012990FB30009E6F94 /* String+Extensions.swift */; }; 53C925052990FBFB009E6F94 /* PrebidServerStatusRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53C925042990FBFB009E6F94 /* PrebidServerStatusRequester.swift */; }; 53C9250729915466009E6F94 /* PrebidServerStatusRequesterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53C9250629915466009E6F94 /* PrebidServerStatusRequesterTests.swift */; }; + 53CDA48D2D11AFD5004766F8 /* MediationBannerAdUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53CDA4872D11AFD5004766F8 /* MediationBannerAdUnitTest.swift */; }; + 53CDA48E2D11AFD5004766F8 /* MockMediationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53CDA48B2D11AFD5004766F8 /* MockMediationUtils.swift */; }; + 53CDA48F2D11AFD5004766F8 /* MediationInterstitialAdUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53CDA4882D11AFD5004766F8 /* MediationInterstitialAdUnitTest.swift */; }; + 53CDA4902D11AFD5004766F8 /* MediationUtilsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53CDA48A2D11AFD5004766F8 /* MediationUtilsTest.swift */; }; + 53CDA4912D11AFD5004766F8 /* MediationRewardedAdUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53CDA4892D11AFD5004766F8 /* MediationRewardedAdUnitTest.swift */; }; 53CE09012AD673020018CB75 /* sample_ortb_native_with_win_event.json in Resources */ = {isa = PBXBuildFile; fileRef = 53CE09002AD673020018CB75 /* sample_ortb_native_with_win_event.json */; }; 53CE09032AD67ECE0018CB75 /* MockPBMBidRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53CE09022AD67ECE0018CB75 /* MockPBMBidRequester.swift */; }; 53CF5B3729DC690600613E84 /* VideoAdUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53CF5B3629DC690600613E84 /* VideoAdUnit.swift */; }; @@ -417,7 +427,6 @@ 5BC379D8271F1D0000444D5E /* PBMDisplayView+InternalState.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BC37788271F1CFE00444D5E /* PBMDisplayView+InternalState.h */; }; 5BC379DB271F1D0000444D5E /* InterstitialControllerLoadingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3778B271F1CFE00444D5E /* InterstitialControllerLoadingDelegate.swift */; }; 5BC379DC271F1D0000444D5E /* PBMDisplayView.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BC3778C271F1CFE00444D5E /* PBMDisplayView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5BC379DD271F1D0000444D5E /* InterstitialControllerInteractionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3778D271F1CFE00444D5E /* InterstitialControllerInteractionDelegate.swift */; }; 5BC379DE271F1D0000444D5E /* InterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3778E271F1CFE00444D5E /* InterstitialController.swift */; }; 5BC379DF271F1D0000444D5E /* DisplayViewLoadingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC3778F271F1CFE00444D5E /* DisplayViewLoadingDelegate.swift */; }; 5BC379E0271F1D0000444D5E /* DisplayViewInteractionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC37790271F1CFE00444D5E /* DisplayViewInteractionDelegate.swift */; }; @@ -668,7 +677,6 @@ 9236A64E272FEAFE00ABBEF4 /* ImpressionTasksExecutor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9236A64D272FEAFE00ABBEF4 /* ImpressionTasksExecutor.swift */; }; 9236A650272FEC5A00ABBEF4 /* ImpressionTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9236A64F272FEC5A00ABBEF4 /* ImpressionTask.swift */; }; 923B55002744F19600E00C88 /* PrebidMediationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923B54FF2744F19600E00C88 /* PrebidMediationDelegate.swift */; }; - 923B5521274536B400E00C88 /* MockMediationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923B5520274536B400E00C88 /* MockMediationUtils.swift */; }; 9247FEFE2808175E00ACD84A /* VideoControlsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9247FEFD2808175E00ACD84A /* VideoControlsConfiguration.swift */; }; 924F661827FDB79C00C8DAF7 /* PBMORTBExtPrebidPassthrough.h in Headers */ = {isa = PBXBuildFile; fileRef = 924F661727FDB79C00C8DAF7 /* PBMORTBExtPrebidPassthrough.h */; settings = {ATTRIBUTES = (Public, ); }; }; 924F661A27FDB82600C8DAF7 /* PBMORTBAdConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 924F661927FDB82600C8DAF7 /* PBMORTBAdConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -717,9 +725,6 @@ 925D5E182737E7D200A8A2B5 /* PBMErrorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 925D5E172737E7D200A8A2B5 /* PBMErrorTest.swift */; }; 925D5E1C2737E89500A8A2B5 /* PBMORTBTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 925D5E1B2737E89400A8A2B5 /* PBMORTBTest.swift */; }; 925D5E1E2737E8BF00A8A2B5 /* PBMBaseInterstitialAdUnit_DelegationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 925D5E1D2737E8BF00A8A2B5 /* PBMBaseInterstitialAdUnit_DelegationTest.swift */; }; - 925D5E222737EA1E00A8A2B5 /* MediationBannerAdUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 925D5E212737EA1E00A8A2B5 /* MediationBannerAdUnitTest.swift */; }; - 925D5E272737EC5E00A8A2B5 /* MediationInterstitialAdUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 925D5E262737EC5E00A8A2B5 /* MediationInterstitialAdUnitTest.swift */; }; - 925D5E292737EC9400A8A2B5 /* MediationRewardedAdUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 925D5E282737EC9400A8A2B5 /* MediationRewardedAdUnitTest.swift */; }; 925D5E2D2737ECEA00A8A2B5 /* PBMWinNotifierTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 925D5E2C2737ECEA00A8A2B5 /* PBMWinNotifierTest.swift */; }; 925D5E302737ED8C00A8A2B5 /* PBMRewardedVideoViewTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 925D5E2F2737ED8C00A8A2B5 /* PBMRewardedVideoViewTest.swift */; }; 925D5E332737EE1C00A8A2B5 /* PBMTransactionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 925D5E322737EE1C00A8A2B5 /* PBMTransactionTests.swift */; }; @@ -762,7 +767,6 @@ 92664569272AB9120064F7BD /* SkadnEventTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92664568272AB9120064F7BD /* SkadnEventTracker.swift */; }; 9266456B272AB9610064F7BD /* SkadnParametersManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9266456A272AB9610064F7BD /* SkadnParametersManager.swift */; }; 927ADB2428057B31006EB8D5 /* MediationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927ADB2328057B31006EB8D5 /* MediationUtils.swift */; }; - 927ADB2728058196006EB8D5 /* MediationUtilsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927ADB2628058196006EB8D5 /* MediationUtilsTest.swift */; }; 9285B46C27AAC9C1009BD3D3 /* NativeAdMarkupTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9285B46B27AAC9C1009BD3D3 /* NativeAdMarkupTest.swift */; }; 928E5A6E27F0F706000ADA1A /* ContextType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 928E5A6D27F0F706000ADA1A /* ContextType.swift */; }; 928E5A7027F0F743000ADA1A /* ContextSubType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 928E5A6F27F0F743000ADA1A /* ContextSubType.swift */; }; @@ -809,8 +813,6 @@ A908694429E05ED500B37479 /* PrebidMobilePluginRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A908694329E05ED500B37479 /* PrebidMobilePluginRenderer.swift */; }; A908694629E05F7900B37479 /* PrebidRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A908694529E05F7900B37479 /* PrebidRenderer.swift */; }; A9750D7A2ABB9A300066E4E6 /* PluginEventListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9750D792ABB9A300066E4E6 /* PluginEventListener.swift */; }; - A9A3396B2AC57193006AD0E7 /* PBMAdViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A3396A2AC57193006AD0E7 /* PBMAdViewDelegate.swift */; }; - F95DA2072C87567D0068A2BB /* SampleCustomRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F95DA2062C87567D0068A2BB /* SampleCustomRenderer.swift */; }; FA5AD5E42271FA4100C8F274 /* ConstantsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5AD5E32271FA4100C8F274 /* ConstantsTest.swift */; }; FA9D7F2722E8A83D006FCBEF /* AdViewUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA9D7F2622E8A83D006FCBEF /* AdViewUtilsTests.swift */; }; FAA29904242D1C27002ACBF2 /* TargetingObjCTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FAA29903242D1C27002ACBF2 /* TargetingObjCTests.m */; }; @@ -997,6 +999,7 @@ 535145DF2CCB758800D40B19 /* NSObject+PBMExtensions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+PBMExtensions.m"; sourceTree = ""; }; 53514CC52D01B5AF00A480C0 /* InterstitialRenderingAdUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterstitialRenderingAdUnitTest.swift; sourceTree = ""; }; 53514CC72D01B5F200A480C0 /* RewardedAdUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RewardedAdUnitTest.swift; sourceTree = ""; }; + 53514CF12D0851DE00A480C0 /* InterstitialControllerInteractionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterstitialControllerInteractionDelegate.swift; sourceTree = ""; }; 5355ACA829C454070014F16E /* VAST_with_empty_companion.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = VAST_with_empty_companion.xml; sourceTree = ""; }; 5355ACAA29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreativeModelCollectionMakerVASTTests.swift; sourceTree = ""; }; 536A39252A84C50F00B1CCEA /* StringExtensionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensionsTest.swift; sourceTree = ""; }; @@ -1035,10 +1038,19 @@ 53BDBF87293F5EF6004B6DE8 /* InternalUserConsentDataManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InternalUserConsentDataManager.h; sourceTree = ""; }; 53BDBF88293F5EFF004B6DE8 /* InternalUserConsentDataManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InternalUserConsentDataManager.m; sourceTree = ""; }; 53BDBF8B293F6573004B6DE8 /* InternalUserConsentDataManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalUserConsentDataManagerTests.swift; sourceTree = ""; }; + 53BFCD102CF9155E00A3287A /* PrebidMobileInterstitialControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidMobileInterstitialControllerProtocol.swift; sourceTree = ""; }; + 53BFCD1E2CFDC60100A3287A /* PrebidMobileDisplayViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PrebidMobileDisplayViewProtocol.h; sourceTree = ""; }; + 53BFCD302CFE435D00A3287A /* PrebidMobilePluginRenderer+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PrebidMobilePluginRenderer+Extensions.swift"; sourceTree = ""; }; + 53BFCD322CFF30C800A3287A /* MockPrebidMobilePluginRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPrebidMobilePluginRenderer.swift; sourceTree = ""; }; 53C8FE1629C0851300ED9230 /* ClickbrowserType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClickbrowserType.swift; sourceTree = ""; }; 53C925012990FB30009E6F94 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = ""; }; 53C925042990FBFB009E6F94 /* PrebidServerStatusRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidServerStatusRequester.swift; sourceTree = ""; }; 53C9250629915466009E6F94 /* PrebidServerStatusRequesterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidServerStatusRequesterTests.swift; sourceTree = ""; }; + 53CDA4872D11AFD5004766F8 /* MediationBannerAdUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediationBannerAdUnitTest.swift; sourceTree = ""; }; + 53CDA4882D11AFD5004766F8 /* MediationInterstitialAdUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediationInterstitialAdUnitTest.swift; sourceTree = ""; }; + 53CDA4892D11AFD5004766F8 /* MediationRewardedAdUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediationRewardedAdUnitTest.swift; sourceTree = ""; }; + 53CDA48A2D11AFD5004766F8 /* MediationUtilsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediationUtilsTest.swift; sourceTree = ""; }; + 53CDA48B2D11AFD5004766F8 /* MockMediationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediationUtils.swift; sourceTree = ""; }; 53CE09002AD673020018CB75 /* sample_ortb_native_with_win_event.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = sample_ortb_native_with_win_event.json; sourceTree = ""; }; 53CE09022AD67ECE0018CB75 /* MockPBMBidRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPBMBidRequester.swift; sourceTree = ""; }; 53CF5B3629DC690600613E84 /* VideoAdUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoAdUnit.swift; sourceTree = ""; }; @@ -1310,7 +1322,6 @@ 5BC37788271F1CFE00444D5E /* PBMDisplayView+InternalState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PBMDisplayView+InternalState.h"; sourceTree = ""; }; 5BC3778B271F1CFE00444D5E /* InterstitialControllerLoadingDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterstitialControllerLoadingDelegate.swift; sourceTree = ""; }; 5BC3778C271F1CFE00444D5E /* PBMDisplayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBMDisplayView.h; sourceTree = ""; }; - 5BC3778D271F1CFE00444D5E /* InterstitialControllerInteractionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterstitialControllerInteractionDelegate.swift; sourceTree = ""; }; 5BC3778E271F1CFE00444D5E /* InterstitialController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterstitialController.swift; sourceTree = ""; }; 5BC3778F271F1CFE00444D5E /* DisplayViewLoadingDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayViewLoadingDelegate.swift; sourceTree = ""; }; 5BC37790271F1CFE00444D5E /* DisplayViewInteractionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayViewInteractionDelegate.swift; sourceTree = ""; }; @@ -1579,7 +1590,6 @@ 9236A64D272FEAFE00ABBEF4 /* ImpressionTasksExecutor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImpressionTasksExecutor.swift; sourceTree = ""; }; 9236A64F272FEC5A00ABBEF4 /* ImpressionTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImpressionTask.swift; sourceTree = ""; }; 923B54FF2744F19600E00C88 /* PrebidMediationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidMediationDelegate.swift; sourceTree = ""; }; - 923B5520274536B400E00C88 /* MockMediationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediationUtils.swift; sourceTree = ""; }; 9247FEFD2808175E00ACD84A /* VideoControlsConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoControlsConfiguration.swift; sourceTree = ""; }; 924F661727FDB79C00C8DAF7 /* PBMORTBExtPrebidPassthrough.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMORTBExtPrebidPassthrough.h; sourceTree = ""; }; 924F661927FDB82600C8DAF7 /* PBMORTBAdConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PBMORTBAdConfiguration.h; sourceTree = ""; }; @@ -1631,11 +1641,8 @@ 925D5E172737E7D200A8A2B5 /* PBMErrorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBMErrorTest.swift; sourceTree = ""; }; 925D5E1B2737E89400A8A2B5 /* PBMORTBTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBMORTBTest.swift; sourceTree = ""; }; 925D5E1D2737E8BF00A8A2B5 /* PBMBaseInterstitialAdUnit_DelegationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBMBaseInterstitialAdUnit_DelegationTest.swift; sourceTree = ""; }; - 925D5E212737EA1E00A8A2B5 /* MediationBannerAdUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediationBannerAdUnitTest.swift; sourceTree = ""; }; 925D5E232737EAE500A8A2B5 /* MediationBannerAdUnit+TestExtension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MediationBannerAdUnit+TestExtension.h"; sourceTree = ""; }; 925D5E242737EB5400A8A2B5 /* MediationInterstitialAdUnit+TestExtension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MediationInterstitialAdUnit+TestExtension.h"; sourceTree = ""; }; - 925D5E262737EC5E00A8A2B5 /* MediationInterstitialAdUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediationInterstitialAdUnitTest.swift; sourceTree = ""; }; - 925D5E282737EC9400A8A2B5 /* MediationRewardedAdUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediationRewardedAdUnitTest.swift; sourceTree = ""; }; 925D5E2C2737ECEA00A8A2B5 /* PBMWinNotifierTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBMWinNotifierTest.swift; sourceTree = ""; }; 925D5E2F2737ED8C00A8A2B5 /* PBMRewardedVideoViewTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBMRewardedVideoViewTest.swift; sourceTree = ""; }; 925D5E312737EDCD00A8A2B5 /* PBMVideoView+pbmTestExtension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PBMVideoView+pbmTestExtension.h"; sourceTree = ""; }; @@ -1682,7 +1689,6 @@ 92664568272AB9120064F7BD /* SkadnEventTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkadnEventTracker.swift; sourceTree = ""; }; 9266456A272AB9610064F7BD /* SkadnParametersManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkadnParametersManager.swift; sourceTree = ""; }; 927ADB2328057B31006EB8D5 /* MediationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediationUtils.swift; sourceTree = ""; }; - 927ADB2628058196006EB8D5 /* MediationUtilsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediationUtilsTest.swift; sourceTree = ""; }; 9285B46B27AAC9C1009BD3D3 /* NativeAdMarkupTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeAdMarkupTest.swift; sourceTree = ""; }; 928E5A6D27F0F706000ADA1A /* ContextType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextType.swift; sourceTree = ""; }; 928E5A6F27F0F743000ADA1A /* ContextSubType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextSubType.swift; sourceTree = ""; }; @@ -1732,8 +1738,6 @@ A908694329E05ED500B37479 /* PrebidMobilePluginRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidMobilePluginRenderer.swift; sourceTree = ""; }; A908694529E05F7900B37479 /* PrebidRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidRenderer.swift; sourceTree = ""; }; A9750D792ABB9A300066E4E6 /* PluginEventListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginEventListener.swift; sourceTree = ""; }; - A9A3396A2AC57193006AD0E7 /* PBMAdViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBMAdViewDelegate.swift; sourceTree = ""; }; - F95DA2062C87567D0068A2BB /* SampleCustomRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleCustomRenderer.swift; sourceTree = ""; }; FA4A88432497A99D00FDCBB6 /* Swizzling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Swizzling.swift; sourceTree = ""; }; FA5AD5E32271FA4100C8F274 /* ConstantsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsTest.swift; sourceTree = ""; }; FA85F9B4264946FC00B8BE72 /* TestUtils.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TestUtils.xcodeproj; path = ../tools/TestUtils/TestUtils.xcodeproj; sourceTree = ""; }; @@ -1832,6 +1836,7 @@ 539D4C7129B5E23600B6B30E /* MockPBMAbstractCreative.swift */, 533FDF842A12030C0066ED5A /* MockPrebidJSLibraryManager.swift */, 53CE09022AD67ECE0018CB75 /* MockPBMBidRequester.swift */, + 53BFCD322CFF30C800A3287A /* MockPrebidMobilePluginRenderer.swift */, 534C61432CB5619B0026119A /* MockVideoView.swift */, ); path = Mocks; @@ -1995,6 +2000,25 @@ path = CacheManagement; sourceTree = ""; }; + 53CDA4832D11AEE3004766F8 /* Recovered References */ = { + isa = PBXGroup; + children = ( + ); + name = "Recovered References"; + sourceTree = ""; + }; + 53CDA48C2D11AFD5004766F8 /* MediationAPI */ = { + isa = PBXGroup; + children = ( + 53CDA4872D11AFD5004766F8 /* MediationBannerAdUnitTest.swift */, + 53CDA4882D11AFD5004766F8 /* MediationInterstitialAdUnitTest.swift */, + 53CDA4892D11AFD5004766F8 /* MediationRewardedAdUnitTest.swift */, + 53CDA48A2D11AFD5004766F8 /* MediationUtilsTest.swift */, + 53CDA48B2D11AFD5004766F8 /* MockMediationUtils.swift */, + ); + path = MediationAPI; + sourceTree = ""; + }; 53CE08FF2AD672980018CB75 /* ORTB-samples */ = { isa = PBXGroup; children = ( @@ -2436,15 +2460,17 @@ 5BC37786271F1CFE00444D5E /* PBMCacheRenderers */ = { isa = PBXGroup; children = ( + A908694529E05F7900B37479 /* PrebidRenderer.swift */, + 53BFCD1E2CFDC60100A3287A /* PrebidMobileDisplayViewProtocol.h */, + 5BC3778C271F1CFE00444D5E /* PBMDisplayView.h */, 5BC37787271F1CFE00444D5E /* PBMDisplayView.m */, 5BC37788271F1CFE00444D5E /* PBMDisplayView+InternalState.h */, - 5BC3778B271F1CFE00444D5E /* InterstitialControllerLoadingDelegate.swift */, - 5BC3778C271F1CFE00444D5E /* PBMDisplayView.h */, - 5BC3778D271F1CFE00444D5E /* InterstitialControllerInteractionDelegate.swift */, - 5BC3778E271F1CFE00444D5E /* InterstitialController.swift */, 5BC3778F271F1CFE00444D5E /* DisplayViewLoadingDelegate.swift */, 5BC37790271F1CFE00444D5E /* DisplayViewInteractionDelegate.swift */, - A908694529E05F7900B37479 /* PrebidRenderer.swift */, + 53BFCD102CF9155E00A3287A /* PrebidMobileInterstitialControllerProtocol.swift */, + 5BC3778E271F1CFE00444D5E /* InterstitialController.swift */, + 5BC3778B271F1CFE00444D5E /* InterstitialControllerLoadingDelegate.swift */, + 53514CF12D0851DE00A480C0 /* InterstitialControllerInteractionDelegate.swift */, ); path = PBMCacheRenderers; sourceTree = ""; @@ -2844,6 +2870,7 @@ 60D792F3217E229A0080F428 /* Products */, 6062B19A218243FA00CE1BA3 /* Frameworks */, EE09B07119B6562CC4356195 /* Pods */, + 53CDA4832D11AEE3004766F8 /* Recovered References */, ); sourceTree = ""; }; @@ -3066,8 +3093,9 @@ 925D5DA82737C4C400A8A2B5 /* Prebid */ = { isa = PBXGroup; children = ( + 53CDA48C2D11AFD5004766F8 /* MediationAPI */, 925D5DA92737C4E800A8A2B5 /* AdLoadFlow */, - 927ADB2528058170006EB8D5 /* MediationAPI */, + 3CD0ADED2C5125E9006CDA6B /* PluginRegisterTest.swift */, 925D5E082737DF5F00A8A2B5 /* PBMBannerViewTest.swift */, 925D5E112737E04B00A8A2B5 /* PBMBidRequesterTest.swift */, 925D5E132737E74C00A8A2B5 /* PBMBidResponseTransformerTest.swift */, @@ -3173,18 +3201,6 @@ path = VASTTests; sourceTree = ""; }; - 927ADB2528058170006EB8D5 /* MediationAPI */ = { - isa = PBXGroup; - children = ( - 925D5E212737EA1E00A8A2B5 /* MediationBannerAdUnitTest.swift */, - 925D5E262737EC5E00A8A2B5 /* MediationInterstitialAdUnitTest.swift */, - 925D5E282737EC9400A8A2B5 /* MediationRewardedAdUnitTest.swift */, - 923B5520274536B400E00C88 /* MockMediationUtils.swift */, - 927ADB2628058196006EB8D5 /* MediationUtilsTest.swift */, - ); - path = MediationAPI; - sourceTree = ""; - }; 929638B927AB1FE900D30F3D /* NativeAssetObjectsTests */ = { isa = PBXGroup; children = ( @@ -3276,11 +3292,10 @@ A908694029E05E8D00B37479 /* PluginRenderer */ = { isa = PBXGroup; children = ( - F95DA2062C87567D0068A2BB /* SampleCustomRenderer.swift */, A908694129E05EAF00B37479 /* PrebidMobilePluginRegister.swift */, A908694329E05ED500B37479 /* PrebidMobilePluginRenderer.swift */, + 53BFCD302CFE435D00A3287A /* PrebidMobilePluginRenderer+Extensions.swift */, A9750D792ABB9A300066E4E6 /* PluginEventListener.swift */, - A9A3396A2AC57193006AD0E7 /* PBMAdViewDelegate.swift */, ); path = PluginRenderer; sourceTree = ""; @@ -3298,7 +3313,6 @@ FAC837D72321583500565051 /* CollectionExtensionTest.swift */, FA5AD5E32271FA4100C8F274 /* ConstantsTest.swift */, FAEBF2A1237ECFEF006BA972 /* StorageUtilsTests.swift */, - 3CD0ADED2C5125E9006CDA6B /* PluginRegisterTest.swift */, ); path = UnitTests; sourceTree = ""; @@ -3573,6 +3587,7 @@ 5BC37929271F1CFF00444D5E /* PBMLocationManagerProtocol.h in Headers */, 5BC37A9E271F1D0000444D5E /* PBMAdLoadFlowState.h in Headers */, 5BC379BD271F1D0000444D5E /* PBMAdRefreshOptions.h in Headers */, + 53BFCD1F2CFDC60100A3287A /* PrebidMobileDisplayViewProtocol.h in Headers */, 5BC378F8271F1CFF00444D5E /* PBMORTBPmp.h in Headers */, 534C612D2CB527F80026119A /* PBMORTBRewardedCompletionVideo.h in Headers */, 92664565272AB5BB0064F7BD /* PBMORTBSkadnFidelity.h in Headers */, @@ -3862,6 +3877,7 @@ 922AFD742737340300732C53 /* PrebidTest.swift in Sources */, 925D5E3F2737F01000A8A2B5 /* URLComponentsTests.swift in Sources */, 38F03B332576624C00E026A2 /* TrackerManagerTests.swift in Sources */, + 53BFCD332CFF30C800A3287A /* MockPrebidMobilePluginRenderer.swift in Sources */, 925D5E382737EF0400A8A2B5 /* RewardedVideo_CompanionTest.swift in Sources */, 539D4C7029B5DDFD00B6B30E /* SafariOpenerTests.swift in Sources */, 539D4C7229B5E23600B6B30E /* MockPBMAbstractCreative.swift in Sources */, @@ -3870,7 +3886,6 @@ FAA2990C243368A1002ACBF2 /* AdUnitSuccessorObjCTests.m in Sources */, 925D5E4B2737F1AA00A8A2B5 /* TestStringExtension.swift in Sources */, 925D5DB52737C57C00A8A2B5 /* MockPrimaryAdRequester.swift in Sources */, - 923B5521274536B400E00C88 /* MockMediationUtils.swift in Sources */, 922AFC8D27352BE900732C53 /* MockViewController.swift in Sources */, 922AFD2B2737258200732C53 /* PBMCreativeViewabilityTrackerTest.swift in Sources */, 922AFCF927370B9800732C53 /* LogObjCTest.m in Sources */, @@ -3905,7 +3920,6 @@ 929638BD27AB20FB00D30F3D /* NativeDataTests.swift in Sources */, 925D5D762737B85400A8A2B5 /* PBMVastCreativeCompanionAdsTest.swift in Sources */, 534C61462CB562E40026119A /* CloseActionManagerTests.swift in Sources */, - 925D5E222737EA1E00A8A2B5 /* MediationBannerAdUnitTest.swift in Sources */, 925D5E6C2737F4DF00A8A2B5 /* PBMVastLoaderTestOMVerificationOneInline.swift in Sources */, 925D5D912737C0A300A8A2B5 /* PBMUIViewTests.swift in Sources */, 9743CB84235F1CDB002E2CAA /* NativeAssetTests.swift in Sources */, @@ -3935,7 +3949,6 @@ 922AFCC727353B6300732C53 /* MockServer.m in Sources */, 922AFD13273718B400732C53 /* PBMAdUnitConfigTest.swift in Sources */, 5355ACAB29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift in Sources */, - 927ADB2728058196006EB8D5 /* MediationUtilsTest.swift in Sources */, 922AFD47273729B700732C53 /* PBMHTMLCreativeTest_MRAIDResize.swift in Sources */, 922AFCFD27370D8100732C53 /* PBMMRAIDControllerObjCTest.m in Sources */, 925D5E142737E74D00A8A2B5 /* PBMBidResponseTransformerTest.swift in Sources */, @@ -3994,7 +4007,6 @@ 925D5D962737C15E00A8A2B5 /* ParameterBuilderServiceTest.swift in Sources */, 925D5E742737F5AB00A8A2B5 /* PBMVideoCreativeTest.swift in Sources */, 925D5D6A2737B5BC00A8A2B5 /* ServerConnectionWithMockServer.swift in Sources */, - 925D5E292737EC9400A8A2B5 /* MediationRewardedAdUnitTest.swift in Sources */, 47D9A16ECCE6B2FB6D603053 /* MockCTTelephonyNetworkInfo.swift in Sources */, 922AFC8827352B0100732C53 /* MockUserAgentService.swift in Sources */, 925D5E702737F55800A8A2B5 /* PBMVastParserTests.swift in Sources */, @@ -4030,6 +4042,11 @@ 9285B46C27AAC9C1009BD3D3 /* NativeAdMarkupTest.swift in Sources */, 922AFC9327352CEB00732C53 /* MockPBMVideoCreative.swift in Sources */, 929638C127AB224400D30F3D /* NativeEventTrackerResponseTests.swift in Sources */, + 53CDA48D2D11AFD5004766F8 /* MediationBannerAdUnitTest.swift in Sources */, + 53CDA48E2D11AFD5004766F8 /* MockMediationUtils.swift in Sources */, + 53CDA48F2D11AFD5004766F8 /* MediationInterstitialAdUnitTest.swift in Sources */, + 53CDA4902D11AFD5004766F8 /* MediationUtilsTest.swift in Sources */, + 53CDA4912D11AFD5004766F8 /* MediationRewardedAdUnitTest.swift in Sources */, 47D9A59A1013D9036158B7E3 /* MockModalManager.swift in Sources */, 925D5E122737E04B00A8A2B5 /* PBMBidRequesterTest.swift in Sources */, 925D5E332737EE1C00A8A2B5 /* PBMTransactionTests.swift in Sources */, @@ -4081,7 +4098,6 @@ 922AFC9127352C9B00732C53 /* MockPBMWebView.swift in Sources */, 922AFCEE2736FC8600732C53 /* XCTest.swift in Sources */, 925D5E2D2737ECEA00A8A2B5 /* PBMWinNotifierTest.swift in Sources */, - 925D5E272737EC5E00A8A2B5 /* MediationInterstitialAdUnitTest.swift in Sources */, 922AFD062737120B00732C53 /* ImpressionTasksExecutorTest.swift in Sources */, 925D5E6E2737F51E00A8A2B5 /* PBMVastLoaderTestWrapperPlusInline.swift in Sources */, 922AFCD327353C5700732C53 /* MockServerMimeType.m in Sources */, @@ -4104,7 +4120,6 @@ FAEE4D20262DC2B200AD9966 /* Signals.swift in Sources */, 5BC37A79271F1D0000444D5E /* PBMVastTransactionFactory.m in Sources */, 5BC37911271F1CFF00444D5E /* PBMDeepLinkPlus.m in Sources */, - A9A3396B2AC57193006AD0E7 /* PBMAdViewDelegate.swift in Sources */, 5BC378DD271F1CFF00444D5E /* PBMORTBPmp.m in Sources */, 5BC378CE271F1CFF00444D5E /* PBMTouchDownRecognizer.m in Sources */, 5BC378ED271F1CFF00444D5E /* PBMORTBAppExtPrebid.m in Sources */, @@ -4264,13 +4279,13 @@ FAEE4D12262DC2B200AD9966 /* RewardedVideoAdUnit.swift in Sources */, 5BC3795B271F1D0000444D5E /* PBMVastCreativeCompanionAdsCompanion.m in Sources */, 53A657B02A8B64C200AE0B4F /* PBMORTBSDKConfiguration.m in Sources */, + 53514CF22D0851DE00A480C0 /* InterstitialControllerInteractionDelegate.swift in Sources */, 92C85D5C27A96C680080BAC5 /* NativeImage.swift in Sources */, 5BC37A86271F1D0000444D5E /* BannerView.swift in Sources */, 5BC3796E271F1D0000444D5E /* PBMVastWrapperAd.m in Sources */, 537B6518283372FD008AE9D1 /* PathBuilder.swift in Sources */, FAEE4D1A262DC2B200AD9966 /* NativeAd.swift in Sources */, 5BC37A1B271F1D0000444D5E /* Prebid.swift in Sources */, - F95DA2072C87567D0068A2BB /* SampleCustomRenderer.swift in Sources */, 5BC379A8271F1D0000444D5E /* PBMModalViewController.m in Sources */, 5BC37AC3271F1D0100444D5E /* PBMURLComponents.m in Sources */, 5BC378D3271F1CFF00444D5E /* PBMORTBAbstract.m in Sources */, @@ -4324,7 +4339,6 @@ 5BC37AB9271F1D0000444D5E /* PBMOpenMeasurementWrapper.m in Sources */, 5BC37A55271F1D0000444D5E /* PBMBidResponseTransformer.m in Sources */, 53F35F6D292B9F2F001C1183 /* UserConsentDataManager.swift in Sources */, - 5BC379DD271F1D0000444D5E /* InterstitialControllerInteractionDelegate.swift in Sources */, 5BC378C3271F1CFF00444D5E /* NSString+PBMExtensions.m in Sources */, 5BC37903271F1CFF00444D5E /* PBMORTBVideo.m in Sources */, 5BC37914271F1CFF00444D5E /* PBMCircularProgressBarView.m in Sources */, @@ -4408,6 +4422,8 @@ 5BC37ABD271F1D0000444D5E /* PBMOpenMeasurementEventTracker.m in Sources */, 5BC37927271F1CFF00444D5E /* PBMFunctions.m in Sources */, 53842BB829E561750069A4B7 /* PrebidImagesRepository.swift in Sources */, + 53BFCD312CFE435D00A3287A /* PrebidMobilePluginRenderer+Extensions.swift in Sources */, + 53BFCD112CF9156000A3287A /* PrebidMobileInterstitialControllerProtocol.swift in Sources */, 92C85D5A27A96AC50080BAC5 /* NativeTitle.swift in Sources */, 5BC37AA4271F1D0000444D5E /* PBMAdLoadFlowController.m in Sources */, 5BC378C0271F1CFF00444D5E /* NSDictionary+PBMExtensions.m in Sources */, diff --git a/PrebidMobile/BuildFiles/PrebidMobile.modulemap b/PrebidMobile/BuildFiles/PrebidMobile.modulemap index 24d74803c..8a2dce061 100644 --- a/PrebidMobile/BuildFiles/PrebidMobile.modulemap +++ b/PrebidMobile/BuildFiles/PrebidMobile.modulemap @@ -173,6 +173,8 @@ framework module PrebidMobile { header "PBMInterstitialAdLoader.h" header "PBMInterstitialAdLoaderDelegate.h" + header "PrebidMobileDisplayViewProtocol.h" + header "OMSDKVersionProvider.h" header "NSObject+PBMExtensions.h" } diff --git a/PrebidMobile/BuildFiles/PrebidMobileSwiftHeaders.h b/PrebidMobile/BuildFiles/PrebidMobileSwiftHeaders.h index be5a45fcb..4a0198640 100644 --- a/PrebidMobile/BuildFiles/PrebidMobileSwiftHeaders.h +++ b/PrebidMobile/BuildFiles/PrebidMobileSwiftHeaders.h @@ -27,6 +27,8 @@ #import "PBMAdLoaderProtocol.h" #import "PBMBannerAdLoader.h" +#import "PrebidMobileDisplayViewProtocol.h" + // Bid #import "PBMORTBBid.h" #import "PBMORTBBidExt.h" diff --git a/PrebidMobile/ConfigurationAndTargeting/Prebid.swift b/PrebidMobile/ConfigurationAndTargeting/Prebid.swift index db6438197..f93c18202 100644 --- a/PrebidMobile/ConfigurationAndTargeting/Prebid.swift +++ b/PrebidMobile/ConfigurationAndTargeting/Prebid.swift @@ -258,15 +258,15 @@ public class Prebid: NSObject { timeoutMillis = defaultTimeoutMillis } - public static func registerPluginRenderer(_ prebidMobilePluginRenderer: PrebidMobilePluginRenderer) { - PrebidMobilePluginRegister.shared.registerPlugin(prebidMobilePluginRenderer); + public static func registerPluginRenderer(_ pluginRenderer: PrebidMobilePluginRenderer) { + PrebidMobilePluginRegister.shared.registerPlugin(pluginRenderer) } - public static func unregisterPluginRenderer(_ prebidMobilePluginRenderer: PrebidMobilePluginRenderer) { - PrebidMobilePluginRegister.shared.unregisterPlugin(prebidMobilePluginRenderer); + public static func unregisterPluginRenderer(_ pluginRenderer: PrebidMobilePluginRenderer) { + PrebidMobilePluginRegister.shared.unregisterPlugin(pluginRenderer) } - public static func containsPluginRenderer(_ prebidMobilePluginRenderer: PrebidMobilePluginRenderer) { - PrebidMobilePluginRegister.shared.containsPlugin(prebidMobilePluginRenderer); + public static func containsPluginRenderer(_ pluginRenderer: PrebidMobilePluginRenderer) -> Bool { + PrebidMobilePluginRegister.shared.containsPlugin(pluginRenderer) } } diff --git a/PrebidMobile/ConfigurationAndTargeting/PrebidSDKInitializer.swift b/PrebidMobile/ConfigurationAndTargeting/PrebidSDKInitializer.swift index 04781787e..b77c222af 100644 --- a/PrebidMobile/ConfigurationAndTargeting/PrebidSDKInitializer.swift +++ b/PrebidMobile/ConfigurationAndTargeting/PrebidSDKInitializer.swift @@ -28,7 +28,10 @@ class PrebidSDKInitializer { let _ = UserConsentDataManager.shared PrebidJSLibraryManager.shared.downloadLibraries() + serverStatusRequester.requestStatus { completion?($0, $1) } + + Prebid.registerPluginRenderer(PrebidRenderer()) } // check for deprecated `GADMobileAds.sdkVersion` diff --git a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequestExtPrebid.h b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequestExtPrebid.h index 0e1a79d67..c2ed8029d 100644 --- a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequestExtPrebid.h +++ b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequestExtPrebid.h @@ -32,13 +32,11 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, nullable) NSArray *> *storedBidResponses; -@property (nonatomic, strong, nullable) NSArray *> *sdkRenderers; - @property (nonatomic, strong, nullable) NSMutableDictionary *cache; @property (nonatomic, strong) PBMMutableJsonDictionary *targeting; -@property (nonatomic, strong, nullable) PBMJsonDictionary *sdk; +@property (nonatomic, strong, nullable) NSArray *sdkRenderers; @end diff --git a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequestExtPrebid.m b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequestExtPrebid.m index 9ead83ca4..9d4bee9ce 100644 --- a/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequestExtPrebid.m +++ b/PrebidMobile/PrebidMobileRendering/ORTB/PBMORTBBidRequestExtPrebid.m @@ -48,8 +48,10 @@ - (nonnull PBMJsonDictionary *)toJsonDictionary { PBMMutableJsonDictionary * const storedRequest = [PBMMutableJsonDictionary new]; ret[@"storedrequest"] = storedRequest; storedRequest[@"id"] = self.storedRequestID; - if (self.sdk != nil) { - ret[@"sdk"] = self.sdk; + + if (self.sdkRenderers != nil && self.sdkRenderers.count > 0) { + NSDictionary * sdk = @{ @"renderers" : self.sdkRenderers }; + ret[@"sdk"] = sdk; } ret[@"targeting"] = self.targeting; diff --git a/PrebidMobile/PrebidMobileRendering/PluginRenderer/PBMAdViewDelegate.swift b/PrebidMobile/PrebidMobileRendering/PluginRenderer/PBMAdViewDelegate.swift deleted file mode 100644 index 93ccf278f..000000000 --- a/PrebidMobile/PrebidMobileRendering/PluginRenderer/PBMAdViewDelegate.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// PBMAdViewDelegate.swift -// PrebidMobile -// -// Created by Paul NICOLAS on 28/09/2023. -// Copyright © 2023 AppNexus. All rights reserved. -// - -import UIKit - -@objc public protocol PBMThirdPartyAdViewLoader: NSObjectProtocol { -} - -public typealias PBMAdViewDelegate = PBMThirdPartyAdViewLoader & PBMAdViewManagerDelegate & PBMModalManagerDelegate diff --git a/PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRegister.swift b/PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRegister.swift index ef41871b1..0972ec925 100644 --- a/PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRegister.swift +++ b/PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRegister.swift @@ -1,35 +1,49 @@ /*   Copyright 2018-2021 Prebid.org, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ import Foundation /// Global singleton responsible to store plugin renderer instances -@objc public class PrebidMobilePluginRegister: NSObject { - @objc public static let shared = PrebidMobilePluginRegister() +@objcMembers +public class PrebidMobilePluginRegister: NSObject { - private let queue = DispatchQueue(label: "PrebidMobilePluginRegisterQueue", attributes: .concurrent) - private var plugins = [String: PrebidMobilePluginRenderer]() + public static let shared = PrebidMobilePluginRegister() + + /// Default PrebidMobile SDK renderer + public var sdkRenderer: PrebidMobilePluginRenderer { + if let renderer = getPluginRenderer(for: PREBID_MOBILE_RENDERER_NAME) { + return renderer + } + + Log.error("SDK couldn't find \(PREBID_MOBILE_RENDERER_NAME) in plugin register. The new instance of \(PREBID_MOBILE_RENDERER_NAME) will be created.") + return PrebidRenderer() + } + + private let queue = DispatchQueue( + label: "PrebidMobilePluginRegisterQueue", + attributes: .concurrent + ) - private let defaultRenderer = PrebidRenderer() + private var plugins = [String: PrebidMobilePluginRenderer]() private override init() { super.init() } /// Register plugin as renderer - @objc public func registerPlugin(_ renderer: PrebidMobilePluginRenderer) { + public func registerPlugin(_ renderer: PrebidMobilePluginRenderer) { let rendererName = renderer.name queue.async(flags: .barrier) { [weak self] in @@ -41,81 +55,82 @@ import Foundation } } - @objc public func unregisterPlugin(_ renderer: PrebidMobilePluginRenderer) { + public func unregisterPlugin(_ renderer: PrebidMobilePluginRenderer) { queue.async(flags: .barrier) { [weak self] in self?.plugins.removeValue(forKey: renderer.name) } } /// Contains plugin - @objc public func containsPlugin(_ renderer: PrebidMobilePluginRenderer) -> Bool { + public func containsPlugin(_ renderer: PrebidMobilePluginRenderer) -> Bool { queue.sync { plugins.contains { $0.value === renderer } } } - private func getPluginRenderer(for key: String) -> PrebidMobilePluginRenderer? { - queue.sync { - plugins[key] - } - } - /// Register event delegate - @objc public func registerEventDelegate(_ pluginEventDelegate: PluginEventDelegate, adUnitConfigFingerprint: String) { + public func registerEventDelegate( + _ pluginEventDelegate: PluginEventDelegate, + adUnitConfigFingerprint: String + ) { queue.async(flags: .barrier) { [plugins] in plugins .values .forEach { - $0.registerEventDelegate?(pluginEventDelegate: pluginEventDelegate, adUnitConfigFingerprint: adUnitConfigFingerprint) + $0.registerEventDelegate?( + pluginEventDelegate: pluginEventDelegate, + adUnitConfigFingerprint: adUnitConfigFingerprint + ) } } } /// Unregister event delegate - @objc public func unregisterEventDelegate(_ pluginEventDelegate: PluginEventDelegate, adUnitConfigFingerprint: String) { + public func unregisterEventDelegate( + _ pluginEventDelegate: PluginEventDelegate, + adUnitConfigFingerprint: String + ) { queue.async(flags: .barrier) { [plugins] in plugins .values .forEach { - $0.unregisterEventDelegate?(pluginEventDelegate: pluginEventDelegate, adUnitConfigFingerprint: adUnitConfigFingerprint) + $0.unregisterEventDelegate?( + pluginEventDelegate: pluginEventDelegate, + adUnitConfigFingerprint: adUnitConfigFingerprint + ) } } } - /// Returns the list of available renderers for the given ad unit for RTB request - @objc public func getRTBListOfRenderersFor(for adUnit: AdUnitConfig) -> [PrebidMobilePluginRenderer] { - queue.sync { - plugins - .values - .filter { renderer in - adUnit.adFormats.contains { format in - renderer.isSupportRendering(for: format) - } - } + /// Returns the registered renderer according to the preferred renderer name in the bid response. + /// If no preferred renderer is found, it returns PrebidRenderer to perform default behavior. + /// Once bid is win we want to resolve the best PluginRenderer candidate to render the ad. + public func getPluginForPreferredRenderer(bid: Bid) -> PrebidMobilePluginRenderer { + guard let preferredRendererName = bid.pluginRendererName, + let preferredPlugin = getPluginRenderer(for: preferredRendererName), + preferredPlugin.version == bid.pluginRendererVersion + else { + return sdkRenderer } + + return preferredPlugin } - /// Returns the registered renderer according to the preferred renderer name in the bid response - /// If no preferred renderer is found, it returns PrebidRenderer to perform default behavior - /// Once bid is win we want to resolve the best PluginRenderer candidate to render the ad - @objc public func getPluginForPreferredRenderer(bid: Bid) -> PrebidMobilePluginRenderer { - if let preferredRendererName = bid.getPreferredPluginRendererName(), - let preferredPlugin = getPluginRenderer(for: preferredRendererName), - preferredPlugin.version == bid.getPreferredPluginRendererVersion(), - preferredPlugin.isSupportRendering(for: bid.adFormat) { - return preferredPlugin - } else { - return defaultRenderer + public func getAllPlugins() -> [PrebidMobilePluginRenderer] { + queue.sync { + return plugins.isEmpty ? [] : Array(plugins.values) } } - @objc public func getAllPlugins() -> [PrebidMobilePluginRenderer] { - queue.sync { - if plugins.isEmpty { - return [] - } - let allPlugins = Array(plugins.values) - return allPlugins - } + public func getAllPluginsJSONRepresentation() -> [[String: Any]] { + return PrebidMobilePluginRegister + .shared + .getAllPlugins() + .filter({ $0.name != PREBID_MOBILE_RENDERER_NAME }) + .map { $0.jsonDictionary() } + } + + private func getPluginRenderer(for key: String) -> PrebidMobilePluginRenderer? { + queue.sync { plugins[key] } } } diff --git a/PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRenderer+Extensions.swift b/PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRenderer+Extensions.swift new file mode 100644 index 000000000..ad0317e19 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRenderer+Extensions.swift @@ -0,0 +1,29 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import Foundation + +extension PrebidMobilePluginRenderer { + + public func jsonDictionary() -> [String: Any] { + var result = [String: Any]() + + result["name"] = name + result["version"] = version + result["data"] = data + + return result + } +} diff --git a/PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRenderer.swift b/PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRenderer.swift index 4f9377fb7..65e708ad6 100644 --- a/PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRenderer.swift +++ b/PrebidMobile/PrebidMobileRendering/PluginRenderer/PrebidMobilePluginRenderer.swift @@ -1,53 +1,76 @@ -/*   Copyright 2018-2021 Prebid.org, Inc. +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +import UIKit - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation - -@objc public protocol PrebidMobilePluginRenderer: AnyObject { +/// A protocol for the plugin renderer, defining the basic interface that any renderer should implement. +/// This protocol provides the ability to retrieve plugin details, support rendering formats, and manage event delegates and +/// methods for creating custom banner view and insterstitial controller. +@objc +public protocol PrebidMobilePluginRenderer: AnyObject { + /// The name of the plugin renderer. This is used to identify the plugin. @objc var name: String { get } - @objc var version: String { get } - @objc var data: [AnyHashable: Any]? { get } - /// Creates and returns Banner View for a given Bid Response - /// Returns nil in the case of an internal error - @objc func createBannerAdView(with frame: CGRect, bid: Bid, adConfiguration: AdUnitConfig, - connection: PrebidServerConnectionProtocol, adViewDelegate: (any PBMAdViewDelegate)?) + /// The version of the plugin renderer. + @objc var version: String { get } - /// Creates and returns an implementation of PrebidMobileInterstitialControllerInterface for a given bid response - /// Returns nil in the case of an internal error - @objc optional func createInterstitialController( bid: Bid, adConfiguration: AdUnitConfig, - connection: PrebidServerConnectionProtocol, - adViewManagerDelegate: InterstitialController?, - videoControlsConfig: VideoControlsConfiguration?) - - /// Returns true only if the given ad unit could be renderer by the plugin - @objc func isSupportRendering(for format: AdFormat?) -> Bool + /// Custom data to be included in the ORTB request. + @objc var data: [String: Any]? { get } - /// Register a listener related to a specific ad unit config fingerprint in order to dispatch specific ad events - @objc optional func registerEventDelegate(pluginEventDelegate: PluginEventDelegate, - adUnitConfigFingerprint: String) - - /// Unregister a listener related to a specific ad unit config fingerprint in order to dispatch specific ad events - @objc optional func unregisterEventDelegate(pluginEventDelegate: PluginEventDelegate, - adUnitConfigFingerprint: String) + /// Register a listener related to a specific ad unit config fingerprint in order to dispatch specific ad events. + @objc optional func registerEventDelegate( + pluginEventDelegate: PluginEventDelegate, + adUnitConfigFingerprint: String + ) - /// Setup a bid for a given ad unit configuration - @objc func setupBid(_ bid: PrebidMobile.Bid, adConfiguration: PrebidMobile.AdUnitConfig, - connection: PrebidServerConnectionProtocol) - + /// Unregister a listener related to a specific ad unit config fingerprint in order to dispatch specific ad events. + @objc optional func unregisterEventDelegate( + pluginEventDelegate: PluginEventDelegate, + adUnitConfigFingerprint: String + ) + /// Creates and returns an ad view conforming to `PrebidMobileDisplayViewManagerProtocol` for a given bid response. + /// Returns nil in the case of an internal error or if no renderer is provided. + /// + /// - Parameters: + /// - frame: The frame specifying the initial size and position of the ad view. + /// - bid: The `Bid` object containing the bid response used for rendering the ad. + /// - adConfiguration: The `AdUnitConfig` instance providing configuration details for the ad unit. + /// - loadingDelegate: The delegate conforming to `DisplayViewLoadingDelegate` for handling ad loading events. + /// - interactionDelegate: The delegate conforming to `DisplayViewInteractionDelegate` for handling ad interaction events. + @objc func createBannerView( + with frame: CGRect, + bid: Bid, + adConfiguration: AdUnitConfig, + loadingDelegate: DisplayViewLoadingDelegate, + interactionDelegate: DisplayViewInteractionDelegate + ) -> (UIView & PrebidMobileDisplayViewProtocol)? + /// Creates and returns an implementation of `PrebidMobileInterstitialControllerProtocol` for a given bid response. + /// Returns nil in the case of an internal error or if no renderer is provided. + /// + /// - Parameters: + /// - bid: The `Bid` object containing the bid response used for rendering the interstitial ad. + /// - adConfiguration: The `AdUnitConfig` instance providing configuration details for the ad unit. + /// - loadingDelegate: The delegate for handling interstitial ad loading events. + /// - interactionDelegate: The delegate for handling user interactions with the interstitial ad. + @objc func createInterstitialController( + bid: Bid, + adConfiguration: AdUnitConfig, + loadingDelegate: InterstitialControllerLoadingDelegate, + interactionDelegate: InterstitialControllerInteractionDelegate + ) -> PrebidMobileInterstitialControllerProtocol? } diff --git a/PrebidMobile/PrebidMobileRendering/PluginRenderer/SampleCustomRenderer.swift b/PrebidMobile/PrebidMobileRendering/PluginRenderer/SampleCustomRenderer.swift deleted file mode 100644 index 8ff5a684e..000000000 --- a/PrebidMobile/PrebidMobileRendering/PluginRenderer/SampleCustomRenderer.swift +++ /dev/null @@ -1,83 +0,0 @@ -/*   Copyright 2018-2021 Prebid.org, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import Foundation -import UIKit - -public class SampleCustomRenderer: NSObject, PrebidMobilePluginRenderer { - - public let name = "SampleCustomRenderer" - - public let version = "1.0.0" - - public var data: [AnyHashable: Any]? = nil - - private var adViewManager: PBMAdViewManager? - - public func isSupportRendering(for format: AdFormat?) -> Bool { - AdFormat.allCases.contains(where: { $0 == format }) - } - - public func setupBid(_ bid: Bid, adConfiguration: AdUnitConfig, connection: PrebidServerConnectionProtocol) { - - } - - public func createBannerAdView(with frame: CGRect, bid: Bid, adConfiguration: AdUnitConfig, - connection: PrebidServerConnectionProtocol, adViewDelegate: (any PBMAdViewDelegate)?) { - DispatchQueue.main.async { - if let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }), - let rootView = window.rootViewController?.view { - if let prebidBannerView = self.findPrebidBannerView(in: rootView) { - print("Found PrebidBannerView: \(prebidBannerView)") - - let label = UILabel() - label.text = "Prebid SDK - Custom Renderer" - label.textAlignment = .center - label.textColor = .black - label.backgroundColor = .yellow - label.translatesAutoresizingMaskIntoConstraints = false - - prebidBannerView.addSubview(label) - - NSLayoutConstraint.activate([ - label.centerXAnchor.constraint(equalTo: prebidBannerView.centerXAnchor), - label.centerYAnchor.constraint(equalTo: prebidBannerView.centerYAnchor), - label.widthAnchor.constraint(equalTo: prebidBannerView.widthAnchor), - label.heightAnchor.constraint(equalTo: prebidBannerView.heightAnchor) - ]) - - } else { - print("PrebidBannerView not found.") - } - } - } - } - - private func findPrebidBannerView(in view: UIView) -> UIView? { - if view.accessibilityIdentifier == "PrebidBannerView" { - return view - } - for subview in view.subviews { - if let foundView = findPrebidBannerView(in: subview) { - return foundView - } - } - return nil - } - - public func createInterstitialController(bid: Bid, adConfiguration: AdUnitConfig, connection: PrebidServerConnectionProtocol, - adViewManagerDelegate adViewDelegate: InterstitialController?, videoControlsConfig: VideoControlsConfiguration?) { - } -} diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/BannerAdLoaderDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/BannerAdLoaderDelegate.swift index 42042fd37..73a262a94 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/BannerAdLoaderDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/BannerAdLoaderDelegate.swift @@ -24,9 +24,4 @@ import UIKit @objc func bannerAdLoader(_ bannerAdLoader: PBMBannerAdLoader, loadedAdView adView: UIView, adSize: CGSize) - - // Hook to insert interaction delegate - @objc func bannerAdLoader(_ bannerAdLoader: PBMBannerAdLoader, - createdDisplayView displayView: PBMDisplayView) - } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMBannerAdLoader.m b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMBannerAdLoader.m index 9d33cce63..184ec77c4 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMBannerAdLoader.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMBannerAdLoader.m @@ -30,12 +30,11 @@ #import #endif - @interface PBMBannerAdLoader () -@property (nonatomic, weak, nullable, readonly) id delegate; -@end +@property (nonatomic, weak, nullable, readonly) id delegate; +@end @implementation PBMBannerAdLoader @@ -43,11 +42,13 @@ @implementation PBMBannerAdLoader // MARK: - Lifecycle -- (instancetype)initWithDelegate:(id)delegate { +- (instancetype)initWithDelegate:(id)delegate { if (!(self = [super init])) { return nil; } + _delegate = delegate; + return self; } @@ -62,21 +63,20 @@ - (instancetype)initWithDelegate:(id)delegate { - (void)createPrebidAdWithBid:(Bid *)bid adUnitConfig:(AdUnitConfig *)adUnitConfig adObjectSaver:(void (^)(id))adObjectSaver - loadMethodInvoker:(void (^)(dispatch_block_t))loadMethodInvoker -{ - CGRect const displayFrame = CGRectMake(0, 0, bid.size.width, bid.size.height); - PBMDisplayView * const newDisplayView = [[PBMDisplayView alloc] initWithFrame:displayFrame - bid:bid - adConfiguration:adUnitConfig]; - adObjectSaver(newDisplayView); + loadMethodInvoker:(void (^)(dispatch_block_t))loadMethodInvoker { @weakify(self); - loadMethodInvoker(^{ + dispatch_async(dispatch_get_main_queue(), ^{ @strongify(self); if (!self) { return; } - newDisplayView.loadingDelegate = self; - [self.delegate bannerAdLoader:self createdDisplayView:newDisplayView]; - [newDisplayView displayAd]; + UIView * displayView = [self createBannerViewWithBid:bid + adUnitConfig:adUnitConfig]; + + adObjectSaver(displayView); + + loadMethodInvoker(^{ + [displayView loadAd]; + }); }); } @@ -108,4 +108,30 @@ - (void)failedWithError:(nullable NSError *)error { [self.flowDelegate adLoader:self failedWithPrimarySDKError:error]; } +- (UIView *)createBannerViewWithBid:(Bid *)bid adUnitConfig:(AdUnitConfig *)adUnitConfig { + id renderer = [[PrebidMobilePluginRegister shared] getPluginForPreferredRendererWithBid:bid]; + PBMLogInfo(@"Renderer: %@", renderer); + + CGRect const displayFrame = CGRectMake(0, 0, bid.size.width, bid.size.height); + + UIView * displayView = [renderer createBannerViewWith:displayFrame + bid:bid + adConfiguration:adUnitConfig + loadingDelegate:self + interactionDelegate:self.delegate]; + + if (displayView) { + return displayView; + } + + PBMLogWarn(@"SDK couldn't retrieve an implementation of PrebidMobileDisplayViewManagerProtocol. SDK will use the PrebidMobile SDK renderer."); + + id sdkRenderer = PrebidMobilePluginRegister.shared.sdkRenderer; + return [sdkRenderer createBannerViewWith:displayFrame + bid:bid + adConfiguration:adUnitConfig + loadingDelegate:self + interactionDelegate:self.delegate]; +} + @end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.h b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.h index 761d76898..2233df880 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.h +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.h @@ -18,6 +18,7 @@ #import "PBMAdLoaderProtocol.h" @protocol PBMInterstitialAdLoaderDelegate; +@protocol PrebidMobileInterstitialControllerProtocol; NS_ASSUME_NONNULL_BEGIN diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.m b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.m index 730ce78f3..a20cd9898 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/AdLoading/PBMInterstitialAdLoader.m @@ -30,7 +30,7 @@ @interface PBMInterstitialAdLoader () -@property (nonatomic, weak, nullable, readonly) id delegate; +@property (nonatomic, weak, nullable, readonly) id delegate; @property (nonatomic, weak, nullable, readonly) id eventHandler; @end @@ -41,9 +41,8 @@ @implementation PBMInterstitialAdLoader // MARK: - Lifecycle -- (instancetype)initWithDelegate:(id)delegate - eventHandler:(nonnull id)eventHandler { - +- (instancetype)initWithDelegate:(id)delegate + eventHandler:(nonnull id)eventHandler { if (!(self = [super init])) { return nil; } @@ -63,25 +62,25 @@ - (instancetype)initWithDelegate:(id)delegate - (void)createPrebidAdWithBid:(Bid *)bid adUnitConfig:(AdUnitConfig *)adUnitConfig adObjectSaver:(void (^)(id))adObjectSaver - loadMethodInvoker:(void (^)(dispatch_block_t))loadMethodInvoker -{ - InterstitialController * const controller = [[InterstitialController alloc] initWithBid:bid - adConfiguration:adUnitConfig]; - adObjectSaver(controller); + loadMethodInvoker:(void (^)(dispatch_block_t))loadMethodInvoker { @weakify(self); - loadMethodInvoker(^{ + dispatch_async(dispatch_get_main_queue(), ^{ @strongify(self); if (!self) { return; } - controller.loadingDelegate = self; - [self.delegate interstitialAdLoader:self createdInterstitialController:controller]; - [controller loadAd]; + id controller = [self createInterstitialControllerWithBid:bid + adUnitConfig:adUnitConfig]; + adObjectSaver(controller); + + loadMethodInvoker(^{ + [controller loadAd]; + }); }); } - (void)reportSuccessWithAdObject:(id)adObject adSize:(nullable NSValue *)adSize { - if ([adObject isKindOfClass:[InterstitialController class]]) { - InterstitialController * const controller = (InterstitialController *)adObject; + if ([adObject conformsToProtocol:@protocol(PrebidMobileInterstitialControllerProtocol)]) { + id controller = (id)adObject; [self.delegate interstitialAdLoader:self loadedAd:^(UIViewController *targetController) { [controller show]; @@ -110,11 +109,12 @@ - (void)reportSuccessWithAdObject:(id)adObject adSize:(nullable NSValue *)adSize // MARK: - InterstitialControllerLoadingDelegate -- (void)interstitialControllerDidLoadAd:(InterstitialController *)interstitialController { +- (void)interstitialControllerDidLoadAd:(id)interstitialController { [self.flowDelegate adLoaderLoadedPrebidAd:self]; } -- (void)interstitialController:(InterstitialController *)interstitialController didFailWithError:(NSError *)error { +- (void)interstitialController:(id)interstitialController + didFailWithError:(NSError *)error { [self.flowDelegate adLoader:self failedWithPrebidError:error]; } @@ -132,4 +132,27 @@ - (void)failedWithError:(nullable NSError *)error { [self.flowDelegate adLoader:self failedWithPrimarySDKError:error]; } +- (id)createInterstitialControllerWithBid:(Bid *)bid + adUnitConfig:(AdUnitConfig *)adUnitConfig { + id renderer = [[PrebidMobilePluginRegister shared] getPluginForPreferredRendererWithBid:bid]; + PBMLogInfo(@"Renderer: %@", renderer); + + id controller = [renderer createInterstitialControllerWithBid:bid + adConfiguration:adUnitConfig + loadingDelegate:self + interactionDelegate:self.delegate]; + + if (controller) { + return controller; + } + + PBMLogWarn(@"SDK couldn't retrieve an implementation of PrebidMobileInterstitialControllerProtocol. SDK will use the PrebidMobile SDK renderer."); + + id sdkRenderer = PrebidMobilePluginRegister.shared.sdkRenderer; + return [sdkRenderer createInterstitialControllerWithBid:bid + adConfiguration:adUnitConfig + loadingDelegate:self + interactionDelegate:self.delegate]; +} + @end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift index 499acd271..2bea8a461 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BannerView.swift @@ -18,11 +18,13 @@ import UIKit fileprivate let assertionMessageMainThread = "Expected to only be called on the main thread" /// The view that will display the particular banner ad. Built for rendering type of integration. -public class BannerView: UIView, - BannerAdLoaderDelegate, - AdLoadFlowControllerDelegate, - BannerEventInteractionDelegate, - DisplayViewInteractionDelegate { +@objcMembers +public class BannerView: + UIView, + BannerAdLoaderDelegate, + AdLoadFlowControllerDelegate, + BannerEventInteractionDelegate, + DisplayViewInteractionDelegate { /// The ad unit configuration. public let adUnitConfig: AdUnitConfig @@ -33,45 +35,45 @@ public class BannerView: UIView, // MARK: - Public Properties /// Banner-specific parameters. - @objc public var bannerParameters: BannerParameters { + public var bannerParameters: BannerParameters { get { adUnitConfig.adConfiguration.bannerParameters } } /// Video-specific parameters. - @objc public var videoParameters: VideoParameters { + public var videoParameters: VideoParameters { get { adUnitConfig.adConfiguration.videoParameters } } /// The last bid response received. - @objc public var lastBidResponse: BidResponse? { + public var lastBidResponse: BidResponse? { adLoadFlowController?.bidResponse } /// ID of Stored Impression on the Prebid server - @objc public var configID: String { + public var configID: String { adUnitConfig.configId } /// The interval for refreshing the ad. - @objc public var refreshInterval: TimeInterval { + public var refreshInterval: TimeInterval { get { adUnitConfig.refreshInterval } set { adUnitConfig.refreshInterval = newValue } } /// Additional sizes for the ad. - @objc public var additionalSizes: [CGSize]? { + public var additionalSizes: [CGSize]? { get { adUnitConfig.additionalSizes } set { adUnitConfig.additionalSizes = newValue } } /// The ad format (e.g., banner, video). - @objc public var adFormat: AdFormat { + public var adFormat: AdFormat { get { adUnitConfig.adFormats.first ?? .banner } set { adUnitConfig.adFormats = [newValue] } } /// The position of the ad on the screen. - @objc public var adPosition: AdPosition { + public var adPosition: AdPosition { get { adUnitConfig.adPosition } set { adUnitConfig.adPosition = newValue } } @@ -83,12 +85,7 @@ public class BannerView: UIView, } /// ORTB configuration string. - @objc public weak var delegate: BannerViewDelegate? - - /// Subscribe to plugin renderer events - @objc public func setPluginEventDelegate(_ pluginEventDelegate: PluginEventDelegate) { - PrebidMobilePluginRegister.shared.registerEventDelegate(pluginEventDelegate, adUnitConfigFingerprint: adUnitConfig.fingerprint) - } + public weak var delegate: BannerViewDelegate? // MARK: Readonly storage @@ -135,11 +132,12 @@ public class BannerView: UIView, /// - configID: The configuration ID for the ad unit. /// - adSize: The size of the ad. /// - eventHandler: The event handler for the banner view. - @objc public init(frame: CGRect, - configID: String, - adSize: CGSize, - eventHandler: BannerEventHandler) { - + public init( + frame: CGRect, + configID: String, + adSize: CGSize, + eventHandler: BannerEventHandler + ) { adUnitConfig = AdUnitConfig(configId: configID, size: adSize) adUnitConfig.adConfiguration.bannerParameters.api = PrebidConstants.supportedRenderingBannerAPISignals @@ -151,10 +149,12 @@ public class BannerView: UIView, adLoadFlowController = PBMAdLoadFlowController( bidRequesterFactory: { [adUnitConfig] config in - PBMBidRequester(connection: PrebidServerConnection.shared, - sdkConfiguration: Prebid.shared, - targeting: Targeting.shared, - adUnitConfiguration: adUnitConfig) + PBMBidRequester( + connection: PrebidServerConnection.shared, + sdkConfiguration: Prebid.shared, + targeting: Targeting.shared, + adUnitConfiguration: adUnitConfig + ) }, adLoader: bannerAdLoader, adUnitConfig: adUnitConfig, @@ -187,16 +187,19 @@ public class BannerView: UIView, /// - Parameters: /// - configID: The configuration ID for the ad unit. /// - eventHandler: The event handler for the banner view. - @objc public convenience init(configID: String, - eventHandler: BannerEventHandler) { - + public convenience init( + configID: String, + eventHandler: BannerEventHandler + ) { let size = eventHandler.adSizes.first ?? CGSize() let frame = CGRect(origin: CGPoint.zero, size: size) - self.init(frame: frame, - configID: configID, - adSize: size, - eventHandler: eventHandler) + self.init( + frame: frame, + configID: configID, + adSize: size, + eventHandler: eventHandler + ) if eventHandler.adSizes.count > 1 { self.additionalSizes = Array(eventHandler.adSizes.suffix(from: 1)) @@ -208,13 +211,17 @@ public class BannerView: UIView, /// - frame: The frame rectangle for the view. /// - configID: The configuration ID for the ad unit. /// - adSize: The size of the ad. - @objc public convenience init(frame: CGRect, - configID: String, - adSize: CGSize) { - self.init(frame: frame, - configID: configID, - adSize: adSize, - eventHandler: BannerEventHandlerStandalone()) + public convenience init( + frame: CGRect, + configID: String, + adSize: CGSize + ) { + self.init( + frame: frame, + configID: configID, + adSize: adSize, + eventHandler: BannerEventHandlerStandalone() + ) } deinit { @@ -226,13 +233,13 @@ public class BannerView: UIView, } /// Loads the ad for the banner view. - @objc public func loadAd() { + public func loadAd() { adLoadFlowController?.refresh() } /// Sets the stored auction response. /// - Parameter storedAuction: The stored auction response string. - @objc public func setStoredAuctionResponse(storedAuction:String){ + public func setStoredAuctionResponse(storedAuction:String){ Prebid.shared.storedAuctionResponse = storedAuction } @@ -251,12 +258,22 @@ public class BannerView: UIView, } /// Stops the auto-refresh of the ad. - @objc public func stopRefresh() { + public func stopRefresh() { adLoadFlowController?.enqueueGatedBlock { [weak self] in self?.isRefreshStopped = true } } + // MARK: Custom Renderer + + /// Subscribe to plugin renderer events + public func setPluginEventDelegate(_ pluginEventDelegate: PluginEventDelegate) { + PrebidMobilePluginRegister.shared.registerEventDelegate( + pluginEventDelegate, + adUnitConfigFingerprint: adUnitConfig.fingerprint + ) + } + // MARK: - Ext Data (imp[].ext.data) /// Adds context data for a specified key. @@ -264,7 +281,7 @@ public class BannerView: UIView, /// - data: The data to add. /// - key: The key associated with the data. @available(*, deprecated, message: "This method is deprecated. Please, use addExtData method instead.") - @objc public func addContextData(_ data: String, forKey key: String) { + public func addContextData(_ data: String, forKey key: String) { addExtData(key: key, value: data) } @@ -273,20 +290,20 @@ public class BannerView: UIView, /// - data: A set of data to update. /// - key: The key associated with the data. @available(*, deprecated, message: "This method is deprecated. Please, use updateExtData method instead.") - @objc public func updateContextData(_ data: Set, forKey key: String) { + public func updateContextData(_ data: Set, forKey key: String) { updateExtData(key: key, value: data) } /// Removes context data for a specified key. /// - Parameter key: The key associated with the data to remove. @available(*, deprecated, message: "This method is deprecated. Please, use removeExtData method instead.") - @objc public func removeContextDate(forKey key: String) { + public func removeContextDate(forKey key: String) { removeExtData(forKey: key) } /// Clears all context data. @available(*, deprecated, message: "This method is deprecated. Please, use clearExtData method instead.") - @objc public func clearContextData() { + public func clearContextData() { clearExtData() } @@ -294,7 +311,7 @@ public class BannerView: UIView, /// - Parameters: /// - key: The key for the data. /// - value: The value for the data. - @objc public func addExtData(key: String, value: String) { + public func addExtData(key: String, value: String) { adUnitConfig.addExtData(key: key, value: value) } @@ -302,19 +319,19 @@ public class BannerView: UIView, /// - Parameters: /// - key: The key for the data. /// - value: The value for the data. - @objc public func updateExtData(key: String, value: Set) { + public func updateExtData(key: String, value: Set) { adUnitConfig.updateExtData(key: key, value: value) } /// Removes ext data. /// - Parameters: /// - key: The key for the data. - @objc public func removeExtData(forKey: String) { + public func removeExtData(forKey: String) { adUnitConfig.removeExtData(for: forKey) } /// Clears ext data. - @objc public func clearExtData() { + public func clearExtData() { adUnitConfig.clearExtData() } @@ -323,50 +340,50 @@ public class BannerView: UIView, /// Adds a context keyword. /// - Parameter newElement: The keyword to add. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeyword method instead.") - @objc public func addContextKeyword(_ newElement: String) { + public func addContextKeyword(_ newElement: String) { addExtKeyword(newElement) } /// Adds a set of context keywords. /// - Parameter newElements: A set of keywords to add. @available(*, deprecated, message: "This method is deprecated. Please, use addExtKeywords method instead.") - @objc public func addContextKeywords(_ newElements: Set) { + public func addContextKeywords(_ newElements: Set) { addExtKeywords(newElements) } /// Removes a context keyword. /// - Parameter element: The keyword to remove. @available(*, deprecated, message: "This method is deprecated. Please, use removeExtKeyword method instead.") - @objc public func removeContextKeyword(_ element: String) { + public func removeContextKeyword(_ element: String) { removeExtKeyword(element) } /// Clears all context keywords. @available(*, deprecated, message: "This method is deprecated. Please, use clearExtKeywords method instead.") - @objc public func clearContextKeywords() { + public func clearContextKeywords() { clearExtKeywords() } /// Adds an extended keyword. /// - Parameter newElement: The keyword to be added. - @objc public func addExtKeyword(_ newElement: String) { + public func addExtKeyword(_ newElement: String) { adUnitConfig.addExtKeyword(newElement) } /// Adds multiple extended keywords. /// - Parameter newElements: A set of keywords to be added. - @objc public func addExtKeywords(_ newElements: Set) { + public func addExtKeywords(_ newElements: Set) { adUnitConfig.addExtKeywords(newElements) } /// Removes an extended keyword. /// - Parameter element: The keyword to be removed. - @objc public func removeExtKeyword(_ element: String) { + public func removeExtKeyword(_ element: String) { adUnitConfig.removeExtKeyword(element) } /// Clears all extended keywords. - @objc public func clearExtKeywords() { + public func clearExtKeywords() { adUnitConfig.clearExtKeywords() } @@ -374,29 +391,29 @@ public class BannerView: UIView, /// Sets the app content data. /// - Parameter appContent: The app content data. - @objc public func setAppContent(_ appContent: PBMORTBAppContent) { + public func setAppContent(_ appContent: PBMORTBAppContent) { adUnitConfig.setAppContent(appContent) } /// Clears the app content data. - @objc public func clearAppContent() { + public func clearAppContent() { adUnitConfig.clearAppContent() } /// Adds app content data objects. /// - Parameter dataObjects: The data objects to be added. - @objc public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { + public func addAppContentData(_ dataObjects: [PBMORTBContentData]) { adUnitConfig.addAppContentData(dataObjects) } /// Removes an app content data object. /// - Parameter dataObject: The data object to be removed. - @objc public func removeAppContentDataObject(_ dataObject: PBMORTBContentData) { + public func removeAppContentDataObject(_ dataObject: PBMORTBContentData) { adUnitConfig.removeAppContentData(dataObject) } /// Clears all app content data objects. - @objc public func clearAppContentDataObjects() { + public func clearAppContentDataObjects() { adUnitConfig.clearAppContentData() } @@ -404,25 +421,25 @@ public class BannerView: UIView, /// Adds user data objects. /// - Parameter userDataObjects: The user data objects to be added. - @objc public func addUserData(_ userDataObjects: [PBMORTBContentData]) { + public func addUserData(_ userDataObjects: [PBMORTBContentData]) { adUnitConfig.addUserData(userDataObjects) } /// Removes a user data object. /// - Parameter userDataObject: The user data object to be removed. - @objc public func removeUserData(_ userDataObject: PBMORTBContentData) { + public func removeUserData(_ userDataObject: PBMORTBContentData) { adUnitConfig.removeUserData(userDataObject) } /// Clears all user data objects. - @objc public func clearUserData() { + public func clearUserData() { adUnitConfig.clearUserData() } // MARK: - DisplayViewInteractionDelegate - public func trackImpression(forDisplayView: PBMDisplayView) { + public func trackImpression(forDisplayView: UIView) { guard let eventHandler = self.eventHandler, eventHandler.responds(to: #selector(BannerEventHandler.trackImpression)) else { return @@ -431,36 +448,41 @@ public class BannerView: UIView, eventHandler.trackImpression() } - public func viewControllerForModalPresentation(fromDisplayView: PBMDisplayView) -> UIViewController? { + public func viewControllerForModalPresentation( + fromDisplayView: UIView + ) -> UIViewController? { return viewControllerForPresentingModal } - public func didLeaveApp(from displayView: PBMDisplayView) { + public func didLeaveApp(from displayView: UIView) { willLeaveApp() } - public func willPresentModal(from displayView: PBMDisplayView) { + public func willPresentModal(from displayView: UIView) { willPresentModal() } - public func didDismissModal(from displayView: PBMDisplayView) { + public func didDismissModal(from displayView: UIView) { didDismissModal() } // MARK: - BannerAdLoaderDelegate - public func bannerAdLoader(_ bannerAdLoader: PBMBannerAdLoader, loadedAdView adView: UIView, adSize: CGSize) { + public func bannerAdLoader( + _ bannerAdLoader: PBMBannerAdLoader, + loadedAdView adView: UIView, + adSize: CGSize + ) { deployView(adView) reportLoadingSuccess(with: adSize) } - public func bannerAdLoader(_ bannerAdLoader: PBMBannerAdLoader, createdDisplayView displayView: PBMDisplayView) { - displayView.interactionDelegate = self - } - // MARK: - PBMAdLoadFlowControllerDelegate - public func adLoadFlowController(_ adLoadFlowController: PBMAdLoadFlowController, failedWithError error: Error?) { + public func adLoadFlowController( + _ adLoadFlowController: PBMAdLoadFlowController, + failedWithError error: Error? + ) { reportLoadingFailed(with: error) } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift index d56b10cfd..1ca836d5c 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnit.swift @@ -189,36 +189,36 @@ class BaseInterstitialAdUnit: // MARK: - InterstitialControllerInteractionDelegate /// Tracks an impression for the given interstitial controller. - public func trackImpression(forInterstitialController: InterstitialController) { + public func trackImpression(forInterstitialController: PrebidMobileInterstitialControllerProtocol) { DispatchQueue.main.async { self.delegate?.callEventHandler_trackImpression() } } /// Called when the ad in the interstitial controller is clicked. - public func interstitialControllerDidClickAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidClickAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { assert(Thread.isMainThread, "Expected to only be called on the main thread") delegate?.callDelegate_didClickAd() } /// Called when the ad in the interstitial controller is closed. - public func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { + public func interstitialControllerDidCloseAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { assert(Thread.isMainThread, "Expected to only be called on the main thread") delegate?.callDelegate_didDismissAd() } /// Called when the ad in the interstitial controller causes the app to leave. - public func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) { + public func interstitialControllerDidLeaveApp(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { assert(Thread.isMainThread, "Expected to only be called on the main thread") delegate?.callDelegate_willLeaveApplication() } - public func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) {} - public func interstitialControllerDidComplete(_ interstitialController: InterstitialController) {} - public func trackUserReward(_ interstitialController: InterstitialController, _ reward: PrebidReward) {} + public func interstitialControllerDidDisplay(_ interstitialController: PrebidMobileInterstitialControllerProtocol) {} + public func interstitialControllerDidComplete(_ interstitialController: PrebidMobileInterstitialControllerProtocol) {} + public func trackUserReward(_ interstitialController: PrebidMobileInterstitialControllerProtocol, _ reward: PrebidReward) {} public func viewControllerForModalPresentation( - fromInterstitialController: InterstitialController + fromInterstitialController: PrebidMobileInterstitialControllerProtocol ) -> UIViewController? { return targetController } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnitProtocol.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnitProtocol.swift index 308bdc29f..f2495680f 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnitProtocol.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseInterstitialAdUnitProtocol.swift @@ -13,12 +13,12 @@  limitations under the License.  */ -import Foundation import UIKit -@objc protocol BaseInterstitialAdUnitProtocol: NSObjectProtocol { +@objc +protocol BaseInterstitialAdUnitProtocol: NSObjectProtocol { - @objc func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) + @objc func interstitialControllerDidCloseAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) @objc func callDelegate_didReceiveAd() @objc func callDelegate_didFailToReceiveAd(with error: Error?) diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseRewardedAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseRewardedAdUnit.swift index 4d7f833db..98b2ba1dc 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseRewardedAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/BaseRewardedAdUnit.swift @@ -35,7 +35,10 @@ class BaseRewardedAdUnit: BaseInterstitialAdUnit, RewardedEventInteractionDelega // MARK: - InterstitialControllerInteractionDelegate - override func trackUserReward(_ interstitialController: InterstitialController, _ reward: PrebidReward) { + override func trackUserReward( + _ interstitialController: PrebidMobileInterstitialControllerProtocol, + _ reward: PrebidReward + ) { DispatchQueue.main.async { self.delegate?.callDelegate_rewardedAdUserDidEarnReward?(reward: reward) } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift index 2ee10b83d..689ab00dc 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/InterstitialRenderingAdUnit.swift @@ -372,7 +372,7 @@ public class InterstitialRenderingAdUnit: NSObject, BaseInterstitialAdUnitProtoc // MARK: - Internal methods - func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { + func interstitialControllerDidCloseAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { baseAdUnit.interstitialControllerDidCloseAd(interstitialController) } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift index 6f7cc158b..ac61f16dc 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedAdUnit.swift @@ -356,7 +356,7 @@ public class RewardedAdUnit: NSObject, BaseInterstitialAdUnitProtocol { // MARK: - Internal methods - func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) { + func interstitialControllerDidCloseAd(_ interstitialController: PrebidMobileInterstitialControllerProtocol) { baseAdUnit.interstitialControllerDidCloseAd(interstitialController) } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedConfig.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedConfig.swift index cabdbefbb..1228f3fbd 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedConfig.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/GAM/RewardedConfig.swift @@ -75,7 +75,7 @@ public class RewardedConfig: NSObject { private let ortbRewarded: PBMORTBRewardedConfiguration? - init(ortbRewarded: PBMORTBRewardedConfiguration?) { + public init(ortbRewarded: PBMORTBRewardedConfiguration?) { self.ortbRewarded = ortbRewarded } } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/PBMStandaloneSDK/EventHandlers/BannerEventHandlerStandalone.swift b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/PBMStandaloneSDK/EventHandlers/BannerEventHandlerStandalone.swift index 2122404c6..302e61637 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/PBMStandaloneSDK/EventHandlers/BannerEventHandlerStandalone.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/Integrations/PBMStandaloneSDK/EventHandlers/BannerEventHandlerStandalone.swift @@ -28,5 +28,6 @@ public class BannerEventHandlerStandalone: NSObject, BannerEventHandler { } public func trackImpression() { + } } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/DisplayViewInteractionDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/DisplayViewInteractionDelegate.swift index f5edeb030..17d82e7b1 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/DisplayViewInteractionDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/DisplayViewInteractionDelegate.swift @@ -13,19 +13,41 @@  limitations under the License.  */ -import Foundation import UIKit +/// A protocol that defines methods for handling user interactions and lifecycle events related to ad display views. +@objc +public protocol DisplayViewInteractionDelegate: NSObjectProtocol { -@objc public protocol DisplayViewInteractionDelegate: NSObjectProtocol { - - func trackImpression(forDisplayView:PBMDisplayView) + /// Tracks an impression for the specified display view. + /// + /// - Parameters: + /// - forDisplayView: The `UIView` instance associated with the ad impression. + @objc func trackImpression(forDisplayView: UIView) - func viewControllerForModalPresentation(fromDisplayView: PBMDisplayView) -> UIViewController? + /// Notifies that the user has left the app after interacting with the ad. + /// + /// - Parameters: + /// - displayView: The `UIView` instance associated with the ad interaction. + @objc func didLeaveApp(from displayView: UIView) - func didLeaveApp(from displayView: PBMDisplayView) + /// Notifies that a modal view is about to be presented from the specified display view. + /// + /// - Parameters: + /// - displayView: The `UIView` instance associated with the modal presentation. + @objc func willPresentModal(from displayView: UIView) - func willPresentModal(from displayView: PBMDisplayView) + /// Notifies the delegate that a modal view has been dismissed. + /// + /// - Parameters: + /// - displayView: The `UIView` instance associated with the dismissed modal. + @objc func didDismissModal(from displayView: UIView) - func didDismissModal(from displayView: PBMDisplayView) + /// Requests the `UIViewController` to be used for presenting modals from the specified display view. + /// + /// - Parameters: + /// - fromDisplayView: The `UIView` instance from which the modal is to be presented. + @objc func viewControllerForModalPresentation( + fromDisplayView: UIView + ) -> UIViewController? } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/DisplayViewLoadingDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/DisplayViewLoadingDelegate.swift index 1a6e418ee..2788e5c70 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/DisplayViewLoadingDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/DisplayViewLoadingDelegate.swift @@ -13,12 +13,23 @@  limitations under the License.  */ -import Foundation +import UIKit -@objc public protocol DisplayViewLoadingDelegate: NSObjectProtocol { - - func displayViewDidLoadAd(_ displayView: PBMDisplayView) +/// A protocol that defines methods for receiving loading events related to ad display views. +/// This protocol is used to notify the delegate when an ad has successfully loaded or if an error occurs during loading. +@objc +public protocol DisplayViewLoadingDelegate: NSObjectProtocol { + + /// Notifies that the ad has successfully loaded in the display view. + /// + /// - Parameters: + /// - displayView: The `UIView` instance in which the ad has been loaded. + @objc func displayViewDidLoadAd(_ displayView: UIView) - func displayView(_ displayView: PBMDisplayView, - didFailWithError error: Error) + /// Notifies that an error occurred during the ad loading process. + /// + /// - Parameters: + /// - displayView: The `UIView` instance where the ad was intended to load. + /// - error: An `Error` instance describing the issue that occurred during the ad loading. + @objc func displayView(_ displayView: UIView, didFailWithError error: Error) } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialController.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialController.swift index b5b49e46b..bebd17947 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialController.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialController.swift @@ -1,25 +1,27 @@ /*   Copyright 2018-2021 Prebid.org, Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ import UIKit -public class InterstitialController: NSObject, PBMAdViewManagerDelegate { - - private var renderer: PrebidMobilePluginRenderer? +@objcMembers +public class InterstitialController: + NSObject, + PrebidMobileInterstitialControllerProtocol, + PBMAdViewManagerDelegate { - @objc public var adFormats: Set { + public var adFormats: Set { get { adConfiguration.adFormats } set { adConfiguration.adFormats = newValue } } @@ -30,57 +32,82 @@ public class InterstitialController: NSObject, PBMAdViewManagerDelegate { set { adConfiguration.adConfiguration.isRewarded = newValue } } - @objc public var videoControlsConfig: VideoControlsConfiguration { + public var videoControlsConfig: VideoControlsConfiguration { get { adConfiguration.adConfiguration.videoControlsConfig } set { adConfiguration.adConfiguration.videoControlsConfig = newValue } } - @objc public var videoParameters: VideoParameters { + public var videoParameters: VideoParameters { get { adConfiguration.adConfiguration.videoParameters } set { adConfiguration.adConfiguration.videoParameters = newValue } } - @objc public weak var loadingDelegate: InterstitialControllerLoadingDelegate? - @objc public weak var interactionDelegate: InterstitialControllerInteractionDelegate? + public weak var loadingDelegate: InterstitialControllerLoadingDelegate? + public weak var interactionDelegate: InterstitialControllerInteractionDelegate? var bid: Bid var adConfiguration: AdUnitConfig var displayProperties: PBMInterstitialDisplayProperties + var transactionFactory: PBMTransactionFactory? var adViewManager: PBMAdViewManager? // MARK: - Life cycle - @objc public init(bid: Bid, adConfiguration: AdUnitConfig) { + public init(bid: Bid, adConfiguration: AdUnitConfig) { self.bid = bid self.adConfiguration = adConfiguration displayProperties = PBMInterstitialDisplayProperties() } - @objc public convenience init(bid: Bid, configId: String) { + public convenience init(bid: Bid, configId: String) { let adConfig = AdUnitConfig(configId: configId) adConfig.adConfiguration.isInterstitialAd = true adConfig.adConfiguration.isRewarded = bid.rewardedConfig != nil self.init(bid: bid, adConfiguration: adConfig) } - // TODO: provide a more relevant name for this function. - @objc public func loadAd() { + public func loadAd() { + guard transactionFactory == nil else { + return + } - self.renderer = PrebidMobilePluginRegister.shared.getPluginForPreferredRenderer(bid: bid) - - let connection: PrebidServerConnectionProtocol = PrebidServerConnection.shared + adConfiguration.adConfiguration.winningBidAdFormat = bid.adFormat + adConfiguration.adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: bid.rewardedConfig) + videoControlsConfig.initialize(with: bid.videoAdConfiguration) - self.renderer?.createInterstitialController?( + // This part is dedicating to test server-side ad configurations. + // Need to be removed when ext.prebid.passthrough will be available. + #if DEBUG + adConfiguration.adConfiguration.videoControlsConfig.initialize(with: bid.testVideoAdConfiguration) + #endif + + transactionFactory = PBMTransactionFactory( bid: bid, adConfiguration: adConfiguration, - connection: connection, - adViewManagerDelegate: self, - videoControlsConfig: videoControlsConfig - ) - } - - @objc public func show() { + connection: PrebidServerConnection.shared, + callback: { [weak self] transaction, error in + + if let transaction = transaction { + self?.display(transaction: transaction) + } else { + self?.reportFailureWithError(error) + } + }) + + PBMWinNotifier.notifyThroughConnection( + PrebidServerConnection.shared, + winning: bid, + callback: { [weak self] adMarkup in + if let adMarkup = adMarkup { + self?.transactionFactory?.load(withAdMarkup: adMarkup) + } else { + Log.error("No ad markup received from server.") + } + }) + } + + public func show() { if let adViewManager = adViewManager { adViewManager.show() } @@ -88,62 +115,49 @@ public class InterstitialController: NSObject, PBMAdViewManagerDelegate { // MARK: - PBMAdViewManagerDelegate protocol - @objc public func viewControllerForModalPresentation() -> UIViewController? { - if let interactionDelegate = interactionDelegate { - return interactionDelegate.viewControllerForModalPresentation(fromInterstitialController: self) - } else { - return nil - } + public func viewControllerForModalPresentation() -> UIViewController? { + interactionDelegate?.viewControllerForModalPresentation(fromInterstitialController: self) } - - @objc public func adLoaded(_ pbmAdDetails: PBMAdDetails) { + + public func adLoaded(_ pbmAdDetails: PBMAdDetails) { reportSuccess() } - - @objc public func failed(toLoad error: Error) { + + public func failed(toLoad error: Error) { reportFailureWithError(error) } - - @objc public func adDidComplete() { + + public func adDidComplete() { if let delegate = interactionDelegate { delegate.interstitialControllerDidComplete(self) } } - @objc public func adDidDisplay() { + public func adDidDisplay() { if let delegate = interactionDelegate { delegate.interstitialControllerDidDisplay(self) } } - @objc public func adWasClicked() { + public func adWasClicked() { if let delegate = interactionDelegate { delegate.interstitialControllerDidClickAd(self) } } - @objc public func adViewWasClicked() { + public func adViewWasClicked() { if let delegate = interactionDelegate { delegate.interstitialControllerDidClickAd(self) } } - @objc public func adDidExpand() { - } - - @objc public func adDidCollapse() { - } - - @objc public func adDidLeaveApp() { + public func adDidLeaveApp() { if let delegate = interactionDelegate { delegate.interstitialControllerDidLeaveApp(self) } } - @objc public func adClickthroughDidClose() { - } - - @objc public func adDidClose() { + public func adDidClose() { adViewManager = nil if let delegate = interactionDelegate { delegate.interstitialControllerDidCloseAd(self) @@ -160,6 +174,10 @@ public class InterstitialController: NSObject, PBMAdViewManagerDelegate { displayProperties } + public func adClickthroughDidClose() {} + public func adDidExpand() {} + public func adDidCollapse() {} + // MARK: - Private Helpers @available(*, unavailable) @@ -173,16 +191,18 @@ public class InterstitialController: NSObject, PBMAdViewManagerDelegate { loadingDelegate.interstitialController(self, didFailWithError: error) } } - + func reportSuccess() { if let loadingDelegate = loadingDelegate { loadingDelegate.interstitialControllerDidLoadAd(self) } } - - public func display(transaction: PBMTransaction) { - adViewManager = PBMAdViewManager(connection: PrebidServerConnection.shared, - modalManagerDelegate: nil) + + private func display(transaction: PBMTransaction) { + adViewManager = PBMAdViewManager( + connection: PrebidServerConnection.shared, + modalManagerDelegate: nil + ) adViewManager?.adViewManagerDelegate = self adViewManager?.adConfiguration.isInterstitialAd = true adViewManager?.adConfiguration.isRewarded = adConfiguration.adConfiguration.isRewarded diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerInteractionDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerInteractionDelegate.swift index 12655b8b3..4d4064b9c 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerInteractionDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerInteractionDelegate.swift @@ -15,17 +15,73 @@ import UIKit -@objc public protocol InterstitialControllerInteractionDelegate: NSObjectProtocol { +/// A protocol that defines methods for handling user interactions and lifecycle events related to interstitial ads. +@objc +public protocol InterstitialControllerInteractionDelegate: NSObjectProtocol { - func trackImpression(forInterstitialController: InterstitialController) + /// Tracks an impression for the specified interstitial ad controller. + /// + /// - Parameters: + /// - forInterstitialController: The interstitial ad controller associated with the impression. + @objc func trackImpression( + forInterstitialController: PrebidMobileInterstitialControllerProtocol + ) - func interstitialControllerDidClickAd(_ interstitialController: InterstitialController) - func interstitialControllerDidCloseAd(_ interstitialController: InterstitialController) - func interstitialControllerDidLeaveApp(_ interstitialController: InterstitialController) - func interstitialControllerDidDisplay(_ interstitialController: InterstitialController) - func interstitialControllerDidComplete(_ interstitialController: InterstitialController) + /// Notifies that the interstitial ad was clicked by the user. + /// + /// - Parameters: + /// - interstitialController: The interstitial ad controller associated with the click. + @objc func interstitialControllerDidClickAd( + _ interstitialController: PrebidMobileInterstitialControllerProtocol + ) + + /// Notifies the delegate that the interstitial ad has been closed by the user. + /// + /// - Parameters: + /// - interstitialController: The interstitial ad controller that was closed. + @objc func interstitialControllerDidCloseAd( + _ interstitialController: PrebidMobileInterstitialControllerProtocol + ) + + /// Notifies the delegate that the user has left the app after interacting with the interstitial ad. + /// + /// - Parameters: + /// - interstitialController: The interstitial ad controller that was displayed when the user left the app. + @objc func interstitialControllerDidLeaveApp( + _ interstitialController: PrebidMobileInterstitialControllerProtocol + ) + + /// Notifies the delegate that the interstitial ad has been displayed to the user. + /// + /// - Parameters: + /// - interstitialController: The interstitial ad controller that displayed the ad. + @objc func interstitialControllerDidDisplay( + _ interstitialController: PrebidMobileInterstitialControllerProtocol + ) + + /// Notifies the delegate that the interstitial ad has completed its presentation. + /// + /// - Parameters: + /// - interstitialController: The interstitial ad controller associated with the completed ad. + @objc func interstitialControllerDidComplete( + _ interstitialController: PrebidMobileInterstitialControllerProtocol + ) - func viewControllerForModalPresentation(fromInterstitialController: InterstitialController) -> UIViewController? + /// Requests the `UIViewController` to be used for presenting modals from the interstitial ad controller. + /// + /// - Parameters: + /// - fromInterstitialController: The interstitial ad controller requesting the view controller. + @objc func viewControllerForModalPresentation( + fromInterstitialController: PrebidMobileInterstitialControllerProtocol + ) -> UIViewController? - @objc optional func trackUserReward(_ interstitialController: InterstitialController, _ reward: PrebidReward) + /// Notifies the delegate when a reward is granted to the user after interacting with a rewarded interstitial ad. + /// + /// - Parameters: + /// - interstitialController: The instance of the interstitial ad controller responsible for managing the ad. + /// - reward: An object containing details about the reward, such as the type and amount. + @objc optional func trackUserReward( + _ interstitialController: PrebidMobileInterstitialControllerProtocol, + _ reward: PrebidReward + ) } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerLoadingDelegate.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerLoadingDelegate.swift index 6b6c55213..291bb575f 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerLoadingDelegate.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/InterstitialControllerLoadingDelegate.swift @@ -15,9 +15,25 @@ import Foundation -@objc public protocol InterstitialControllerLoadingDelegate: NSObjectProtocol { +/// A protocol that defines methods for receiving loading events related to interstitial ad controllers. +@objc +public protocol InterstitialControllerLoadingDelegate: NSObjectProtocol { - func interstitialControllerDidLoadAd(_ interstitialController: InterstitialController) - func interstitialController(_ interstitialController: InterstitialController, - didFailWithError error: Error) + /// Notifies the delegate that the interstitial ad has successfully loaded. + /// + /// - Parameters: + /// - interstitialController: The interstitial ad controller that successfully loaded the ad. + @objc func interstitialControllerDidLoadAd( + _ interstitialController: PrebidMobileInterstitialControllerProtocol + ) + + /// Notifies the delegate that an error occurred during the interstitial ad loading process. + /// + /// - Parameters: + /// - interstitialController: The interstitial ad controller that attempted to load the ad. + /// - error: An `Error` instance describing the issue that occurred during the ad loading. + @objc func interstitialController( + _ interstitialController: PrebidMobileInterstitialControllerProtocol, + didFailWithError error: Error + ) } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView+InternalState.h b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView+InternalState.h index e33b5b49c..3457bca3f 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView+InternalState.h +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView+InternalState.h @@ -27,7 +27,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, readonly, nullable) id connection; -- (instancetype)initWithFrame:(CGRect)frame bid:(Bid *)bid adConfiguration:(AdUnitConfig *)adConfiguration; +- (instancetype)initWithFrame:(CGRect)frame + bid:(Bid *)bid + adConfiguration:(AdUnitConfig *)adConfiguration; @end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView.h b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView.h index e3beb7b29..8bdafef44 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView.h +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView.h @@ -15,21 +15,28 @@ #import -@class Bid; +#import "PBMAdViewManagerDelegate.h" +#import "PBMModalManagerDelegate.h" +#import "PrebidMobileDisplayViewProtocol.h" + @protocol DisplayViewLoadingDelegate; @protocol DisplayViewInteractionDelegate; +@class AdUnitConfig; +@class Bid; + NS_ASSUME_NONNULL_BEGIN -@interface PBMDisplayView : UIView +@interface PBMDisplayView : UIView @property (atomic, weak, nullable) NSObject *loadingDelegate; @property (atomic, weak, nullable) NSObject *interactionDelegate; @property (nonatomic, readonly) BOOL isCreativeOpened; - (instancetype)initWithFrame:(CGRect)frame bid:(Bid *)bid configId:(NSString *)configId; +- (instancetype)initWithFrame:(CGRect)frame bid:(Bid *)bid adConfiguration:(AdUnitConfig *)adConfiguration; -- (void)displayAd; +- (void)loadAd; @end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView.m index d8a66fc2a..8d6850728 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PBMDisplayView.m @@ -34,45 +34,76 @@ #import "PBMMacros.h" -@interface PBMDisplayView () +@interface PBMDisplayView () @property (nonatomic, strong, readonly, nonnull) Bid *bid; @property (nonatomic, strong, readonly, nonnull) AdUnitConfig *adConfiguration; +@property (nonatomic, strong, nullable) PBMTransactionFactory *transactionFactory; @property (nonatomic, strong, nullable) PBMAdViewManager *adViewManager; @property (nonatomic, strong, readonly, nonnull) PBMInterstitialDisplayProperties *interstitialDisplayProperties; -@property (nonatomic, strong, nullable) id renderer; - @end @implementation PBMDisplayView // MARK: - Public API + - (instancetype)initWithFrame:(CGRect)frame bid:(Bid *)bid configId:(NSString *)configId { - return self = [self initWithFrame:frame bid:bid adConfiguration:[[AdUnitConfig alloc] initWithConfigId:configId size:bid.size]]; + return self = [self initWithFrame:frame + bid:bid + adConfiguration:[[AdUnitConfig alloc] initWithConfigId:configId + size:bid.size]]; } -- (instancetype)initWithFrame:(CGRect)frame bid:(Bid *)bid adConfiguration:(AdUnitConfig *)adConfiguration { +- (instancetype)initWithFrame:(CGRect)frame + bid:(Bid *)bid + adConfiguration:(AdUnitConfig *)adConfiguration { + if (!(self = [super initWithFrame:frame])) { return nil; } + _bid = bid; _adConfiguration = adConfiguration; _interstitialDisplayProperties = [[PBMInterstitialDisplayProperties alloc] init]; + return self; } -- (void)displayAd { - self.renderer = [[PrebidMobilePluginRegister shared] getPluginForPreferredRendererWithBid:self.bid]; - - NSLog(@"Renderer: %@", self.renderer); +- (void)loadAd { + if (self.transactionFactory) { + return; + } + self.adConfiguration.adConfiguration.winningBidAdFormat = self.bid.adFormat; - id const connection = self.connection ?: PrebidServerConnection.shared; - dispatch_async(dispatch_get_main_queue(), ^{ - [self.renderer createBannerAdViewWith:self.frame bid:self.bid adConfiguration:self.adConfiguration connection:connection adViewDelegate:self]; - }); + self.adConfiguration.adConfiguration.rewardedConfig = [[PBMRewardedConfig alloc] initWithOrtbRewarded:self.bid.rewardedConfig]; + + @weakify(self); + self.transactionFactory = [[PBMTransactionFactory alloc] initWithBid:self.bid + adConfiguration:self.adConfiguration + connection:self.connection ?: PrebidServerConnection.shared + callback:^(PBMTransaction * _Nullable transaction, + NSError * _Nullable error) { + @strongify(self); + if (!self) { return; } + + if (error) { + [self reportFailureWithError:error]; + } else { + [self displayTransaction:transaction]; + } + }]; + + [PBMWinNotifier notifyThroughConnection:PrebidServerConnection.shared + winningBid:self.bid + callback:^(NSString *adMarkup) { + @strongify(self); + if (!self) { return; } + + [self.transactionFactory loadWithAdMarkup:adMarkup]; + }]; } - (BOOL)isCreativeOpened { @@ -158,6 +189,17 @@ - (void)reportSuccess { [self.loadingDelegate displayViewDidLoadAd:self]; } +- (void)displayTransaction:(PBMTransaction *)transaction { + id const connection = self.connection ?: PrebidServerConnection.shared; + self.adViewManager = [[PBMAdViewManager alloc] initWithConnection:connection modalManagerDelegate:self]; + self.adViewManager.adViewManagerDelegate = self; + self.adViewManager.adConfiguration = self.adConfiguration.adConfiguration; + if (self.adConfiguration.adConfiguration.winningBidAdFormat == AdFormat.video) { + self.adConfiguration.adConfiguration.isBuiltInVideo = YES; + } + [self.adViewManager handleExternalTransaction:transaction]; +} + - (void)interactionDelegateWillPresentModal { NSObject const *delegate = self.interactionDelegate; if ([delegate respondsToSelector:@selector(willPresentModalFrom:)]) { diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidMobileDisplayViewProtocol.h b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidMobileDisplayViewProtocol.h new file mode 100644 index 000000000..b847c7e0f --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidMobileDisplayViewProtocol.h @@ -0,0 +1,26 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +#import + +/// This protocol is used to load and display the ad content in a view. +@protocol PrebidMobileDisplayViewProtocol + +/// Loads the ad content into the display view. +/// - Important: This method is expected to call the `loadingDelegate` once the +/// ad is successfully loaded or if any error occurred. +- (void)loadAd; + +@end diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidMobileInterstitialControllerProtocol.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidMobileInterstitialControllerProtocol.swift new file mode 100644 index 000000000..965473116 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidMobileInterstitialControllerProtocol.swift @@ -0,0 +1,29 @@ +/*   Copyright 2018-2021 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import UIKit + +/// A protocol that defines the interface for controlling and interacting with interstitial ads. +/// This protocol allows loading and displaying interstitial ads, as well as managing interactions with them. +@objc +public protocol PrebidMobileInterstitialControllerProtocol: NSObjectProtocol { + + /// Loads the ad content for the interstitial. + /// - Important: This method is expected to call the `loadingDelegate` once the ad is successfully loaded or if any error occurred. + func loadAd() + + /// Displays the interstitial ad. + func show() +} diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidRenderer.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidRenderer.swift index 12576bcf7..092c8d313 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidRenderer.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCacheRenderers/PrebidRenderer.swift @@ -1,119 +1,61 @@ -/*   Copyright 2018-2021 Prebid.org, Inc. +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +import UIKit - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ -import Foundation +public let PREBID_MOBILE_RENDERER_NAME = "PrebidRenderer" public class PrebidRenderer: NSObject, PrebidMobilePluginRenderer { - public let name = "PrebidRenderer" - + public let name = PREBID_MOBILE_RENDERER_NAME public let version = Prebid.shared.version - - public var data: [AnyHashable: Any]? = nil - - private var adViewManager: PBMAdViewManager? - - public var transactionFactory: PBMTransactionFactory? - - public func isSupportRendering(for format: AdFormat?) -> Bool { - AdFormat.allCases.contains(where: { $0 == format }) - } - - public func setupBid(_ bid: Bid, adConfiguration: AdUnitConfig, connection: PrebidServerConnectionProtocol) { + public var data: [String: Any]? + + public func createBannerView( + with frame: CGRect, + bid: Bid, + adConfiguration: AdUnitConfig, + loadingDelegate: DisplayViewLoadingDelegate, + interactionDelegate: DisplayViewInteractionDelegate + ) -> (UIView & PrebidMobileDisplayViewProtocol)? { + let displayView = PBMDisplayView( + frame: frame, + bid: bid, + adConfiguration: adConfiguration + ) - } - - public func createBannerAdView(with frame: CGRect, bid: Bid, adConfiguration: AdUnitConfig, - connection: PrebidServerConnectionProtocol, - adViewDelegate: (any PBMAdViewDelegate)?) { - - self.transactionFactory = PBMTransactionFactory(bid: bid, adConfiguration: adConfiguration, connection: connection) { [weak self] transaction, error in - self?.transactionFactory = nil - guard let transaction else { - adViewDelegate?.failed(toLoad: NSError(domain: "", code: 0)) - return - } - if let error { - adViewDelegate?.failed(toLoad: error) - return - } - self?.displayTransaction(transaction, adConfiguration: adConfiguration, connection: connection, adViewDelegate: adViewDelegate) - } + displayView.interactionDelegate = interactionDelegate + displayView.loadingDelegate = loadingDelegate - PBMWinNotifier.notifyThroughConnection(PrebidServerConnection.shared, - winning: bid) { [weak self] adMarkup in - - self?.transactionFactory?.load(withAdMarkup: adMarkup!) - } - + return displayView } - - private func displayTransaction(_ transaction: PBMTransaction, adConfiguration: AdUnitConfig, - connection: PrebidServerConnectionProtocol, - adViewDelegate: (any PBMAdViewDelegate)?) { - adViewManager = PBMAdViewManager(connection: connection, modalManagerDelegate: adViewDelegate) - adViewManager?.adViewManagerDelegate = adViewDelegate - adViewManager?.adConfiguration = adConfiguration.adConfiguration - - if adConfiguration.adConfiguration.winningBidAdFormat == .video { - adConfiguration.adConfiguration.isBuiltInVideo = true - } - - adViewManager?.handleExternalTransaction(transaction) - } - - public func createInterstitialController(bid: Bid, adConfiguration: AdUnitConfig, - connection: PrebidServerConnectionProtocol, - adViewManagerDelegate adViewDelegate: InterstitialController?, - videoControlsConfig: VideoControlsConfiguration?) { - guard transactionFactory == nil else { - return - } - - adConfiguration.adConfiguration.winningBidAdFormat = bid.adFormat - videoControlsConfig?.initialize(with: bid.videoAdConfiguration) + public func createInterstitialController( + bid: Bid, + adConfiguration: AdUnitConfig, + loadingDelegate: InterstitialControllerLoadingDelegate, + interactionDelegate: InterstitialControllerInteractionDelegate + ) -> PrebidMobileInterstitialControllerProtocol? { + let interstitialController = InterstitialController( + bid: bid, + adConfiguration: adConfiguration + ) - adConfiguration.adConfiguration.rewardedConfig = RewardedConfig(ortbRewarded: bid.rewardedConfig) + interstitialController.loadingDelegate = loadingDelegate + interstitialController.interactionDelegate = interactionDelegate - // This part is dedicating to test server-side ad configurations. - // Need to be removed when ext.prebid.passthrough will be available. - #if DEBUG - adConfiguration.adConfiguration.videoControlsConfig.initialize(with: bid.testVideoAdConfiguration) - #endif - transactionFactory = PBMTransactionFactory(bid: bid, - adConfiguration: adConfiguration, - connection: PrebidServerConnection.shared, - callback: { [weak adViewDelegate] transaction, error in - - if let transaction = transaction { - adViewDelegate?.display(transaction: transaction) - } else { - self.transactionFactory = nil - adViewDelegate?.reportFailureWithError(error) - } - }) - - PBMWinNotifier.notifyThroughConnection(PrebidServerConnection.shared, - winning: bid, - callback: { [weak self] adMarkup in - if let self = self, let adMarkup = adMarkup { - self.transactionFactory?.load(withAdMarkup: adMarkup) - } else { - Log.debug("Ad markup is empty") - } - }) + return interstitialController } - } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift index 155916ca4..3a54f6df4 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/AdUnitConfig.swift @@ -309,6 +309,10 @@ public class AdUnitConfig: NSObject, NSCopying { clone.adFormats = self.adFormats clone.nativeAdConfiguration = self.nativeAdConfiguration + clone.adConfiguration.bannerParameters = self.adConfiguration.bannerParameters + clone.adConfiguration.videoParameters = self.adConfiguration.videoParameters + clone.adConfiguration.videoControlsConfig = self.adConfiguration.videoControlsConfig + clone.adConfiguration.winningBidAdFormat = self.adConfiguration.winningBidAdFormat clone.sizes = sizes clone.adSize = adSize clone.minSizePerc = self.minSizePerc diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/Bid.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/Bid.swift index 98f75cfe3..6fe4e509d 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/Bid.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/Bid.swift @@ -18,6 +18,7 @@ import Foundation import StoreKit import UIKit +@objcMembers public class Bid: NSObject { public static let KEY_RENDERER_NAME = "rendererName"; @@ -27,7 +28,7 @@ public class Bid: NSObject { /// Bid price expressed as CPM although the actual transaction is for a unit impression only. /// Note that while the type indicates float, integer math is highly recommended /// when handling currencies (e.g., BigDecimal in Java). - @objc public var price: Float { + public var price: Float { bid.price.floatValue } @@ -35,15 +36,15 @@ public class Bid: NSObject { /// Win notice URL called by the exchange if the bid wins (not necessarily indicative of a delivered, /// viewed, or billable ad); optional means of serving ad markup. /// Substitution macros (Section 4.4) may be included in both the URL and optionally returned markup. - @objc public private(set) var nurl: String? + public private(set) var nurl: String? /// Optional means of conveying ad markup in case the bid wins; supersedes the win notice /// if markup is included in both. /// Substitution macros (Section 4.4) may be included. - @objc public private(set) var adm: String? + public private(set) var adm: String? /// Ad size - @objc public var size: CGSize { + public var size: CGSize { guard let w = bid.w, let h = bid.h else { return CGSize.zero } @@ -52,12 +53,12 @@ public class Bid: NSObject { } /// Targeting information that needs to be passed to the ad server SDK. - @objc public var targetingInfo: [String : String]? { + public var targetingInfo: [String : String]? { bid.ext.prebid?.targeting } /// Targeting information that needs to be passed to the ad server SDK. - @objc public var meta: [String : String]? { + public var meta: [String : String]? { bid.ext.prebid?.meta } @@ -65,24 +66,34 @@ public class Bid: NSObject { SKAdNetwork parameters about an App Store product. Used in the StoreKit */ - @objc public var skadn: PBMORTBBidExtSkadn? { + public var skadn: PBMORTBBidExtSkadn? { return bid.ext.skadn } /// Prebid ad format - @objc public var adFormat: AdFormat? { + public var adFormat: AdFormat? { AdFormat.allCases.filter { $0.stringEquivalent == bid.ext.prebid?.type }.first } /// Prebid video ad configuration - @objc public var videoAdConfiguration: PBMORTBAdConfiguration? { + public var videoAdConfiguration: PBMORTBAdConfiguration? { bid.ext.prebid?.passthrough?.filter { $0.type == "prebidmobilesdk" }.first?.adConfiguration } + /// Preffered plugin renderer name + public var pluginRendererName: String? { + meta?[Bid.KEY_RENDERER_NAME] + } + + /// Preffered plugin renderer version + public var pluginRendererVersion: String? { + meta?[Bid.KEY_RENDERER_VERSION] + } + // This part is dedicating to test server-side ad configurations. // Need to be removed when ext.prebid.passthrough will be available. #if DEBUG - @objc public var testVideoAdConfiguration: PBMORTBAdConfiguration? { + public var testVideoAdConfiguration: PBMORTBAdConfiguration? { bid.ext.passthrough?.filter { $0.type == "prebidmobilesdk" }.first?.adConfiguration } #endif @@ -92,7 +103,7 @@ public class Bid: NSObject { } /// Returns YES if this bid is intented for display. - @objc public var isWinning: Bool { + public var isWinning: Bool { guard let targetingInfo = self.targetingInfo else { return false } @@ -111,24 +122,16 @@ public class Bid: NSObject { return true } - @objc public var events: PBMORTBExtPrebidEvents? { + public var events: PBMORTBExtPrebidEvents? { bid.ext.prebid?.events } - @objc public private(set) var bid: PBMORTBBid + public private(set) var bid: PBMORTBBid - @objc public init(bid: PBMORTBBid) { + public init(bid: PBMORTBBid) { self.bid = bid let macrosHelper = PBMORTBMacrosHelper(bid: bid) adm = macrosHelper.replaceMacros(in: bid.adm) nurl = macrosHelper.replaceMacros(in: bid.nurl) } - - public func getPreferredPluginRendererName() -> String? { - return meta?[Bid.KEY_RENDERER_NAME] - } - - public func getPreferredPluginRendererVersion() -> String? { - return meta?[Bid.KEY_RENDERER_VERSION] - } } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/BidResponse.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/BidResponse.swift index 342a7d97e..de99bde79 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/BidResponse.swift +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/BidResponse.swift @@ -101,12 +101,4 @@ public class BidResponse: NSObject { targetingInfo?[key] = value } - - public func getPreferredPluginRendererName() -> String? { - winningBid?.getPreferredPluginRendererName() - } - - public func getPreferredPluginRendererVersion() -> String? { - winningBid?.getPreferredPluginRendererVersion() - } } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m index 9db0a2fdc..f443dd3ca 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m @@ -74,9 +74,8 @@ - (void)buildBidRequest:(nonnull PBMORTBBidRequest *)bidRequest { bidRequest.extPrebid.storedBidResponses = [Prebid.shared getStoredBidResponses]; bidRequest.ortbObject = [self.adConfiguration.adConfiguration getCheckedOrtbConfig]; - NSArray *renderers = [[PrebidMobilePluginRegister shared] getAllPlugins]; - if (renderers.count != 0 && self.adConfiguration.adConfiguration.isOriginalAPI == false) { - bidRequest.extPrebid.sdk = [PBMPrebidParameterBuilder getRenderersJson]; + if (!self.adConfiguration.adConfiguration.isOriginalAPI) { + bidRequest.extPrebid.sdkRenderers = [PrebidMobilePluginRegister.shared getAllPluginsJSONRepresentation]; } if (Prebid.shared.pbsDebug) { @@ -314,20 +313,6 @@ - (void)buildBidRequest:(nonnull PBMORTBBidRequest *)bidRequest { } } -+ (nonnull PBMJsonDictionary *)getRenderersJson { - PBMMutableJsonDictionary * const sdk = [PBMMutableJsonDictionary new]; - NSMutableArray *renderersArray = [NSMutableArray array]; - NSArray *renderers = [[PrebidMobilePluginRegister shared] getAllPlugins]; - for (id renderer in renderers) { - PBMMutableJsonDictionary *rendererDict = [PBMMutableJsonDictionary new]; - rendererDict[@"name"] = renderer.name; - rendererDict[@"version"] = renderer.version; - [renderersArray addObject:rendererDict]; - } - sdk[@"renderers"] = renderersArray; - return sdk; -} - + (PBMORTBFormat *)ortbFormatWithSize:(NSValue *)size { PBMORTBFormat * const format = [[PBMORTBFormat alloc] init]; CGSize const cgSize = size.CGSizeValue; diff --git a/PrebidMobileTests/RenderingTests/Mocks/MockPrebidMobilePluginRenderer.swift b/PrebidMobileTests/RenderingTests/Mocks/MockPrebidMobilePluginRenderer.swift new file mode 100644 index 000000000..357878644 --- /dev/null +++ b/PrebidMobileTests/RenderingTests/Mocks/MockPrebidMobilePluginRenderer.swift @@ -0,0 +1,55 @@ +/* Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation +@testable import PrebidMobile + +class MockPrebidMobilePluginRenderer: PrebidMobilePluginRenderer { + + let name: String + let version: String + var data: [String: Any]? + + init(name: String, version: String, data: [String: Any]? = nil) { + self.name = name + self.version = version + self.data = data + } + + func jsonDictionary() -> [String: Any] { + var json: [String: Any] = ["name": name, "version": version] + json["data"] = data + return json + } + + func createBannerView( + with frame: CGRect, + bid: Bid, + adConfiguration: AdUnitConfig, + loadingDelegate: any DisplayViewLoadingDelegate, + interactionDelegate: any DisplayViewInteractionDelegate + ) -> (any UIView & PrebidMobileDisplayViewProtocol)? { + return nil + } + + func createInterstitialController( + bid: Bid, + adConfiguration: AdUnitConfig, + loadingDelegate: any InterstitialControllerLoadingDelegate, + interactionDelegate: any InterstitialControllerInteractionDelegate + ) -> (any PrebidMobileInterstitialControllerProtocol)? { + return nil + } +} diff --git a/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift b/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift index 865f808d5..7f30a864a 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PBMORTBAbstractTest.swift @@ -181,13 +181,14 @@ class PBMORTBAbstractTest : XCTestCase { extPrebid.storedRequestID = "b4eb1475-4e3d-4186-97b7-25b6a6cf8618" extPrebid.dataBidders = ["openx", "prebid", "thanatos"] extPrebid.storedAuctionResponse = "stored-auction-response-test" + extPrebid.sdkRenderers = [["name": "MockRenderer1", "version": "0.0.1"], ["name": "MockRenderer2", "version": "0.0.2"]] - codeAndDecode(abstract: extPrebid, expectedString: "{\"data\":{\"bidders\":[\"openx\",\"prebid\",\"thanatos\"]},\"storedauctionresponse\":{\"id\":\"stored-auction-response-test\"},\"storedrequest\":{\"id\":\"b4eb1475-4e3d-4186-97b7-25b6a6cf8618\"},\"targeting\":{}}") + codeAndDecode(abstract: extPrebid, expectedString: "{\"data\":{\"bidders\":[\"openx\",\"prebid\",\"thanatos\"]},\"sdk\":{\"renderers\":[{\"name\":\"MockRenderer1\",\"version\":\"0.0.1\"},{\"name\":\"MockRenderer2\",\"version\":\"0.0.2\"}]},\"storedauctionresponse\":{\"id\":\"stored-auction-response-test\"},\"storedrequest\":{\"id\":\"b4eb1475-4e3d-4186-97b7-25b6a6cf8618\"},\"targeting\":{}}") let pbmORTBBidRequest = PBMORTBBidRequest() pbmORTBBidRequest.extPrebid = extPrebid - codeAndDecode(abstract: pbmORTBBidRequest, expectedString: "{\"ext\":{\"prebid\":{\"data\":{\"bidders\":[\"openx\",\"prebid\",\"thanatos\"]},\"storedauctionresponse\":{\"id\":\"stored-auction-response-test\"},\"storedrequest\":{\"id\":\"b4eb1475-4e3d-4186-97b7-25b6a6cf8618\"},\"targeting\":{}}},\"imp\":[{\"clickbrowser\":0,\"ext\":{\"dlp\":1},\"instl\":0,\"secure\":0}]}") + codeAndDecode(abstract: pbmORTBBidRequest, expectedString: "{\"ext\":{\"prebid\":{\"data\":{\"bidders\":[\"openx\",\"prebid\",\"thanatos\"]},\"sdk\":{\"renderers\":[{\"name\":\"MockRenderer1\",\"version\":\"0.0.1\"},{\"name\":\"MockRenderer2\",\"version\":\"0.0.2\"}]},\"storedauctionresponse\":{\"id\":\"stored-auction-response-test\"},\"storedrequest\":{\"id\":\"b4eb1475-4e3d-4186-97b7-25b6a6cf8618\"},\"targeting\":{}}},\"imp\":[{\"clickbrowser\":0,\"ext\":{\"dlp\":1},\"instl\":0,\"secure\":0}]}") } func testSourceToJsonString() { diff --git a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift index e3bfc4620..71a7d97bb 100644 --- a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift @@ -826,6 +826,37 @@ class PrebidParameterBuilderTest: XCTestCase { XCTAssert(bidRequest.ortbObject?.isEmpty == true) } + + func testExtPrebidSDKRenderers() { + let mockRenderer1 = MockPrebidMobilePluginRenderer(name: "MockRenderer1", version: "0.0.1") + let mockRenderer2 = MockPrebidMobilePluginRenderer(name: "MockRenderer2", version: "0.0.2") + let mockRenderer3 = MockPrebidMobilePluginRenderer(name: "MockRenderer3", version: "0.0.3") + + PrebidMobilePluginRegister.shared.registerPlugin(mockRenderer1) + PrebidMobilePluginRegister.shared.registerPlugin(mockRenderer2) + PrebidMobilePluginRegister.shared.registerPlugin(mockRenderer3) + + let adUnitConfig = AdUnitConfig(configId: "test") + let bidRequest = buildBidRequest(with: adUnitConfig) + let realResult = bidRequest.extPrebid.sdkRenderers + + XCTAssert(realResult?.count == 3) + } + + func testExtPrebidSDKRenderers_OriginalAPI() { + let mockRenderer1 = MockPrebidMobilePluginRenderer(name: "MockRenderer1", version: "0.0.1") + let mockRenderer2 = MockPrebidMobilePluginRenderer(name: "MockRenderer2", version: "0.0.2") + let mockRenderer3 = MockPrebidMobilePluginRenderer(name: "MockRenderer3", version: "0.0.3") + + PrebidMobilePluginRegister.shared.registerPlugin(mockRenderer1) + PrebidMobilePluginRegister.shared.registerPlugin(mockRenderer2) + PrebidMobilePluginRegister.shared.registerPlugin(mockRenderer3) + + let adUnit = AdUnit(configId: "test", size: CGSize.zero, adFormats: [.banner]) + + let bidRequest = buildBidRequest(with: adUnit.adUnitConfig) + XCTAssertNil(bidRequest.extPrebid.sdkRenderers) + } // MARK: - Helpers diff --git a/PrebidMobileTests/UnitTests/PluginRegisterTest.swift b/PrebidMobileTests/RenderingTests/Tests/Prebid/PluginRegisterTest.swift similarity index 50% rename from PrebidMobileTests/UnitTests/PluginRegisterTest.swift rename to PrebidMobileTests/RenderingTests/Tests/Prebid/PluginRegisterTest.swift index ae32aaa8b..7fd848b08 100644 --- a/PrebidMobileTests/UnitTests/PluginRegisterTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/Prebid/PluginRegisterTest.swift @@ -1,40 +1,53 @@ -// -// PluginRegisterTest.swift -// PrebidMobileTests -// -// Created by Richard Dépierre on 24/07/2024. -// Copyright © 2024 AppNexus. All rights reserved. -// +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ import XCTest - @testable import PrebidMobile -import TestUtils + +extension PrebidMobilePluginRegister { + + func unregisterAllPlugins() { + let plugins = getAllPlugins() + for plugin in plugins { + unregisterPlugin(plugin) + } + } +} class PluginRegisterTest: XCTestCase { + let prebidMobilePluginRegister = PrebidMobilePluginRegister.shared - let plugin = SampleCustomRenderer() + + let plugin = MockPrebidMobilePluginRenderer( + name: "MockPrebidMobilePluginRenderer", + version: "1.0.0" + ) override func setUp() { super.setUp() - unregisterAllPlugins() + prebidMobilePluginRegister.unregisterAllPlugins() prebidMobilePluginRegister.registerPlugin(plugin) } - - func unregisterAllPlugins() { - let plugins = prebidMobilePluginRegister.getAllPlugins() - for plugin in plugins { - prebidMobilePluginRegister.unregisterPlugin(plugin) - } - } func testRegisterPlugin() { - let containsPlugin = prebidMobilePluginRegister.containsPlugin(plugin) + XCTAssertEqual(true, prebidMobilePluginRegister.containsPlugin(plugin)) + let plugins = prebidMobilePluginRegister.getAllPlugins() - XCTAssertEqual(true, containsPlugin) XCTAssertEqual(1, plugins.count) } - + func testUnregisterPlugin() { prebidMobilePluginRegister.unregisterPlugin(plugin) @@ -43,34 +56,18 @@ class PluginRegisterTest: XCTestCase { } func testGetPluginForBidContainingSampleCustomRenderer() { - let bidResponse = Bid( + let bid = Bid( bid: RawSampleCustomRendererBidFabricator.makeSampleCustomRendererBid( - rendererName: "SampleCustomRenderer", + rendererName: "MockPrebidMobilePluginRenderer", rendererVersion: "1.0.0" ) ) let pluginRenderer = prebidMobilePluginRegister.getPluginForPreferredRenderer( - bid: bidResponse + bid: bid ) + XCTAssertEqual(pluginRenderer.name, plugin.name) XCTAssertEqual(pluginRenderer.version, plugin.version) } - - func testGetRTBListOfRenderersFor() { - let adUnitConfigBanner = AdUnitConfig( - configId: "configID", - size: CGSize( - width: 300, - height: 250 - ) - ) - let adUnitConfigError = AdUnitConfig(configId: "configID") - - var renderers = prebidMobilePluginRegister.getRTBListOfRenderersFor(for: adUnitConfigBanner) - XCTAssertEqual(1, renderers.count) - - renderers = prebidMobilePluginRegister.getRTBListOfRenderersFor(for: adUnitConfigError) - XCTAssertEqual(1, renderers.count) - } } diff --git a/PrebidMobileTests/RenderingTests/Tests/PrebidTest.swift b/PrebidMobileTests/RenderingTests/Tests/PrebidTest.swift index 5f9fdfb91..8521f47ae 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PrebidTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PrebidTest.swift @@ -33,6 +33,7 @@ class PrebidTest: XCTestCase { sdkConfiguration = nil Prebid.reset() + PrebidMobilePluginRegister.shared.unregisterAllPlugins() super.tearDown() } @@ -329,6 +330,15 @@ class PrebidTest: XCTestCase { XCTAssertEqual(Prebid.shared.creativeFactoryTimeoutPreRenderContent, creativeFactoryTimeoutPreRenderContent) } + func testRegisterSDKRenderer() { + XCTAssertTrue(PrebidMobilePluginRegister.shared.getAllPlugins().isEmpty) + + Prebid.initializeSDK() + + XCTAssertTrue(PrebidMobilePluginRegister.shared.getAllPlugins().count == 1) + XCTAssertTrue(PrebidMobilePluginRegister.shared.getAllPlugins().first?.name == PREBID_MOBILE_RENDERER_NAME) + } + // MARK: - Private Methods private func checkInitialValue(sdkConfiguration: Prebid, file: StaticString = #file, line: UInt = #line) { From d7e5cf93cb2d4ed04c919a9bbcbeb446c095f962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20D=C3=A9pierre?= <146739800+github-richard-depierre@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:21:06 +0100 Subject: [PATCH 15/23] Fix naming of the PluginEventListener -> PluginEventDelegate (#1046) --- .../{PluginEventListener.swift => PluginEventDelegate.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename PrebidMobile/PrebidMobileRendering/PluginRenderer/{PluginEventListener.swift => PluginEventDelegate.swift} (100%) diff --git a/PrebidMobile/PrebidMobileRendering/PluginRenderer/PluginEventListener.swift b/PrebidMobile/PrebidMobileRendering/PluginRenderer/PluginEventDelegate.swift similarity index 100% rename from PrebidMobile/PrebidMobileRendering/PluginRenderer/PluginEventListener.swift rename to PrebidMobile/PrebidMobileRendering/PluginRenderer/PluginEventDelegate.swift From 792779c58e0abde495610b5e7fd1a35c6f0b6f39 Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:28:00 +0200 Subject: [PATCH 16/23] Add missing file to project (#1079) --- PrebidMobile.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index 045327e3c..d0b514d07 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -109,6 +109,7 @@ 53514CF22D0851DE00A480C0 /* InterstitialControllerInteractionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CF12D0851DE00A480C0 /* InterstitialControllerInteractionDelegate.swift */; }; 5355ACA929C454070014F16E /* VAST_with_empty_companion.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5355ACA829C454070014F16E /* VAST_with_empty_companion.xml */; }; 5355ACAB29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355ACAA29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift */; }; + 536469C02D2FD78200F50B6D /* PluginEventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536469BF2D2FD78200F50B6D /* PluginEventDelegate.swift */; }; 536A39262A84C50F00B1CCEA /* StringExtensionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536A39252A84C50F00B1CCEA /* StringExtensionsTest.swift */; }; 536A427F282D11DA0069E9B2 /* PrebidServerConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536A427E282D11DA0069E9B2 /* PrebidServerConnection.swift */; }; 536A4283282D12E80069E9B2 /* PrebidServerConnectionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536A4282282D12E80069E9B2 /* PrebidServerConnectionProtocol.swift */; }; @@ -812,7 +813,6 @@ A908694229E05EAF00B37479 /* PrebidMobilePluginRegister.swift in Sources */ = {isa = PBXBuildFile; fileRef = A908694129E05EAF00B37479 /* PrebidMobilePluginRegister.swift */; }; A908694429E05ED500B37479 /* PrebidMobilePluginRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A908694329E05ED500B37479 /* PrebidMobilePluginRenderer.swift */; }; A908694629E05F7900B37479 /* PrebidRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A908694529E05F7900B37479 /* PrebidRenderer.swift */; }; - A9750D7A2ABB9A300066E4E6 /* PluginEventListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9750D792ABB9A300066E4E6 /* PluginEventListener.swift */; }; FA5AD5E42271FA4100C8F274 /* ConstantsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5AD5E32271FA4100C8F274 /* ConstantsTest.swift */; }; FA9D7F2722E8A83D006FCBEF /* AdViewUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA9D7F2622E8A83D006FCBEF /* AdViewUtilsTests.swift */; }; FAA29904242D1C27002ACBF2 /* TargetingObjCTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FAA29903242D1C27002ACBF2 /* TargetingObjCTests.m */; }; @@ -1002,6 +1002,7 @@ 53514CF12D0851DE00A480C0 /* InterstitialControllerInteractionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterstitialControllerInteractionDelegate.swift; sourceTree = ""; }; 5355ACA829C454070014F16E /* VAST_with_empty_companion.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = VAST_with_empty_companion.xml; sourceTree = ""; }; 5355ACAA29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreativeModelCollectionMakerVASTTests.swift; sourceTree = ""; }; + 536469BF2D2FD78200F50B6D /* PluginEventDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginEventDelegate.swift; sourceTree = ""; }; 536A39252A84C50F00B1CCEA /* StringExtensionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensionsTest.swift; sourceTree = ""; }; 536A427E282D11DA0069E9B2 /* PrebidServerConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidServerConnection.swift; sourceTree = ""; }; 536A4282282D12E80069E9B2 /* PrebidServerConnectionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidServerConnectionProtocol.swift; sourceTree = ""; }; @@ -1737,7 +1738,6 @@ A908694129E05EAF00B37479 /* PrebidMobilePluginRegister.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidMobilePluginRegister.swift; sourceTree = ""; }; A908694329E05ED500B37479 /* PrebidMobilePluginRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidMobilePluginRenderer.swift; sourceTree = ""; }; A908694529E05F7900B37479 /* PrebidRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidRenderer.swift; sourceTree = ""; }; - A9750D792ABB9A300066E4E6 /* PluginEventListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginEventListener.swift; sourceTree = ""; }; FA4A88432497A99D00FDCBB6 /* Swizzling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Swizzling.swift; sourceTree = ""; }; FA5AD5E32271FA4100C8F274 /* ConstantsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsTest.swift; sourceTree = ""; }; FA85F9B4264946FC00B8BE72 /* TestUtils.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TestUtils.xcodeproj; path = ../tools/TestUtils/TestUtils.xcodeproj; sourceTree = ""; }; @@ -3292,10 +3292,10 @@ A908694029E05E8D00B37479 /* PluginRenderer */ = { isa = PBXGroup; children = ( + 536469BF2D2FD78200F50B6D /* PluginEventDelegate.swift */, A908694129E05EAF00B37479 /* PrebidMobilePluginRegister.swift */, A908694329E05ED500B37479 /* PrebidMobilePluginRenderer.swift */, 53BFCD302CFE435D00A3287A /* PrebidMobilePluginRenderer+Extensions.swift */, - A9750D792ABB9A300066E4E6 /* PluginEventListener.swift */, ); path = PluginRenderer; sourceTree = ""; @@ -4165,7 +4165,6 @@ 5BC37AC6271F1D0100444D5E /* PBMSKAdNetworksParameterBuilder.m in Sources */, 53CF5B3729DC690600613E84 /* VideoAdUnit.swift in Sources */, 5BC37A1C271F1D0000444D5E /* PBMErrorFamily.m in Sources */, - A9750D7A2ABB9A300066E4E6 /* PluginEventListener.swift in Sources */, FAEE4D28262DC2B200AD9966 /* PbFindSizeError.swift in Sources */, 5BC37990271F1D0000444D5E /* PBMVideoCreative.m in Sources */, FAEE4D2A262DC2B200AD9966 /* IMAUtils.swift in Sources */, @@ -4387,6 +4386,7 @@ 5BC37A8C271F1D0000444D5E /* BaseInterstitialAdUnitProtocol.swift in Sources */, 5BC3797A271F1D0000444D5E /* PBMVastRequester.m in Sources */, 53322AA3282D45EE0049229D /* PrebidServerEventTracker.swift in Sources */, + 536469C02D2FD78200F50B6D /* PluginEventDelegate.swift in Sources */, 5BC37901271F1CFF00444D5E /* PBMORTBImp.m in Sources */, 5BC37AE0271F1D0100444D5E /* AdFormat.swift in Sources */, 3CADBD252BFDDE01007B6913 /* PBMORTBRendererConfig.m in Sources */, From 499fd156043b0edb5788db7aa2d96ab9a73e1e09 Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:01:26 +0200 Subject: [PATCH 17/23] Do not send device size for interstitial ads (#1078) * feat: do not set device size for interstitials * feat: remove extra spacing --- .../PBMCore/PBMPrebidParameterBuilder.m | 8 ---- .../PrebidParameterBuilderTest.swift | 45 ++++++++++++++----- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m index f443dd3ca..73da9b08f 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMPrebidParameterBuilder.m @@ -152,14 +152,6 @@ - (void)buildBidRequest:(nonnull PBMORTBBidRequest *)bidRequest { } formats = newFormats; } else if (isInterstitial) { - NSNumber * const w = bidRequest.device.w; - NSNumber * const h = bidRequest.device.h; - if (w && h) { - PBMORTBFormat * const newFormat = [[PBMORTBFormat alloc] init]; - newFormat.w = w; - newFormat.h = h; - formats = @[newFormat]; - } if (self.adConfiguration.minSizePerc && isHTML) { const CGSize minSizePerc = self.adConfiguration.minSizePerc.CGSizeValue; PBMORTBDeviceExtPrebidInterstitial * const interstitial = bidRequest.device.extPrebid.interstitial; diff --git a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift index 71a7d97bb..2075ddd35 100644 --- a/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/ParameterBuilderTests/PrebidParameterBuilderTest.swift @@ -111,6 +111,20 @@ class PrebidParameterBuilderTest: XCTestCase { PBMAssertEq(bidRequest.imp.first?.banner?.format[1].h, 90) } + func testInterstitialDeviceSizeNotSet() { + let adUnitConfig = AdUnitConfig(configId: "test-config-id", size: CGSize.zero) + adUnitConfig.adFormats = [.banner, .video] + adUnitConfig.adConfiguration.isInterstitialAd = true + + let bidRequest = buildBidRequest(with: adUnitConfig) + + // If no ad size is specified, the SDK should not assign a device size for interstitials + XCTAssertEqual(bidRequest.imp.first?.banner?.format.count, 0) + + XCTAssertNil(bidRequest.imp.first?.video?.w) + XCTAssertNil(bidRequest.imp.first?.video?.h) + } + func testVideo() { let configId = "b6260e2b-bc4c-4d10-bdb5-f7bdd62f5ed4" let adUnitConfig = AdUnitConfig(configId: configId, size: CGSize(width: 320, height: 50)) @@ -862,17 +876,26 @@ class PrebidParameterBuilderTest: XCTestCase { func buildBidRequest(with adUnitConfig: AdUnitConfig) -> PBMORTBBidRequest { let bidRequest = PBMORTBBidRequest() - PBMBasicParameterBuilder(adConfiguration: adUnitConfig.adConfiguration, - sdkConfiguration: sdkConfiguration, - sdkVersion: "MOCK_SDK_VERSION", - targeting: targeting) - .build(bidRequest) - - PBMPrebidParameterBuilder(adConfiguration: adUnitConfig, - sdkConfiguration: sdkConfiguration, - targeting: targeting, - userAgentService: MockUserAgentService()) - .build(bidRequest) + PBMBasicParameterBuilder( + adConfiguration: adUnitConfig.adConfiguration, + sdkConfiguration: sdkConfiguration, + sdkVersion: "MOCK_SDK_VERSION", + targeting: targeting + ) + .build(bidRequest) + + DeviceInfoParameterBuilder( + deviceAccessManager: MockDeviceAccessManager(rootViewController: nil) + ) + .build(bidRequest) + + PBMPrebidParameterBuilder( + adConfiguration: adUnitConfig, + sdkConfiguration: sdkConfiguration, + targeting: targeting, + userAgentService: MockUserAgentService() + ) + .build(bidRequest) return bidRequest } From 839d5b8ca37aad7161e5e1b6d0b0ed7813019d95 Mon Sep 17 00:00:00 2001 From: Marcus Kida Date: Fri, 10 Jan 2025 10:08:30 +0100 Subject: [PATCH 18/23] Implement Event Delegate (#1035) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Implement PrebidEventDelegate * feat: document event delegate * feat: call the delegate asynchronously and move to background --------- Co-authored-by: Steffen Müller --- PrebidMobile.xcodeproj/project.pbxproj | 12 ++++ .../ConfigurationAndTargeting/Prebid.swift | 3 + .../PrebidEventDelegate.swift | 28 +++++++++ .../Prebid/PBMCore/PBMBidRequester.m | 6 +- .../PBMCore/PBMEventDelegateHelper.swift | 26 ++++++++ .../PrebidEventDelegateTests.swift | 62 +++++++++++++++++++ 6 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 PrebidMobile/ConfigurationAndTargeting/PrebidEventDelegate.swift create mode 100644 PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMEventDelegateHelper.swift create mode 100644 PrebidMobileTests/PrebidEventDelegateTests.swift diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index d0b514d07..4edb86043 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 2A9DDDCC2C5BE190000EA4A0 /* PrebidEventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9DDDCB2C5BE190000EA4A0 /* PrebidEventDelegate.swift */; }; + 2A9DDDD42C63665A000EA4A0 /* PBMEventDelegateHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9DDDD32C63665A000EA4A0 /* PBMEventDelegateHelper.swift */; }; + 2A9DDDD82C63A88C000EA4A0 /* PrebidEventDelegateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9DDDD72C63A88C000EA4A0 /* PrebidEventDelegateTests.swift */; }; 34C9CD5F2850CE6300FB5451 /* OMSDKVersionProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 34C9CD5D2850CE6300FB5451 /* OMSDKVersionProvider.h */; settings = {ATTRIBUTES = (Private, ); }; }; 34C9CD602850CE6300FB5451 /* OMSDKVersionProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C9CD5E2850CE6300FB5451 /* OMSDKVersionProvider.m */; }; 34F51A562850F50E0063763D /* OMSDK-Static_Prebidorg.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34F51A552850F50E0063763D /* OMSDK-Static_Prebidorg.xcframework */; }; @@ -897,6 +900,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2A9DDDCB2C5BE190000EA4A0 /* PrebidEventDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidEventDelegate.swift; sourceTree = ""; }; + 2A9DDDD32C63665A000EA4A0 /* PBMEventDelegateHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PBMEventDelegateHelper.swift; sourceTree = ""; }; + 2A9DDDD72C63A88C000EA4A0 /* PrebidEventDelegateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidEventDelegateTests.swift; sourceTree = ""; }; 34C9CD5D2850CE6300FB5451 /* OMSDKVersionProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OMSDKVersionProvider.h; sourceTree = ""; }; 34C9CD5E2850CE6300FB5451 /* OMSDKVersionProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OMSDKVersionProvider.m; sourceTree = ""; }; 34F51A552850F50E0063763D /* OMSDK-Static_Prebidorg.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = "OMSDK-Static_Prebidorg.xcframework"; path = "Frameworks/OMSDK-Static_Prebidorg.xcframework"; sourceTree = ""; }; @@ -2042,6 +2048,7 @@ 53C8FE1629C0851300ED9230 /* ClickbrowserType.swift */, 53138B292A71152200B18B5C /* PrebidSDKInitializer.swift */, 53138B2B2A7132CD00B18B5C /* PrebidGAMVersionChecker.swift */, + 2A9DDDCB2C5BE190000EA4A0 /* PrebidEventDelegate.swift */, ); path = ConfigurationAndTargeting; sourceTree = ""; @@ -2513,6 +2520,7 @@ 5BC377C6271F1CFF00444D5E /* PBMWinNotifierBlock.h */, 5BC3780D271F1CFF00444D5E /* PBMWinNotifierFactoryBlock.h */, 5BC37831271F1CFF00444D5E /* TransactionFactory */, + 2A9DDDD32C63665A000EA4A0 /* PBMEventDelegateHelper.swift */, ); path = PBMCore; sourceTree = ""; @@ -3431,6 +3439,7 @@ 606FAC5122022932008EAE5E /* AdUnitSwizzleHelper.swift */, 38F03B262576421400E026A2 /* CacheManagerTests.swift */, 38F03B322576624C00E026A2 /* TrackerManagerTests.swift */, + 2A9DDDD72C63A88C000EA4A0 /* PrebidEventDelegateTests.swift */, 47D9A35B40BBC16996BC05A9 /* RenderingTests */, ); path = PrebidMobileTests; @@ -3926,6 +3935,7 @@ 92C85D9627A9DC9D0080BAC5 /* NativeAdTests.swift in Sources */, 922AFCE42735573900732C53 /* ServerConnectionTest.swift in Sources */, 925D5D9F2737C33800A8A2B5 /* NetworkParameterBuilderTest.swift in Sources */, + 2A9DDDD82C63A88C000EA4A0 /* PrebidEventDelegateTests.swift in Sources */, 922AFD4B27372A0500732C53 /* PBMHTMLCreativeTest_NoMRAID.swift in Sources */, 9743CB86235F264B002E2CAA /* NativeEventTrackerTests.swift in Sources */, 922AFCE12735424F00732C53 /* PBMAssert.swift in Sources */, @@ -4142,6 +4152,7 @@ 5BC378D7271F1CFF00444D5E /* PBMORTBUser.m in Sources */, 5BC3798D271F1D0000444D5E /* PBMAdRefreshOptions.m in Sources */, 5BC378CC271F1CFF00444D5E /* PBMWeakTimerTargetBox.m in Sources */, + 2A9DDDD42C63665A000EA4A0 /* PBMEventDelegateHelper.swift in Sources */, 5BC379AC271F1D0000444D5E /* PBMModalManager.m in Sources */, FAEE4D1C262DC2B200AD9966 /* InstreamVideoAdUnit.swift in Sources */, 53B221D12A0E3D3D00C91CCB /* PrebidJSLibraryManager.swift in Sources */, @@ -4227,6 +4238,7 @@ 5BC37997271F1D0000444D5E /* PBMAbstractCreative.m in Sources */, 53A447D12CAECA3C008DE6C0 /* BaseRewardedAdUnit.swift in Sources */, 92EE5A0D27F9D292003D7691 /* Position.swift in Sources */, + 2A9DDDCC2C5BE190000EA4A0 /* PrebidEventDelegate.swift in Sources */, 92C3A83627F989E200DC05E9 /* AutoRefreshCountConfig.swift in Sources */, 534C61402CB5504D0026119A /* PBMCloseActionManager.m in Sources */, 5BC37A72271F1D0000444D5E /* BidResponse.swift in Sources */, diff --git a/PrebidMobile/ConfigurationAndTargeting/Prebid.swift b/PrebidMobile/ConfigurationAndTargeting/Prebid.swift index f93c18202..f5423e6ad 100644 --- a/PrebidMobile/ConfigurationAndTargeting/Prebid.swift +++ b/PrebidMobile/ConfigurationAndTargeting/Prebid.swift @@ -54,6 +54,9 @@ public class Prebid: NSObject { /// Stored bid responses identified by bidder names. public var storedBidResponses: [String: String] = [:] + /// Optional Delegate which returns Request and Response Data for further processing + public weak var eventDelegate: PrebidEventDelegate? + /// This property is set by the developer when he is willing to assign the assetID for Native ad. public var shouldAssignNativeAssetID : Bool = false diff --git a/PrebidMobile/ConfigurationAndTargeting/PrebidEventDelegate.swift b/PrebidMobile/ConfigurationAndTargeting/PrebidEventDelegate.swift new file mode 100644 index 000000000..82b2e4b39 --- /dev/null +++ b/PrebidMobile/ConfigurationAndTargeting/PrebidEventDelegate.swift @@ -0,0 +1,28 @@ +/*   Copyright 2018-2021 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +@objc +public protocol PrebidEventDelegate { + /// Notifies the delegate when a Prebid bid request has finished. + /// + /// This method is called on a global background thread. + /// + /// - Parameters: + /// - requestData: The Prebid Server request data that was sent. + /// - responseData: The Prebid Server response data that was received. + func prebidBidRequestDidFinish(requestData: Data?, responseData: Data?) +} diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMBidRequester.m b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMBidRequester.m index dc4437dde..e75a33add 100644 --- a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMBidRequester.m +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMBidRequester.m @@ -94,10 +94,12 @@ - (void)makeRequestWithCompletion:(void (^)(BidResponse *, NSError *))completion const NSTimeInterval postTimeout = (dynamicTimeout_onRead ? dynamicTimeout_onRead.doubleValue : (rawTimeoutMS_onRead / 1000.0)); + NSData *rtbRequestData = [requestString dataUsingEncoding:NSUTF8StringEncoding]; + @weakify(self); NSDate * const requestDate = [NSDate date]; [self.connection post:requestServerURL - data:[requestString dataUsingEncoding:NSUTF8StringEncoding] + data:rtbRequestData timeout:postTimeout callback:^(PrebidServerResponse * _Nonnull serverResponse) { @strongify(self); @@ -157,6 +159,8 @@ - (void)makeRequestWithCompletion:(void (^)(BidResponse *, NSError *))completion } completion(bidResponse, trasformationError); + [Prebid.shared callEventDelegateAsync_prebidBidRequestDidFinishWithRequestData:rtbRequestData + responseData:serverResponse.rawData]; }]; } diff --git a/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMEventDelegateHelper.swift b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMEventDelegateHelper.swift new file mode 100644 index 000000000..ff4729337 --- /dev/null +++ b/PrebidMobile/PrebidMobileRendering/Prebid/PBMCore/PBMEventDelegateHelper.swift @@ -0,0 +1,26 @@ +/*   Copyright 2018-2021 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +@objc public extension Prebid { + func callEventDelegateAsync_prebidBidRequestDidFinishWith(requestData: Data?, responseData: Data?) { + DispatchQueue.global(qos: .background).async { + if let delegate = self.eventDelegate { + delegate.prebidBidRequestDidFinish(requestData: requestData, responseData: responseData) + } + } + } +} diff --git a/PrebidMobileTests/PrebidEventDelegateTests.swift b/PrebidMobileTests/PrebidEventDelegateTests.swift new file mode 100644 index 000000000..ebcac4ac7 --- /dev/null +++ b/PrebidMobileTests/PrebidEventDelegateTests.swift @@ -0,0 +1,62 @@ +/*   Copyright 2018-2021 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import XCTest +@testable import PrebidMobile + +private typealias Callback = (Data?, Data?) -> Void + +class PrebidEventDelegateTests: XCTestCase { + + let mockRequestData = "req".data(using: .utf8) + let mockResponseData = "res".data(using: .utf8) + + var delegate: PrebidEventDelegate? + + func test_eventDelegate_isCalled() { + let exp = expectation(description: "Expect PrebidEventDelegate to be called") + + delegate = PrebidEventDelegateTestsMockDelegate(onRequestDidFinish: { requestData, responseData in + XCTAssertEqual(requestData, self.mockRequestData) + XCTAssertEqual(responseData, self.mockResponseData) + XCTAssertFalse(Thread.isMainThread) + exp.fulfill() + }) + + Prebid.shared.eventDelegate = delegate + + Prebid.shared.callEventDelegateAsync_prebidBidRequestDidFinishWith(requestData: mockRequestData, responseData: mockResponseData) + waitForExpectations(timeout: 1.0) + } + + func test_callEventDelegateAsync_doesNothing_whenDelegateIsNil() { + /// This test aims to ensure that there is no nullpointer exception if the delegate is unset + /// and a call to `callEventDelegateAsync_prebidBidRequestDidFinishWith(_:,:_)` is made + Prebid.shared.eventDelegate = nil + Prebid.shared.callEventDelegateAsync_prebidBidRequestDidFinishWith(requestData: mockRequestData, responseData: mockResponseData) + } +} + +private class PrebidEventDelegateTestsMockDelegate: PrebidEventDelegate { + private let onRequestDidFinish: Callback + + init(onRequestDidFinish: @escaping Callback) { + self.onRequestDidFinish = onRequestDidFinish + } + + func prebidBidRequestDidFinish(requestData: Data?, responseData: Data?) { + onRequestDidFinish(requestData, responseData) + } +} From 110f6d298977c43c0f0bb6d6add86602f33b7298 Mon Sep 17 00:00:00 2001 From: Jono Sligh <139150153+jsligh@users.noreply.github.com> Date: Fri, 10 Jan 2025 03:28:41 -0600 Subject: [PATCH 19/23] Custom logger implementation on iOS (#1026) * Made custom logger. * Forgot to set to nil afterwards. * Fixing customLogger Implementation * Fixing issues. * Included log function in other functions. * Trying to fix build errors. * Does this fix it? * More testing. * fix: fix error in project settings * fix: add missing file * feat: finalize third-party logging system --------- Co-authored-by: Olena Stepaniuk --- PrebidMobile.xcodeproj/project.pbxproj | 20 ++- PrebidMobile/Constants.swift | 8 +- PrebidMobile/Logging/Log.swift | 152 +++++++----------- PrebidMobile/Logging/PrebidLogger.swift | 84 ++++++++++ PrebidMobile/Logging/SDKConsoleLogger.swift | 98 +++++++++++ .../String+Extensions.swift | 8 + .../Utilities/PBMFunctions.m | 2 +- PrebidMobile/Utils/URL+Extensions.swift | 6 + .../RenderingTests/Tests/LogTest.swift | 80 ++++++++- 9 files changed, 350 insertions(+), 108 deletions(-) create mode 100644 PrebidMobile/Logging/PrebidLogger.swift create mode 100644 PrebidMobile/Logging/SDKConsoleLogger.swift diff --git a/PrebidMobile.xcodeproj/project.pbxproj b/PrebidMobile.xcodeproj/project.pbxproj index 4edb86043..345601082 100644 --- a/PrebidMobile.xcodeproj/project.pbxproj +++ b/PrebidMobile.xcodeproj/project.pbxproj @@ -112,7 +112,9 @@ 53514CF22D0851DE00A480C0 /* InterstitialControllerInteractionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CF12D0851DE00A480C0 /* InterstitialControllerInteractionDelegate.swift */; }; 5355ACA929C454070014F16E /* VAST_with_empty_companion.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5355ACA829C454070014F16E /* VAST_with_empty_companion.xml */; }; 5355ACAB29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5355ACAA29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift */; }; - 536469C02D2FD78200F50B6D /* PluginEventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536469BF2D2FD78200F50B6D /* PluginEventDelegate.swift */; }; + 535ADE0B2D2E970200DB888F /* SDKConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9F0999C2C78CC8A007DB464 /* SDKConsoleLogger.swift */; }; + 535ADE102D2E987E00DB888F /* PluginEventDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535ADE0F2D2E987E00DB888F /* PluginEventDelegate.swift */; }; + 535ADE122D2EA2F500DB888F /* PrebidLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535ADE112D2EA2F500DB888F /* PrebidLogger.swift */; }; 536A39262A84C50F00B1CCEA /* StringExtensionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536A39252A84C50F00B1CCEA /* StringExtensionsTest.swift */; }; 536A427F282D11DA0069E9B2 /* PrebidServerConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536A427E282D11DA0069E9B2 /* PrebidServerConnection.swift */; }; 536A4283282D12E80069E9B2 /* PrebidServerConnectionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536A4282282D12E80069E9B2 /* PrebidServerConnectionProtocol.swift */; }; @@ -816,6 +818,7 @@ A908694229E05EAF00B37479 /* PrebidMobilePluginRegister.swift in Sources */ = {isa = PBXBuildFile; fileRef = A908694129E05EAF00B37479 /* PrebidMobilePluginRegister.swift */; }; A908694429E05ED500B37479 /* PrebidMobilePluginRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A908694329E05ED500B37479 /* PrebidMobilePluginRenderer.swift */; }; A908694629E05F7900B37479 /* PrebidRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A908694529E05F7900B37479 /* PrebidRenderer.swift */; }; + F9F0999B2C78C668007DB464 /* SDKConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91B60A82C52A73500DA9796 /* SDKConsoleLogger.swift */; }; FA5AD5E42271FA4100C8F274 /* ConstantsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5AD5E32271FA4100C8F274 /* ConstantsTest.swift */; }; FA9D7F2722E8A83D006FCBEF /* AdViewUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA9D7F2622E8A83D006FCBEF /* AdViewUtilsTests.swift */; }; FAA29904242D1C27002ACBF2 /* TargetingObjCTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FAA29903242D1C27002ACBF2 /* TargetingObjCTests.m */; }; @@ -1008,7 +1011,8 @@ 53514CF12D0851DE00A480C0 /* InterstitialControllerInteractionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterstitialControllerInteractionDelegate.swift; sourceTree = ""; }; 5355ACA829C454070014F16E /* VAST_with_empty_companion.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = VAST_with_empty_companion.xml; sourceTree = ""; }; 5355ACAA29C454770014F16E /* CreativeModelCollectionMakerVASTTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreativeModelCollectionMakerVASTTests.swift; sourceTree = ""; }; - 536469BF2D2FD78200F50B6D /* PluginEventDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginEventDelegate.swift; sourceTree = ""; }; + 535ADE0F2D2E987E00DB888F /* PluginEventDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginEventDelegate.swift; sourceTree = ""; }; + 535ADE112D2EA2F500DB888F /* PrebidLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidLogger.swift; sourceTree = ""; }; 536A39252A84C50F00B1CCEA /* StringExtensionsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensionsTest.swift; sourceTree = ""; }; 536A427E282D11DA0069E9B2 /* PrebidServerConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidServerConnection.swift; sourceTree = ""; }; 536A4282282D12E80069E9B2 /* PrebidServerConnectionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidServerConnectionProtocol.swift; sourceTree = ""; }; @@ -1744,6 +1748,7 @@ A908694129E05EAF00B37479 /* PrebidMobilePluginRegister.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidMobilePluginRegister.swift; sourceTree = ""; }; A908694329E05ED500B37479 /* PrebidMobilePluginRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidMobilePluginRenderer.swift; sourceTree = ""; }; A908694529E05F7900B37479 /* PrebidRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidRenderer.swift; sourceTree = ""; }; + F9F0999C2C78CC8A007DB464 /* SDKConsoleLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKConsoleLogger.swift; sourceTree = ""; }; FA4A88432497A99D00FDCBB6 /* Swizzling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Swizzling.swift; sourceTree = ""; }; FA5AD5E32271FA4100C8F274 /* ConstantsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsTest.swift; sourceTree = ""; }; FA85F9B4264946FC00B8BE72 /* TestUtils.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TestUtils.xcodeproj; path = ../tools/TestUtils/TestUtils.xcodeproj; sourceTree = ""; }; @@ -1970,6 +1975,8 @@ FAEE4CE2262DC2B200AD9966 /* Log.swift */, 531CF21827E8FC1B005E5ABE /* LogLevel.swift */, 53F096C927E917D40058C94C /* Log+Extensions.h */, + 535ADE112D2EA2F500DB888F /* PrebidLogger.swift */, + F9F0999C2C78CC8A007DB464 /* SDKConsoleLogger.swift */, ); path = Logging; sourceTree = ""; @@ -2056,6 +2063,7 @@ 5BC3764E271F1CFD00444D5E /* ExtensionsAndWrappers */ = { isa = PBXGroup; children = ( + 5BC37666271F1CFD00444D5E /* NSTimer */, 5BC37652271F1CFD00444D5E /* Exposure */, 5BC37659271F1CFD00444D5E /* NSDictionary+PBMExtensions.h */, 5BC3765F271F1CFD00444D5E /* NSDictionary+PBMExtensions.m */, @@ -2067,18 +2075,17 @@ 5BC3765A271F1CFD00444D5E /* NSNumber+PBMORTBNative.m */, 5BC3764F271F1CFD00444D5E /* NSString+PBMExtensions.h */, 5BC37662271F1CFD00444D5E /* NSString+PBMExtensions.m */, - 5BC37666271F1CFD00444D5E /* NSTimer */, 5BC3765B271F1CFD00444D5E /* PBMTouchDownRecognizer.h */, 5BC3766E271F1CFD00444D5E /* PBMTouchDownRecognizer.m */, 5BC37650271F1CFD00444D5E /* UIView+PBMExtensions.h */, 5BC37661271F1CFD00444D5E /* UIView+PBMExtensions.m */, 5BC3765E271F1CFD00444D5E /* UIWindow+PBMExtensions.h */, 5BC3766F271F1CFD00444D5E /* UIWindow+PBMExtensions.m */, - 53C925012990FB30009E6F94 /* String+Extensions.swift */, 53D3C38B2C2BEF9B0074D99B /* NSURL+PBMExtensions.h */, 53D3C38C2C2BEF9B0074D99B /* NSURL+PBMExtensions.m */, 535145DE2CCB758800D40B19 /* NSObject+PBMExtensions.h */, 535145DF2CCB758800D40B19 /* NSObject+PBMExtensions.m */, + 53C925012990FB30009E6F94 /* String+Extensions.swift */, ); path = ExtensionsAndWrappers; sourceTree = ""; @@ -3300,7 +3307,7 @@ A908694029E05E8D00B37479 /* PluginRenderer */ = { isa = PBXGroup; children = ( - 536469BF2D2FD78200F50B6D /* PluginEventDelegate.swift */, + 535ADE0F2D2E987E00DB888F /* PluginEventDelegate.swift */, A908694129E05EAF00B37479 /* PrebidMobilePluginRegister.swift */, A908694329E05ED500B37479 /* PrebidMobilePluginRenderer.swift */, 53BFCD302CFE435D00A3287A /* PrebidMobilePluginRenderer+Extensions.swift */, @@ -4119,6 +4126,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 535ADE0B2D2E970200DB888F /* SDKConsoleLogger.swift in Sources */, 5BC37AB1271F1D0000444D5E /* DemandResponseInfo.swift in Sources */, 5BC37AAE271F1D0000444D5E /* BannerEventHandlerStandalone.swift in Sources */, 5BC378DF271F1CFF00444D5E /* PBMORTBApp.m in Sources */, @@ -4155,6 +4163,7 @@ 2A9DDDD42C63665A000EA4A0 /* PBMEventDelegateHelper.swift in Sources */, 5BC379AC271F1D0000444D5E /* PBMModalManager.m in Sources */, FAEE4D1C262DC2B200AD9966 /* InstreamVideoAdUnit.swift in Sources */, + 535ADE102D2E987E00DB888F /* PluginEventDelegate.swift in Sources */, 53B221D12A0E3D3D00C91CCB /* PrebidJSLibraryManager.swift in Sources */, 5BC37970271F1D0000444D5E /* PBMVastAbstractAd.m in Sources */, 53BDBF89293F5EFF004B6DE8 /* InternalUserConsentDataManager.m in Sources */, @@ -4386,6 +4395,7 @@ 928E5A7027F0F743000ADA1A /* ContextSubType.swift in Sources */, 5BC37A20271F1D0000444D5E /* PBMORTBMacrosHelper.m in Sources */, 92989653275FE98C00D3C174 /* PBMORTBContentData.m in Sources */, + 535ADE122D2EA2F500DB888F /* PrebidLogger.swift in Sources */, 5BC378BB271F1CFF00444D5E /* NSNumber+PBMORTBNative.m in Sources */, 5BC37961271F1D0000444D5E /* PBMVastAdsBuilder.m in Sources */, 5BC37A91271F1D0000444D5E /* InterstitialEventLoadingDelegate.swift in Sources */, diff --git a/PrebidMobile/Constants.swift b/PrebidMobile/Constants.swift index 719b820da..5f3c788d7 100644 --- a/PrebidMobile/Constants.swift +++ b/PrebidMobile/Constants.swift @@ -17,10 +17,6 @@ import UIKit public let PrebidLocalCacheIdKey = "hb_cache_id_local" -@objc public class Constants: NSObject { - @objc public static let PREBID_VERSION = "2.3.1" -} - extension String { static let EMPTY_String = "" @@ -117,4 +113,8 @@ public class PrebidConstants: NSObject { """ } + + public static let PREBID_VERSION = "2.3.1" + + public static let SDK_NAME = "prebid-mobile-sdk" } diff --git a/PrebidMobile/Logging/Log.swift b/PrebidMobile/Logging/Log.swift index 739007856..a3e80bb87 100644 --- a/PrebidMobile/Logging/Log.swift +++ b/PrebidMobile/Logging/Log.swift @@ -15,66 +15,90 @@ import Foundation +/// This class serves as the central point for all logging operations within the SDK. +/// It allows for categorized logging based on severity levels (e.g., error, warning, debug) and offers options for both console and file-based logging. +/// It also provides the ability to set third-party logger. @objc(PBMLog) @objcMembers public class Log: NSObject { // MARK: - Public properties - public static var dateFormat = "yyyy-MM-dd hh:mm:ssSSS" - public static var dateFormatter: DateFormatter { - let formatter = DateFormatter() - formatter.dateFormat = dateFormat - formatter.locale = Locale.current - formatter.timeZone = TimeZone.current - return formatter - } - + /// The current logging level. Only messages at this level or higher will be logged. public static var logLevel: LogLevel = .debug + + /// Indicates whether logs should also be saved to a file. public static var logToFile = false + + /// Sets a custom logger to handle log messages. + public static func setCustomLogger(_ logger: PrebidLogger) { + self.logger = logger + } public static func error(_ object: Any, filename: String = #file, line: Int = #line, function: String = #function) { - log(object, logLevel: .error, filename: filename, line: line, function: function) + logger.error(object, filename: filename, line: line, function: function) } public static func info(_ object: Any, filename: String = #file, line: Int = #line, function: String = #function) { - log(object, logLevel: .info, filename: filename, line: line, function: function) + logger.info(object, filename: filename, line: line, function: function) } public static func debug(_ object: Any, filename: String = #file, line: Int = #line, function: String = #function) { - log(object, logLevel: .debug, filename: filename, line: line, function: function) + logger.debug(object, filename: filename, line: line, function: function) } public static func verbose(_ object: Any, filename: String = #file, line: Int = #line, function: String = #function) { - log(object, logLevel: .verbose, filename: filename, line: line, function: function) + logger.verbose(object, filename: filename, line: line, function: function) } public static func warn(_ object: Any, filename: String = #file, line: Int = #line, function: String = #function) { - log(object, logLevel: .warn, filename: filename, line: line, function: function) + logger.warn(object, filename: filename, line: line, function: function) } public static func severe(_ object: Any, filename: String = #file, line: Int = #line, function: String = #function) { - log(object, logLevel: .severe, filename: filename, line: line, function: function) + logger.severe(object, filename: filename, line: line, function: function) } public static func whereAmI(filename: String = #file, line: Int = #line, function: String = #function) { - log("", logLevel: .info, filename: filename, line: line, function: function) - } - - static func log(_ object: Any, logLevel: LogLevel, filename: String, line: Int, function: String) { - if isLoggingEnabled(for: logLevel) { - let finalMessage = "\(sdkName): \(Date().toString()) \(logLevel.stringValue)[\(sourceFileName(filePath: filename))]:\(line) \(function) -> \(object)" - print(finalMessage) - serialWriteToLog(finalMessage) - } + logger.whereAmI(filename: filename, line: line, function: function) } + /// Writes a log message to the log file asynchronously. + /// + /// - Parameter message: The log message to be written to the file. public static func serialWriteToLog(_ message: String) { loggingQueue.async { writeToLogFile(message) } } - public static func writeToLogFile(_ message: String) { + /// Reads the contents of the log file as a single string. + public static func getLogFileAsString() -> String? { + loggingQueue.sync { + if let logFileURL = logFileURL { + do { + return try String(contentsOf: logFileURL, encoding: .utf8) + } catch { + Log.error("\(PrebidConstants.SDK_NAME) Error getting log file: \(error.localizedDescription)") + } + } + return nil + } + } + + /// Clears the contents of the log file. + public static func clearLogFile() { + loggingQueue.sync { + do { + if let logFileURL = logFileURL { + try "".data(using: .utf8)?.write(to: logFileURL) + } + } catch { + Log.error("\(PrebidConstants.SDK_NAME) Error clearing log file: \(error.localizedDescription)") + } + } + } + + static func writeToLogFile(_ message: String) { if !Log.logToFile { return } @@ -93,7 +117,7 @@ public class Log: NSObject { try fileHandle.write(contentsOf: data) try fileHandle.close() } catch { - Log.error("\(sdkName) Couldn't write to log file: \(error.localizedDescription)") + Log.error("\(PrebidConstants.SDK_NAME) Couldn't write to log file: \(error.localizedDescription)") } } else { fileHandle.seekToEndOfFile() @@ -106,83 +130,17 @@ public class Log: NSObject { do { try data.write(to: logFileURL) } catch { - Log.error("\(sdkName) Couldn't write to log file URL: \(error.localizedDescription)") - } - } - } - } - - public static func getLogFileAsString() -> String? { - loggingQueue.sync { - if let logFileURL = logFileURL { - do { - return try String(contentsOf: logFileURL, encoding: .utf8) - } catch { - Log.error("\(sdkName) Error getting log file: \(error.localizedDescription)") - } - } - return nil - } - } - - public static func clearLogFile() { - loggingQueue.sync { - do { - if let logFileURL = logFileURL { - try "".data(using: .utf8)?.write(to: logFileURL) + Log.error("\(PrebidConstants.SDK_NAME) Couldn't write to log file URL: \(error.localizedDescription)") } - } catch { - Log.error("\(sdkName) Error clearing log file: \(error.localizedDescription)") } } } - - // MARK: - Internal properties and methods - - private static let sdkName = "prebid-mobile-sdk" - private static let loggingQueue = DispatchQueue(label: sdkName) + // MARK: - Private Properties - private static var logFileURL = getURLForDoc(sdkName + ".txt") + private static var logger: PrebidLogger = SDKConsoleLogger() - private class func isLoggingEnabled(for currentLevel: LogLevel) -> Bool { - #if !(DEBUG) - return false - #endif - - if currentLevel.rawValue < Log.logLevel.rawValue { - return false - } - - return true - } + private static let loggingQueue = DispatchQueue(label: PrebidConstants.SDK_NAME) - private static func getURLForDoc(_ docName: String) -> URL? { - let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory()) - return temporaryDirectoryURL.appendingPathComponent(docName) - } - - private static func sourceFileName(filePath: String) -> String { - let components = filePath.components(separatedBy: "/") - return components.isEmpty ? "" : components.last! - } -} - -extension Date { - func toString() -> String { - return Log.dateFormatter.string(from: self as Date) - } -} - -/// Wrapping Swift.print() within DEBUG flag -/// -/// - Note: *print()* might cause [security vulnerabilities](https://codifiedsecurity.com/mobile-app-security-testing-checklist-ios/) -/// -/// - Parameter object: The object which is to be logged -/// -func print(_ object: Any) { - // Only allowing in DEBUG mode - #if DEBUG - Swift.print(object) - #endif + private static let logFileURL = URL.temporaryURL(for: PrebidConstants.SDK_NAME + ".txt") } diff --git a/PrebidMobile/Logging/PrebidLogger.swift b/PrebidMobile/Logging/PrebidLogger.swift new file mode 100644 index 000000000..1ee31eaa2 --- /dev/null +++ b/PrebidMobile/Logging/PrebidLogger.swift @@ -0,0 +1,84 @@ +/*   Copyright 2018-2025 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import Foundation + +/// A protocol defining methods for logging messages at various levels. +/// +/// Implement this protocol to handle logging in a customizable way. +@objc public protocol PrebidLogger { + + /// Logs an error message. + /// + /// - Parameters: + /// - object: The object or message to log. + /// - filename: The name of the file where the log was generated. + /// - line: The line number where the log was generated. + /// - function: The function name where the log was generated. + func error(_ object: Any, filename: String, line: Int, function: String) + + /// Logs an informational message. + /// + /// - Parameters: + /// - object: The object or message to log. + /// - filename: The name of the file where the log was generated. + /// - line: The line number where the log was generated. + /// - function: The function name where the log was generated. + func info(_ object: Any, filename: String, line: Int, function: String) + + /// Logs a debug message. + /// + /// - Parameters: + /// - object: The object or message to log. + /// - filename: The name of the file where the log was generated. + /// - line: The line number where the log was generated. + /// - function: The function name where the log was generated. + func debug(_ object: Any, filename: String, line: Int, function: String) + + /// Logs a verbose message, typically used for detailed or low-level information. + /// + /// - Parameters: + /// - object: The object or message to log. + /// - filename: The name of the file where the log was generated. + /// - line: The line number where the log was generated. + /// - function: The function name where the log was generated. + func verbose(_ object: Any, filename: String, line: Int, function: String) + + /// Logs a warning message. + /// + /// - Parameters: + /// - object: The object or message to log. + /// - filename: The name of the file where the log was generated. + /// - line: The line number where the log was generated. + /// - function: The function name where the log was generated. + func warn(_ object: Any, filename: String, line: Int, function: String) + + /// Logs a severe error message, indicating a critical issue. + /// + /// - Parameters: + /// - object: The object or message to log. + /// - filename: The name of the file where the log was generated. + /// - line: The line number where the log was generated. + /// - function: The function name where the log was generated. + func severe(_ object: Any, filename: String, line: Int, function: String) + + /// Logs the current location in the code. + /// + /// - Parameters: + /// - filename: The name of the file where this method was called. + /// - line: The line number where this method was called. + /// - function: The function name where this method was called. + func whereAmI(filename: String, line: Int, function: String) +} diff --git a/PrebidMobile/Logging/SDKConsoleLogger.swift b/PrebidMobile/Logging/SDKConsoleLogger.swift new file mode 100644 index 000000000..d6ae57217 --- /dev/null +++ b/PrebidMobile/Logging/SDKConsoleLogger.swift @@ -0,0 +1,98 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// A logger implementation for Prebid SDK that logs messages to the console. +@objc +public class SDKConsoleLogger: NSObject, PrebidLogger { + + public func error(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .error, filename: filename, line: line, function: function) + } + + public func info(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .info, filename: filename, line: line, function: function) + } + + public func debug(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .debug, filename: filename, line: line, function: function) + } + + public func verbose(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .verbose, filename: filename, line: line, function: function) + } + + public func warn(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .warn, filename: filename, line: line, function: function) + } + + public func severe(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .severe, filename: filename, line: line, function: function) + } + + public func whereAmI(filename: String, line: Int, function: String) { + log("", logLevel: .info, filename: filename, line: line, function: function) + } + + func log(_ object: Any, logLevel: LogLevel, filename: String, line: Int, function: String) { + if isLoggingEnabled(for: logLevel) { + let finalMessage = "\(PrebidConstants.SDK_NAME): \(Date().toString()) \(logLevel.stringValue)[\(filename.sourceFileName())]:\(line) \(function) -> \(object)" + print(finalMessage) + Log.serialWriteToLog(finalMessage) + } + } + + // MARK: - Private methods + + private func isLoggingEnabled(for currentLevel: LogLevel) -> Bool { + #if !(DEBUG) + return false + #endif + + if currentLevel.rawValue < Log.logLevel.rawValue { + return false + } + + return true + } +} + +fileprivate extension Date { + + func toString() -> String { + let dateFormat = "yyyy-MM-dd hh:mm:ssSSS" + + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = dateFormat + dateFormatter.locale = Locale.current + dateFormatter.timeZone = TimeZone.current + + return dateFormatter.string(from: self as Date) + } +} + +/// Wrapping Swift.print() within DEBUG flag +/// +/// - Note: *print()* might cause [security vulnerabilities](https://codifiedsecurity.com/mobile-app-security-testing-checklist-ios/) +/// +/// - Parameter object: The object which is to be logged +/// +func print(_ object: Any) { + // Only allowing in DEBUG mode + #if DEBUG + Swift.print(object) + #endif +} diff --git a/PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/String+Extensions.swift b/PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/String+Extensions.swift index f60fd9567..38241d3c8 100644 --- a/PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/String+Extensions.swift +++ b/PrebidMobile/PrebidMobileRendering/ExtensionsAndWrappers/String+Extensions.swift @@ -44,4 +44,12 @@ extension String { func containsOnly(_ characterSet: CharacterSet) -> Bool { return self.trimmingCharacters(in: characterSet).count == 0 } + + /// Returns the last component of a file path, typically the file name. + /// + /// - Returns: The file name as a `String`, or an empty string if the path is empty. + func sourceFileName() -> String { + let pathComponents = components(separatedBy: "/") + return pathComponents.last ?? "" + } } diff --git a/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.m b/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.m index fc36b6cd9..2a33f5bb6 100644 --- a/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.m +++ b/PrebidMobile/PrebidMobileRendering/Utilities/PBMFunctions.m @@ -37,7 +37,7 @@ @implementation PBMFunctions + (nonnull NSString *)sdkVersion { - NSString *version = Constants.PREBID_VERSION; + NSString *version = PrebidConstants.PREBID_VERSION; return version ? version : @""; } diff --git a/PrebidMobile/Utils/URL+Extensions.swift b/PrebidMobile/Utils/URL+Extensions.swift index 1914c67a3..71b11b7ac 100644 --- a/PrebidMobile/Utils/URL+Extensions.swift +++ b/PrebidMobile/Utils/URL+Extensions.swift @@ -28,4 +28,10 @@ extension URL { return URL(string: str) } + + /// Returns a URL for a document stored in the temporary directory. + static func temporaryURL(for docName: String) -> URL? { + let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory()) + return temporaryDirectoryURL.appendingPathComponent(docName) + } } diff --git a/PrebidMobileTests/RenderingTests/Tests/LogTest.swift b/PrebidMobileTests/RenderingTests/Tests/LogTest.swift index cb55737b3..4dfaf549d 100644 --- a/PrebidMobileTests/RenderingTests/Tests/LogTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/LogTest.swift @@ -31,6 +31,8 @@ class LogTest: XCTestCase { override func tearDown() { logToFile = nil + Log.setCustomLogger(SDKConsoleLogger()) + super.tearDown() } @@ -85,7 +87,6 @@ class LogTest: XCTestCase { func testAllKinds() { // Test: default params - logToFile = .init() Log.error(message) @@ -107,6 +108,37 @@ class LogTest: XCTestCase { checkLogAndClean(level: .severe) } + func testAllKindsWithCustom() { + // Test: default params + logToFile = .init() + + Log.setCustomLogger(CustomTestLogger()) + + Log.error(message) + XCTAssertTrue(Log.getLogFileAsString()?.contains("TESTLOG") ?? false) + checkLogAndClean(level: .error) + + Log.info(message) + XCTAssertTrue(Log.getLogFileAsString()?.contains("TESTLOG") ?? false) + checkLogAndClean(level: .info) + + Log.debug(message) + XCTAssertTrue(Log.getLogFileAsString()?.contains("TESTLOG") ?? false) + checkLogAndClean(level: .debug) + + Log.verbose(message) + XCTAssertTrue(Log.getLogFileAsString()?.contains("TESTLOG") ?? false) + checkLogAndClean(level: .verbose) + + Log.warn(message) + XCTAssertTrue(Log.getLogFileAsString()?.contains("TESTLOG") ?? false) + checkLogAndClean(level: .warn) + + Log.severe(message) + XCTAssertTrue(Log.getLogFileAsString()?.contains("TESTLOG") ?? false) + checkLogAndClean(level: .severe) + } + func testWhereAmI() { logToFile = .init() @@ -116,6 +148,16 @@ class LogTest: XCTestCase { XCTAssertTrue(log.contains(LogLevel.info.stringValue)) } + func testWhereAmICustom() { + logToFile = .init() + Log.setCustomLogger(CustomTestLogger()) + + Log.whereAmI() + + let log = Log.getLogFileAsString() ?? "" + XCTAssertTrue(log.contains("WHEREAMI")) + } + func testLogLevel() { logToFile = .init() @@ -151,4 +193,40 @@ class LogTest: XCTestCase { logToFile = nil logToFile = .init() } + + class CustomTestLogger: PrebidLogger { + func error(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .error, filename: filename, line: line, function: function) + } + + func info(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .info, filename: filename, line: line, function: function) + } + + func debug(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .debug, filename: filename, line: line, function: function) + } + + func verbose(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .verbose, filename: filename, line: line, function: function) + } + + func warn(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .warn, filename: filename, line: line, function: function) + } + + func severe(_ object: Any, filename: String, line: Int, function: String) { + log(object, logLevel: .severe, filename: filename, line: line, function: function) + } + + func whereAmI(filename: String, line: Int, function: String) { + log("WHEREAMI", logLevel: .info, filename: filename, line: line, function: function) + } + + func log(_ object: Any, logLevel: PrebidMobile.LogLevel, filename: String, line: Int, function: String) { + let finalMessage = "\(logLevel.stringValue) \(object): TESTLOG" + print(finalMessage) + Log.serialWriteToLog(finalMessage) + } + } } From 50120081450a318e97186067304c1d01d22d6521 Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:42:51 +0200 Subject: [PATCH 20/23] Add getter for click URL (#1084) --- PrebidMobile/AdUnits/Native/NativeAd.swift | 4 ++++ PrebidMobileTests/NativeUnitTests/NativeAdTests.swift | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/PrebidMobile/AdUnits/Native/NativeAd.swift b/PrebidMobile/AdUnits/Native/NativeAd.swift index f407df97e..15618610f 100644 --- a/PrebidMobile/AdUnits/Native/NativeAd.swift +++ b/PrebidMobile/AdUnits/Native/NativeAd.swift @@ -112,6 +112,10 @@ public class NativeAd: NSObject, CacheExpiryDelegate { return dataObjects(of: .ctaText).first?.value } + /// Returns landing URL of the clickable link. + @objc public var clickURL: String? { + nativeAdMarkup?.link?.url + } /// Creates a `NativeAd` instance from the given cache ID. /// - Parameter cacheId: The cache ID to retrieve the bid response. /// - Returns: A `NativeAd` instance if successful, otherwise `nil`. diff --git a/PrebidMobileTests/NativeUnitTests/NativeAdTests.swift b/PrebidMobileTests/NativeUnitTests/NativeAdTests.swift index dc6c6061f..1d9460650 100644 --- a/PrebidMobileTests/NativeUnitTests/NativeAdTests.swift +++ b/PrebidMobileTests/NativeUnitTests/NativeAdTests.swift @@ -23,7 +23,6 @@ class NativeAdTests: XCTestCase { } func testNativeAd() { - let cacheId = CacheManager.shared.save(content: nativeAdString) let nativeAd = NativeAd.create(cacheId: cacheId!) @@ -36,6 +35,7 @@ class NativeAdTests: XCTestCase { XCTAssertEqual(nativeAd!.nativeAdMarkup!.link!.url, "https://www.testUrl.com/") XCTAssertEqual(nativeAd!.nativeAdMarkup!.link!.clicktrackers, ["https://testUrl.com/events/click/root/url"]) XCTAssertEqual(nativeAd!.nativeAdMarkup!.eventtrackers!.first!.url, "https://testUrl.com/events/tracker/impression") + XCTAssertEqual(nativeAd!.clickURL, "https://www.testUrl.com/") } func testArrayGetters() { From a0ac4f35c5fa09df4979e6882525e76718ed378a Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Tue, 21 Jan 2025 12:11:20 +0200 Subject: [PATCH 21/23] Reset eventDelegate for tests (#1089) --- PrebidMobileTests/PrebidEventDelegateTests.swift | 7 +++++++ .../Prebid/PBMSDKConfiguration+TestExtension.swift | 2 ++ .../Tests/PrebidServerStatusRequesterTests.swift | 6 ++++++ .../Tests/VASTTests/RewardedVideoEventsTest.swift | 8 +++++--- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/PrebidMobileTests/PrebidEventDelegateTests.swift b/PrebidMobileTests/PrebidEventDelegateTests.swift index ebcac4ac7..00133391b 100644 --- a/PrebidMobileTests/PrebidEventDelegateTests.swift +++ b/PrebidMobileTests/PrebidEventDelegateTests.swift @@ -25,6 +25,13 @@ class PrebidEventDelegateTests: XCTestCase { var delegate: PrebidEventDelegate? + override func tearDown() { + super.tearDown() + + Prebid.reset() + delegate = nil + } + func test_eventDelegate_isCalled() { let exp = expectation(description: "Expect PrebidEventDelegate to be called") diff --git a/PrebidMobileTests/RenderingTests/TestExtensions/Prebid/PBMSDKConfiguration+TestExtension.swift b/PrebidMobileTests/RenderingTests/TestExtensions/Prebid/PBMSDKConfiguration+TestExtension.swift index 441a97da9..0a5e7d24a 100644 --- a/PrebidMobileTests/RenderingTests/TestExtensions/Prebid/PBMSDKConfiguration+TestExtension.swift +++ b/PrebidMobileTests/RenderingTests/TestExtensions/Prebid/PBMSDKConfiguration+TestExtension.swift @@ -45,6 +45,8 @@ extension Prebid { Prebid.shared.creativeFactoryTimeout = 6.0 Prebid.shared.creativeFactoryTimeoutPreRenderContent = 30.0 + + Prebid.shared.eventDelegate = nil } static var forcedIsViewable: Bool { diff --git a/PrebidMobileTests/RenderingTests/Tests/PrebidServerStatusRequesterTests.swift b/PrebidMobileTests/RenderingTests/Tests/PrebidServerStatusRequesterTests.swift index 111191e46..d65daaab2 100644 --- a/PrebidMobileTests/RenderingTests/Tests/PrebidServerStatusRequesterTests.swift +++ b/PrebidMobileTests/RenderingTests/Tests/PrebidServerStatusRequesterTests.swift @@ -18,6 +18,12 @@ import XCTest class PrebidServerStatusRequesterTests: XCTestCase { + override func setUp() { + super.setUp() + + Prebid.reset() + } + override func tearDown() { super.tearDown() diff --git a/PrebidMobileTests/RenderingTests/Tests/VASTTests/RewardedVideoEventsTest.swift b/PrebidMobileTests/RenderingTests/Tests/VASTTests/RewardedVideoEventsTest.swift index 7312747fc..75ca71c63 100644 --- a/PrebidMobileTests/RenderingTests/Tests/VASTTests/RewardedVideoEventsTest.swift +++ b/PrebidMobileTests/RenderingTests/Tests/VASTTests/RewardedVideoEventsTest.swift @@ -41,15 +41,17 @@ class RewardedVideoEventsTest : XCTestCase, PBMCreativeViewDelegate { var pbmVideoCreative:PBMVideoCreative! override func setUp() { + super.setUp() + MockServer.shared.reset() + Prebid.reset() } override func tearDown() { - MockServer.shared.reset() + super.tearDown() + MockServer.shared.reset() Prebid.reset() - - super.tearDown() } func testEvents() { From 02f47c0f005bf27e902beae03d5c48f69656df0e Mon Sep 17 00:00:00 2001 From: Olena Stepaniuk <92721590+OlenaPostindustria@users.noreply.github.com> Date: Mon, 27 Jan 2025 09:56:25 +0200 Subject: [PATCH 22/23] Introduce test cases for PUC (#1077) * feat: add GAM test case * feat: add web view test case * feat: clean up * feat: tune GAM case * feat: clean up Info.plist --- .../InternalTestApp.xcodeproj/project.pbxproj | 12 ++ .../AppDelegate.swift | 2 + .../Model/TestCasesManager.swift | 24 +++ .../Resources/Base.lproj/Main.storyboard | 66 ++++++++- ...niversalCreativeTestingGAMController.swift | 137 ++++++++++++++++++ ...rsalCreativeTestingWebViewController.swift | 56 +++++++ 6 files changed, 295 insertions(+), 2 deletions(-) create mode 100644 InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OriginalAPI/PrebidUniversalCreativeTestingGAMController.swift create mode 100644 InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OriginalAPI/PrebidUniversalCreativeTestingWebViewController.swift diff --git a/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj b/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj index bae98f082..aa9b01b76 100644 --- a/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj +++ b/InternalTestApp/InternalTestApp.xcodeproj/project.pbxproj @@ -39,6 +39,10 @@ 53514CEC2D08416B00A480C0 /* SampleInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CDA2D08416B00A480C0 /* SampleInterstitialController.swift */; }; 53514CED2D08416B00A480C0 /* SampleAdView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CD82D08416B00A480C0 /* SampleAdView.swift */; }; 53514CEE2D08416B00A480C0 /* CustomRendererBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53514CDE2D08416B00A480C0 /* CustomRendererBannerController.swift */; }; + 535ADDFB2D19731000DB888F /* PrebidUniversalCreativeTestingGAMController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535ADDFA2D19731000DB888F /* PrebidUniversalCreativeTestingGAMController.swift */; }; + 535ADDFC2D19731000DB888F /* PrebidUniversalCreativeTestingGAMController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535ADDFA2D19731000DB888F /* PrebidUniversalCreativeTestingGAMController.swift */; }; + 535ADDFE2D19A62C00DB888F /* PrebidUniversalCreativeTestingWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535ADDFD2D19A62C00DB888F /* PrebidUniversalCreativeTestingWebViewController.swift */; }; + 535ADDFF2D19A62C00DB888F /* PrebidUniversalCreativeTestingWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535ADDFD2D19A62C00DB888F /* PrebidUniversalCreativeTestingWebViewController.swift */; }; 5397BD142936185400ABDA22 /* PrebidOriginalAPIDisplayBannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5397BD132936185400ABDA22 /* PrebidOriginalAPIDisplayBannerController.swift */; }; 5397BD26293760F500ABDA22 /* PrebidOriginalAPIDisplayInterstitialController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5397BD25293760F500ABDA22 /* PrebidOriginalAPIDisplayInterstitialController.swift */; }; 5397BD2A29376C9700ABDA22 /* PrebidOriginalAPIVideoRewardedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5397BD2929376C9700ABDA22 /* PrebidOriginalAPIVideoRewardedController.swift */; }; @@ -393,6 +397,8 @@ 53514CDC2D08416B00A480C0 /* SampleModalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleModalViewController.swift; sourceTree = ""; }; 53514CDE2D08416B00A480C0 /* CustomRendererBannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRendererBannerController.swift; sourceTree = ""; }; 53514CDF2D08416B00A480C0 /* CustomRendererInterstitialController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRendererInterstitialController.swift; sourceTree = ""; }; + 535ADDFA2D19731000DB888F /* PrebidUniversalCreativeTestingGAMController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidUniversalCreativeTestingGAMController.swift; sourceTree = ""; }; + 535ADDFD2D19A62C00DB888F /* PrebidUniversalCreativeTestingWebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidUniversalCreativeTestingWebViewController.swift; sourceTree = ""; }; 5397BD132936185400ABDA22 /* PrebidOriginalAPIDisplayBannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidOriginalAPIDisplayBannerController.swift; sourceTree = ""; }; 5397BD25293760F500ABDA22 /* PrebidOriginalAPIDisplayInterstitialController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidOriginalAPIDisplayInterstitialController.swift; sourceTree = ""; }; 5397BD2929376C9700ABDA22 /* PrebidOriginalAPIVideoRewardedController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrebidOriginalAPIVideoRewardedController.swift; sourceTree = ""; }; @@ -746,6 +752,8 @@ 5397BD312937895700ABDA22 /* PrebidOriginalAPIVideoInstreamViewController.swift */, 539F961329DEF2670061E7A5 /* PrebidOriginalAPIVideoInterstitialController.swift */, 5397BD2929376C9700ABDA22 /* PrebidOriginalAPIVideoRewardedController.swift */, + 535ADDFA2D19731000DB888F /* PrebidUniversalCreativeTestingGAMController.swift */, + 535ADDFD2D19A62C00DB888F /* PrebidUniversalCreativeTestingWebViewController.swift */, ); path = OriginalAPI; sourceTree = ""; @@ -1610,6 +1618,7 @@ 53ED2FF629798104007D13EE /* BaseConfigurationController.swift in Sources */, 53ED2FF729798104007D13EE /* NativeAsset+Extensions.swift in Sources */, 53ED2FF829798104007D13EE /* PrebidGAMNativeAdController.swift in Sources */, + 535ADDFE2D19A62C00DB888F /* PrebidUniversalCreativeTestingWebViewController.swift in Sources */, 53ED2FF929798104007D13EE /* PrebidOriginalAPIVideoInstreamViewController.swift in Sources */, 53ED2FFA29798104007D13EE /* PrebidAdMobBannerViewController.swift in Sources */, 53ED2FFB29798104007D13EE /* ConfigurableViewController.swift in Sources */, @@ -1639,6 +1648,7 @@ 53514CE52D08416B00A480C0 /* SampleInterstitialController.swift in Sources */, 53514CE62D08416B00A480C0 /* SampleAdView.swift in Sources */, 53514CE72D08416B00A480C0 /* CustomRendererBannerController.swift in Sources */, + 535ADDFC2D19731000DB888F /* PrebidUniversalCreativeTestingGAMController.swift in Sources */, 53ED300E29798104007D13EE /* NativeAdViewBoxProtocol.swift in Sources */, 53ED300F29798104007D13EE /* FormViewController+RowBuildHelpers.swift in Sources */, 53ED301029798104007D13EE /* PrebidGAMBannerController.swift in Sources */, @@ -1710,6 +1720,7 @@ 5B209B4A21075E0A00C0F5CE /* BaseConfigurationController.swift in Sources */, 92C4E60B27A2F5D900738370 /* NativeAsset+Extensions.swift in Sources */, 92102C6127B0F2FE003F12B2 /* PrebidGAMNativeAdController.swift in Sources */, + 535ADDFF2D19A62C00DB888F /* PrebidUniversalCreativeTestingWebViewController.swift in Sources */, 5397BD322937895700ABDA22 /* PrebidOriginalAPIVideoInstreamViewController.swift in Sources */, 92E92502276B299F002B57F3 /* PrebidAdMobBannerViewController.swift in Sources */, 5BCCA3D62136D2E800DA3B5A /* ConfigurableViewController.swift in Sources */, @@ -1739,6 +1750,7 @@ 53514CEC2D08416B00A480C0 /* SampleInterstitialController.swift in Sources */, 53514CED2D08416B00A480C0 /* SampleAdView.swift in Sources */, 53514CEE2D08416B00A480C0 /* CustomRendererBannerController.swift in Sources */, + 535ADDFB2D19731000DB888F /* PrebidUniversalCreativeTestingGAMController.swift in Sources */, 92F9A5D127A15BD1007B0B17 /* NativeAdViewBoxProtocol.swift in Sources */, 341CC3142562C9D000186F29 /* FormViewController+RowBuildHelpers.swift in Sources */, 3493021C2473F408004A6086 /* PrebidGAMBannerController.swift in Sources */, diff --git a/InternalTestApp/PrebidMobileDemoRendering/AppDelegate.swift b/InternalTestApp/PrebidMobileDemoRendering/AppDelegate.swift index c4c076b6a..1b46f832e 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/AppDelegate.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/AppDelegate.swift @@ -183,4 +183,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } + + } diff --git a/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift b/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift index 781ad0c1a..b360132c6 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift +++ b/InternalTestApp/PrebidMobileDemoRendering/Model/TestCasesManager.swift @@ -467,6 +467,30 @@ struct TestCaseManager { setupCustomParams(for: interstitialController.prebidConfigId) }), + + // MARK: ---- Prebid Universal Creative Tests + + TestCase(title: "Prebid Universal Creative (GAM)", + tags: [.banner, .originalAPI, .server], + exampleVCStoryboardID: "AdapterViewController", + configurationClosure: { vc in + guard let adapterVC = vc as? AdapterViewController else { + return + } + + let bannerController = PrebidUniversalCreativeTestingGAMController(rootController: adapterVC) + bannerController.adSize = CGSize(width: 300, height: 250) + bannerController.gamAdUnitID = "/21808260008/puc-testing-secure-banner-300x250" + + adapterVC.setup(adapter: bannerController) + setupCustomParams(for: bannerController.prebidConfigId) + }), + + TestCase(title: "Prebid Universal Creative (WebView)", + tags: [.banner, .originalAPI, .server], + exampleVCStoryboardID: "PrebidUniversalCreativeTestingWebViewController", + configurationClosure: { vc in }), + // MARK: ---- Native (Original API) TestCase(title: "Native Banner (GAM Original) [OK, PUC]", diff --git a/InternalTestApp/PrebidMobileDemoRendering/Resources/Base.lproj/Main.storyboard b/InternalTestApp/PrebidMobileDemoRendering/Resources/Base.lproj/Main.storyboard index 571f4d114..36d32fa6b 100644 --- a/InternalTestApp/PrebidMobileDemoRendering/Resources/Base.lproj/Main.storyboard +++ b/InternalTestApp/PrebidMobileDemoRendering/Resources/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -376,6 +376,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OriginalAPI/PrebidUniversalCreativeTestingGAMController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OriginalAPI/PrebidUniversalCreativeTestingGAMController.swift new file mode 100644 index 000000000..e4b47246f --- /dev/null +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OriginalAPI/PrebidUniversalCreativeTestingGAMController.swift @@ -0,0 +1,137 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import GoogleMobileAds + +final class PrebidUniversalCreativeTestingGAMController: + NSObject, + AdaptedController, + PrebidConfigurableBannerController { + + var refreshInterval: TimeInterval = 0 + var prebidConfigId: String = "" + var gamAdUnitID: String = "" + var adSize = CGSize.zero + + let rootController: AdapterViewController + + private var gamBanner: GAMBannerView! + + private let bannerViewDidReceiveAd = EventReportContainer() + private let bannerViewDidFailToReceiveAd = EventReportContainer() + + private let configIdLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 12) + return label + }() + + private let reloadButton = ThreadCheckingButton() + + init(rootController: AdapterViewController) { + self.rootController = rootController + super.init() + + setupAdapterController() + } + + func configurationController() -> BaseConfigurationController? { + return PrebidBannerConfigurationController(controller: self) + } + + func loadAd() { + configIdLabel.isHidden = false + configIdLabel.text = "AdUnit ID: \(gamAdUnitID)" + + gamBanner = GAMBannerView(adSize: GADAdSizeFromCGSize(adSize)) + gamBanner.adUnitID = gamAdUnitID + gamBanner.rootViewController = rootController + gamBanner.delegate = self + + rootController.bannerView?.addSubview(gamBanner) + + let gamRequest = GAMRequest() + gamBanner.load(gamRequest) + } + + @objc private func reload() { + reloadButton.isEnabled = false + let gamRequest = GAMRequest() + gamBanner.load(gamRequest) + } + + private func setupAdapterController() { + rootController.showButton.isHidden = true + + configIdLabel.isHidden = true + rootController.actionsView.addArrangedSubview(configIdLabel) + + reloadButton.addTarget(self, action: #selector(reload), for: .touchUpInside) + + setupActions() + } + + private func setupActions() { + rootController.setupAction(bannerViewDidReceiveAd, "bannerViewDidReceiveAd called", accessibilityLabel: "bannerViewDidReceiveAd called") + rootController.setupAction(bannerViewDidFailToReceiveAd, "bannerViewDidFailToReceiveAd called") + rootController.setupAction(reloadButton, "[Reload]") + } + + private func resetEvents() { + bannerViewDidReceiveAd.isEnabled = false + bannerViewDidFailToReceiveAd.isEnabled = false + reloadButton.isEnabled = true + } +} + +// MARK: - GADBannerViewDelegate + +extension PrebidUniversalCreativeTestingGAMController: GADBannerViewDelegate { + + func bannerViewDidReceiveAd(_ bannerView: GADBannerView) { + rootController.bannerView.backgroundColor = .clear + bannerViewDidReceiveAd.isEnabled = true + reloadButton.isEnabled = true + rootController.bannerView.constraints.first { $0.firstAttribute == .width }?.constant = 300 + rootController.bannerView.constraints.first { $0.firstAttribute == .height }?.constant = 250 + + let targetWebView = bannerView.allSubViewsOf(type: WKWebView.self).first + targetWebView?.navigationDelegate = self + } + + func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: any Error) { + resetEvents() + bannerViewDidFailToReceiveAd.isEnabled = true + print(error.localizedDescription) + } +} + +// MARK: - WKNavigationDelegate + +extension PrebidUniversalCreativeTestingGAMController: WKNavigationDelegate { + + func webView( + _ webView: WKWebView, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: @MainActor @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void + ) { + if let serverTrust = challenge.protectionSpace.serverTrust { + let credential = URLCredential(trust: serverTrust) + completionHandler(.useCredential, credential) + } else { + print("Error: failed to get server trust.") + } + } +} diff --git a/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OriginalAPI/PrebidUniversalCreativeTestingWebViewController.swift b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OriginalAPI/PrebidUniversalCreativeTestingWebViewController.swift new file mode 100644 index 000000000..6d32108db --- /dev/null +++ b/InternalTestApp/PrebidMobileDemoRendering/ViewControllers/Adapters/Prebid/OriginalAPI/PrebidUniversalCreativeTestingWebViewController.swift @@ -0,0 +1,56 @@ +/*   Copyright 2018-2024 Prebid.org, Inc. + +  Licensed under the Apache License, Version 2.0 (the "License"); +  you may not use this file except in compliance with the License. +  You may obtain a copy of the License at + +  http://www.apache.org/licenses/LICENSE-2.0 + +  Unless required by applicable law or agreed to in writing, software +  distributed under the License is distributed on an "AS IS" BASIS, +  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +  See the License for the specific language governing permissions and +  limitations under the License. +  */ + +import UIKit +import WebKit + +final class PrebidUniversalCreativeTestingWebViewController: UIViewController { + + @IBOutlet weak var webView: WKWebView! + @IBOutlet weak var textField: UITextField! + + override func viewDidLoad() { + super.viewDidLoad() + + webView.navigationDelegate = self + } + + @IBAction func onOpenURLPressed(_ sender: Any) { + guard let urlString = textField.text, let url = URL(string: "\(urlString)") else { + return + } + + let request = URLRequest(url: url) + webView.load(request) + } +} + +// MARK: - WKNavigationDelegate + +extension PrebidUniversalCreativeTestingWebViewController: WKNavigationDelegate { + + func webView( + _ webView: WKWebView, + didReceive challenge: URLAuthenticationChallenge, + completionHandler: @MainActor @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void + ) { + if let serverTrust = challenge.protectionSpace.serverTrust { + let credential = URLCredential(trust: serverTrust) + completionHandler(.useCredential, credential) + } else { + print("Error: failed to get server trust.") + } + } +} From 9e6e6108ee8b79e1fb6cb46536108e6ad67d66a7 Mon Sep 17 00:00:00 2001 From: Ankit Rajendra Thanekar Date: Tue, 28 Jan 2025 07:28:04 +0000 Subject: [PATCH 23/23] Update TargetingInfo Behavior (#1088) * Add Targeting option * Remove FF usage * PR Comments + Tests * PR Comments * Update tests * Remove cacheId value * Reset values for test * Fix test for checking correct testcase * In case of winning bid, targeting should have local-cache-id * Refactor teardown * PR Update * Add Native format case * Check cacheid with adObject --------- Co-authored-by: Ankit Rajendra Thanekar --- PrebidMobile/AdUnits/AdUnit.swift | 21 ++- .../ConfigurationAndTargeting/Targeting.swift | 3 + .../AdUnitTests/AdUnitTests.swift | 147 ++++++++++++++++++ ...BidResponseTransformer+TestExtension.swift | 10 ++ PrebidMobileTests/TargetingTests.swift | 12 ++ 5 files changed, 191 insertions(+), 2 deletions(-) diff --git a/PrebidMobile/AdUnits/AdUnit.swift b/PrebidMobile/AdUnits/AdUnit.swift index 475edbddc..76e84c202 100644 --- a/PrebidMobile/AdUnits/AdUnit.swift +++ b/PrebidMobile/AdUnits/AdUnit.swift @@ -186,11 +186,28 @@ public class AdUnit: NSObject, DispatcherDelegate { }) } - private func setUp(_ adObject: AnyObject?, with bidResponse: BidResponse) -> ResultCode { + func setUp(_ adObject: AnyObject?, with bidResponse: BidResponse) -> ResultCode { + + if Targeting.shared.forceSdkToChooseWinner { + Log.error("Breaking change: set Targeting.forceSdkToChooseWinner = false and test your behavior. In the upcoming major release, the SDK will send all targeting keywords to the AdSever, so you should prepare your setup.") + } + guard let winningBid = bidResponse.winningBid else { + + //When the new behavior is active + if !Targeting.shared.forceSdkToChooseWinner { + + if let adObject { + Utils.shared.validateAndAttachKeywords(adObject: adObject, bidResponse: bidResponse) + } + //If there are no winning bids, but there are bids the SDK will send back prebidDemandFetchSuccess + if let bids = bidResponse.allBids, !bids.isEmpty { + return .prebidDemandFetchSuccess + } + } return .prebidDemandNoBids } - + if let cacheId = cacheBidIfNeeded(winningBid) { bidResponse.addTargetingInfoValue(key: PrebidLocalCacheIdKey, value: cacheId) } diff --git a/PrebidMobile/ConfigurationAndTargeting/Targeting.swift b/PrebidMobile/ConfigurationAndTargeting/Targeting.swift index c0661e0d4..103c834ae 100644 --- a/PrebidMobile/ConfigurationAndTargeting/Targeting.swift +++ b/PrebidMobile/ConfigurationAndTargeting/Targeting.swift @@ -189,6 +189,9 @@ public class Targeting: NSObject { UserConsentDataManager.shared.isAllowedAccessDeviceData() } + /// This value forces SDK to choose targeting info of the winning bid + public var forceSdkToChooseWinner : Bool = true + // MARK: - External User Ids /// Array of external user IDs. diff --git a/PrebidMobileTests/AdUnitTests/AdUnitTests.swift b/PrebidMobileTests/AdUnitTests/AdUnitTests.swift index 3c8027185..0b3dc2b2b 100644 --- a/PrebidMobileTests/AdUnitTests/AdUnitTests.swift +++ b/PrebidMobileTests/AdUnitTests/AdUnitTests.swift @@ -20,7 +20,10 @@ class AdUnitTests: XCTestCase { override func tearDown() { Targeting.shared.clearUserKeywords() + Targeting.shared.forceSdkToChooseWinner = true + Prebid.shared.useExternalClickthroughBrowser = false + Prebid.shared.useCacheForReportingWithRenderingAPI = false } func testFetchDemand() { @@ -114,6 +117,150 @@ class AdUnitTests: XCTestCase { XCTAssertEqual(realBidInfo?.nativeAdCacheId, expectedCacheId) } + //forceSdkToChooseWinner + Winner = Contains Targeting Info + func testForcedWinnerAndWinningBid() { + //given + Targeting.shared.forceSdkToChooseWinner = true + + let expected = ResultCode.prebidDemandFetchSuccess + + let adUnit = AdUnit(configId: "138c4d03-0efb-4498-9dc6-cb5a9acb2ea4", size: CGSize(width: 300, height: 250), adFormats: [.banner]) + //This needs to after AdUnit init as the AdUnit enables this value. + //We need to disabled to not look for cache id for winning bid + Prebid.shared.useCacheForReportingWithRenderingAPI = false + let adObject = NSMutableDictionary() + let rawWinningBid = PBMBidResponseTransformer.makeValidResponse(bidPrice: 0.75) + let jsonDict = rawWinningBid.jsonDict as? NSDictionary + let bidResponse = BidResponse(jsonDictionary: jsonDict ?? [:]) + + //when + let resultCode = adUnit.setUp(adObject, with: bidResponse) + + //then + XCTAssertTrue((adObject.allKeys as? [String])?.contains("hb_bidder") ?? false) + XCTAssertEqual(resultCode, expected) + } + + //forceSdkToChooseWinner + No Winner = Doesn't contain Targeting Info + func testForcedWinnerAndLoosingBid() { + //given + Targeting.shared.forceSdkToChooseWinner = true + let expected = ResultCode.prebidDemandNoBids + let adUnit = AdUnit(configId: "138c4d03-0efb-4498-9dc6-cb5a9acb2ea4", size: CGSize(width: 300, height: 250), adFormats: [.banner]) + //This needs to after AdUnit init as the AdUnit enables this value. + //We need to disabled to not look for cache id for winning bid + Prebid.shared.useCacheForReportingWithRenderingAPI = false + let adObject = NSMutableDictionary() + let rawWinningBid = PBMBidResponseTransformer.makeValidResponseWithNonWinningTargetingInfo() + let jsonDict = rawWinningBid.jsonDict as? NSDictionary + let bidResponse = BidResponse(jsonDictionary: jsonDict ?? [:]) + + //when + let resultCode = adUnit.setUp(adObject, with: bidResponse) + + //then + XCTAssertFalse((adObject.allKeys as? [String])?.contains("hb_bidder") ?? false) + XCTAssertEqual(resultCode, expected) + } + + //Don't forceSdkToChooseWinner + Winner = Contains Targeting Info + func testNonForcedWinnerAndWinningBid() { + //given + Targeting.shared.forceSdkToChooseWinner = false + let expected = ResultCode.prebidDemandFetchSuccess + let adUnit = AdUnit(configId: "138c4d03-0efb-4498-9dc6-cb5a9acb2ea4", size: CGSize(width: 300, height: 250), adFormats: [.banner]) + //This needs to after AdUnit init as the AdUnit enables this value. + //We need to disabled to not look for cache id for winning bid + Prebid.shared.useCacheForReportingWithRenderingAPI = false + let adObject = NSMutableDictionary() + let rawWinningBid = PBMBidResponseTransformer.makeValidResponse(bidPrice: 0.75) + let jsonDict = rawWinningBid.jsonDict as? NSDictionary + let bidResponse = BidResponse(jsonDictionary: jsonDict ?? [:]) + + //when + let resultCode = adUnit.setUp(adObject, with: bidResponse) + + //then + XCTAssertTrue((adObject.allKeys as? [String])?.contains("hb_bidder") ?? false) + XCTAssertEqual(resultCode, expected) + } + + //Don't forceSdkToChooseWinner + No Winner = Contains Targeting Info + func testNonForcedWinnerAndNonWinningBid() { + //given + Targeting.shared.forceSdkToChooseWinner = false + + let expected = ResultCode.prebidDemandFetchSuccess + let adUnit = AdUnit(configId: "138c4d03-0efb-4498-9dc6-cb5a9acb2ea4", size: CGSize(width: 300, height: 250), adFormats: [.banner]) + //This needs to after AdUnit init as the AdUnit enables this value. + //We need to disabled to not look for cache id for winning bid + Prebid.shared.useCacheForReportingWithRenderingAPI = false + let adObject = NSMutableDictionary() + let rawWinningBid = PBMBidResponseTransformer.makeValidResponseWithNonWinningTargetingInfo() + let jsonDict = rawWinningBid.jsonDict as? NSDictionary + let bidResponse = BidResponse(jsonDictionary: jsonDict ?? [:]) + + //when + let resultCode = adUnit.setUp(adObject, with: bidResponse) + + //then + XCTAssertTrue((adObject.allKeys as? [String])?.contains("hb_bidder") ?? false) + XCTAssertEqual(adObject["hb_bidder"] as? String, "Test-Bidder-1") + XCTAssertEqual(resultCode, expected) + } + + //forceSdkToChooseWinner + Native Format Winner = Contains Targeting Info + func testForcedWinnerAndWinningBidNativeFormat() { + //given + Targeting.shared.forceSdkToChooseWinner = true + + let expected = ResultCode.prebidDemandFetchSuccess + + let adUnit = AdUnit(configId: "138c4d03-0efb-4498-9dc6-cb5a9acb2ea4", size: CGSize(width: 300, height: 250), adFormats: [.native]) + //This needs to after AdUnit init as the AdUnit enables this value. + //We need to disabled to not look for cache id for winning bid + Prebid.shared.useCacheForReportingWithRenderingAPI = false + let adObject = NSMutableDictionary() + let rawWinningBid = PBMBidResponseTransformer.makeNativeValidResponse(bidPrice: 0.75) + let jsonDict = rawWinningBid.jsonDict as? NSDictionary + let bidResponse = BidResponse(jsonDictionary: jsonDict ?? [:]) + + //when + let resultCode = adUnit.setUp(adObject, with: bidResponse) + + //then + XCTAssertNotNil(bidResponse.targetingInfo?[PrebidLocalCacheIdKey]) + XCTAssertTrue((adObject.allKeys as? [String])?.contains(PrebidLocalCacheIdKey) ?? false) + XCTAssertTrue((adObject.allKeys as? [String])?.contains("hb_bidder") ?? false) + XCTAssertEqual(resultCode, expected) + } + + //Don't forceSdkToChooseWinner + Native Format Winner = Contains Targeting Info + func testNonForcedWinnerAndWinningBidNativeFormat() { + //given + Targeting.shared.forceSdkToChooseWinner = false + + let expected = ResultCode.prebidDemandFetchSuccess + + let adUnit = AdUnit(configId: "138c4d03-0efb-4498-9dc6-cb5a9acb2ea4", size: CGSize(width: 300, height: 250), adFormats: [.native]) + //This needs to after AdUnit init as the AdUnit enables this value. + //We need to disabled to not look for cache id for winning bid + Prebid.shared.useCacheForReportingWithRenderingAPI = false + let adObject = NSMutableDictionary() + let rawWinningBid = PBMBidResponseTransformer.makeNativeValidResponse(bidPrice: 0.75) + let jsonDict = rawWinningBid.jsonDict as? NSDictionary + let bidResponse = BidResponse(jsonDictionary: jsonDict ?? [:]) + + //when + let resultCode = adUnit.setUp(adObject, with: bidResponse) + + //then + XCTAssertNotNil(bidResponse.targetingInfo?[PrebidLocalCacheIdKey]) + XCTAssertTrue((adObject.allKeys as? [String])?.contains(PrebidLocalCacheIdKey) ?? false) + XCTAssertTrue((adObject.allKeys as? [String])?.contains("hb_bidder") ?? false) + XCTAssertEqual(resultCode, expected) + } + func testBidInfoCompletion() { Prebid.shared.prebidServerAccountId = "test-account-id" diff --git a/PrebidMobileTests/RenderingTests/TestExtensions/Prebid/PBMBidResponseTransformer+TestExtension.swift b/PrebidMobileTests/RenderingTests/TestExtensions/Prebid/PBMBidResponseTransformer+TestExtension.swift index 858b77857..693958142 100644 --- a/PrebidMobileTests/RenderingTests/TestExtensions/Prebid/PBMBidResponseTransformer+TestExtension.swift +++ b/PrebidMobileTests/RenderingTests/TestExtensions/Prebid/PBMBidResponseTransformer+TestExtension.swift @@ -50,6 +50,16 @@ extension PBMBidResponseTransformer { return buildResponse("{\"id\":\"B4A2D3F4-41B6-4D37-B68B-EE8893E85C31\",\"seatbid\":[\(winningBidFragment)],\"cur\":\"USD\",\"ext\":{\"responsetimemillis\":{\"openx\":62}}}") } + static func makeNativeValidResponse(bidPrice: Float, noWinningBidProperties: Bool = false) -> PrebidServerResponse { + let winningBidFragment = noWinningBidProperties ? "" : "{\"bid\":[{\"id\":\"test-bid-id-1\",\"impid\":\"8BBB0D42-5A73-45AC-B275-51B299A74C32\",\"price\":\(bidPrice),\"adm\":\"
You Won! This is a test bid
\",\"adid\":\"test-ad-id-12345\",\"adomain\":[\"openx.com\"],\"crid\":\"test-creative-id-1\",\"w\":300,\"h\":250,\"ext\":{\"prebid\":{\"targeting\":{\"hb_bidder\":\"openx\",\"hb_bidder_openx\":\"openx\",\"hb_env\":\"mobile-app\",\"hb_env_openx\":\"mobile-app\",\"hb_pb\":\"0.10\",\"hb_pb_openx\":\"0.10\",\"hb_size\":\"300x250\",\"hb_size_openx\":\"300x250\"},\"type\":\"native\"},\"bidder\":{\"ad_ox_cats\":[2],\"agency_id\":\"agency_10\",\"brand_id\":\"brand_10\",\"buyer_id\":\"buyer_10\",\"matching_ad_id\":{\"campaign_id\":1,\"creative_id\":3,\"placement_id\":2},\"next_highest_bid_price\":0.099}}}],\"seat\":\"openx\"}" + return buildResponse("{\"id\":\"B4A2D3F4-41B6-4D37-B68B-EE8893E85C31\",\"seatbid\":[\(winningBidFragment)],\"cur\":\"USD\",\"ext\":{\"responsetimemillis\":{\"openx\":62}}}") + } + + static func makeValidResponseWithNonWinningTargetingInfo() -> PrebidServerResponse { + let winningBidFragment = "{\n \"bid\": [\n {\n \"id\": \"test_id_bid\",\n \"impid\": \"test_imp\",\n \"price\": 0,\n \"ext\": {\n \"origbidcpm\": 0,\n \"prebid\": {\n \"meta\": {},\n \"targeting\": {\n \"hb_bidder\": \"Test-Bidder-1\",\n \"hb_source\": \"s2s\",\n }\n }\n }\n }\n ],\n \"seat\": \"Test-Bidder-1\"\n}" + return buildResponse("{\"id\":\"B4A2D3F4-41B6-4D37-B68B-EE8893E85C31\",\"seatbid\":[\(winningBidFragment)],\"cur\":\"USD\",\"ext\":{\"responsetimemillis\":{\"openx\":62}}}") + } + static func makeValidResponseWithCTF(bidPrice: Float, ctfBanner: Double, ctfPreRender: Double) -> PrebidServerResponse { let winningBidFragment = "{\"bid\":[{\"id\":\"test-bid-id-1\",\"impid\":\"8BBB0D42-5A73-45AC-B275-51B299A74C32\",\"price\":\(bidPrice),\"adm\":\"
You Won! This is a test bid
\",\"adid\":\"test-ad-id-12345\",\"adomain\":[\"openx.com\"],\"crid\":\"test-creative-id-1\",\"w\":300,\"h\":250,\"ext\":{\"prebid\":{\"targeting\":{\"hb_bidder\":\"openx\",\"hb_bidder_openx\":\"openx\",\"hb_env\":\"mobile-app\",\"hb_env_openx\":\"mobile-app\",\"hb_pb\":\"0.10\",\"hb_pb_openx\":\"0.10\",\"hb_size\":\"300x250\",\"hb_size_openx\":\"300x250\"},\"type\":\"banner\"},\"bidder\":{\"ad_ox_cats\":[2],\"agency_id\":\"agency_10\",\"brand_id\":\"brand_10\",\"buyer_id\":\"buyer_10\",\"matching_ad_id\":{\"campaign_id\":1,\"creative_id\":3,\"placement_id\":2},\"next_highest_bid_price\":0.099}}}],\"seat\":\"openx\"}" return buildResponse("{\"id\":\"B4A2D3F4-41B6-4D37-B68B-EE8893E85C31\",\"seatbid\":[\(winningBidFragment)],\"cur\":\"USD\",\"ext\":{\"responsetimemillis\":{\"openx\":62},\"prebid\":{\"passthrough\":[{\"type\":\"prebidmobilesdk\", \"sdkconfiguration\":{\"cftbanner\":\(ctfBanner),\"cftprerender\":\(ctfPreRender)}}]}}}") diff --git a/PrebidMobileTests/TargetingTests.swift b/PrebidMobileTests/TargetingTests.swift index 01c1b6d57..0adcfb2d6 100644 --- a/PrebidMobileTests/TargetingTests.swift +++ b/PrebidMobileTests/TargetingTests.swift @@ -47,6 +47,7 @@ class TargetingTests: XCTestCase { override func tearDown() { UtilitiesForTesting.resetTargeting(.shared) + Targeting.shared.forceSdkToChooseWinner = true } func testDomain() { @@ -122,6 +123,17 @@ class TargetingTests: XCTestCase { XCTAssertEqual(locationPrecision, Targeting.shared.locationPrecision) } + func testforceSdkToChooseWinner() { + //given + let forceSdkToChooseWinner = false + + //when + Targeting.shared.forceSdkToChooseWinner = forceSdkToChooseWinner + + //then + XCTAssertEqual(forceSdkToChooseWinner, Targeting.shared.forceSdkToChooseWinner) + } + // MARK: - Year Of Birth func testYearOfBirth() { //given