From b3a41df856a5def7343354a312a5627f09128a7a Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Mon, 11 Sep 2023 14:10:32 -0700 Subject: [PATCH 01/23] Lay foundation --- Source/BidMachineAdapter.swift | 125 ++++++++++++++++ Source/BidMachineAdapterAd.swift | 37 +++++ Source/BidMachineAdapterBannerAd.swift | 149 +++++++++++++++++++ Source/BidMachineAdapterConfig.swift | 78 ++++++++++ Source/BidMachineAdapterInterstitialAd.swift | 132 ++++++++++++++++ Source/BidMachineAdapterRewardedAd.swift | 29 ++++ 6 files changed, 550 insertions(+) create mode 100644 Source/BidMachineAdapter.swift create mode 100644 Source/BidMachineAdapterAd.swift create mode 100644 Source/BidMachineAdapterBannerAd.swift create mode 100644 Source/BidMachineAdapterConfig.swift create mode 100644 Source/BidMachineAdapterInterstitialAd.swift create mode 100644 Source/BidMachineAdapterRewardedAd.swift diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift new file mode 100644 index 0000000..f3f45c0 --- /dev/null +++ b/Source/BidMachineAdapter.swift @@ -0,0 +1,125 @@ +// Copyright 2022-2023 Chartboost, Inc. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +import ChartboostMediationSDK +import Foundation +import UIKit +import BidMachine + +final class BidMachineAdapter: PartnerAdapter { + + private let SOURCE_ID_KEY = "source_id" + + /// The version of the partner SDK. + let partnerSDKVersion = BidMachineSdk.sdkVersion + + /// The version of the adapter. + /// It should have either 5 or 6 digits separated by periods, where the first digit is Chartboost Mediation SDK's major version, the last digit is the adapter's build version, and intermediate digits are the partner SDK's version. + /// Format: `.....` where `.` is optional. + let adapterVersion = "4.2.3.0.1" + + /// The partner's unique identifier. + let partnerIdentifier = "bidmachine" + + /// The human-friendly partner name. + let partnerDisplayName = "BidMachine" + + /// The designated initializer for the adapter. + /// Chartboost Mediation SDK will use this constructor to create instances of conforming types. + /// - parameter storage: An object that exposes storage managed by the Chartboost Mediation SDK to the adapter. + /// It includes a list of created `PartnerAd` instances. You may ignore this parameter if you don't need it. + init(storage: PartnerAdapterStorage) { + } + + /// Does any setup needed before beginning to load ads. + /// - parameter configuration: Configuration data for the adapter to set up. + /// - parameter completion: Closure to be performed by the adapter when it's done setting up. It should include an error indicating the cause for failure or `nil` if the operation finished successfully. + func setUp(with configuration: PartnerConfiguration, completion: @escaping (Error?) -> Void) { + log(.setUpStarted) + BidMachineSdk.shared.populate { + $0.withTestMode(BidMachineAdapterConfiguration.testMode) + .withLoggingMode(BidMachineAdapterConfiguration.logging) + .withBidLoggingMode(BidMachineAdapterConfiguration.bidLogging) + .withEventLoggingMode(BidMachineAdapterConfiguration.eventLogging) + } + BidMachineSdk.shared.regulationInfo.populate { + $0.withCOPPA(BidMachineAdapterConfiguration.coppa) + .withGDPRZone(BidMachineAdapterConfiguration.gdprZone) + .withGDPRConsent(BidMachineAdapterConfiguration.gdprConsent) + .withUSPrivacyString(BidMachineAdapterConfiguration.usPrivacyString) + } + guard let sourceID = configuration.credentials[SOURCE_ID_KEY] as? String else { + let error = error(.initializationFailureInvalidCredentials, description: "The 'source ID' was invalid") + log(.setUpFailed(error)) + completion(error) + return + } + // Initialize the SDK + BidMachineSdk.shared.initializeSdk(sourceID) + guard BidMachineSdk.shared.isInitialized == true else { + let error = error(.initializationFailureUnknown) + log(.setUpFailed(error)) + completion(error) + return + } + log(.setUpSucceded) + completion(nil) + } + + /// Fetches bidding tokens needed for the partner to participate in an auction. + /// - parameter request: Information about the ad load request. + /// - parameter completion: Closure to be performed with the fetched info. + func fetchBidderInformation(request: PreBidRequest, completion: @escaping ([String : String]?) -> Void) { + log(.fetchBidderInfoStarted(request)) + guard let token = BidMachineSdk.shared.token else { + let error = error(.prebidFailureInvalidArgument, description: "No bidding token provided by BidMachine SDK") + log(.fetchBidderInfoFailed(request, error: error)) + completion(nil) + return + } + log(.fetchBidderInfoSucceeded(request)) + completion(["token": token]) + } + + /// Indicates if GDPR applies or not and the user's GDPR consent status. + /// - parameter applies: `true` if GDPR applies, `false` if not, `nil` if the publisher has not provided this information. + /// - parameter status: One of the `GDPRConsentStatus` values depending on the user's preference. + func setGDPR(applies: Bool?, status: GDPRConsentStatus) { +// log(.privacyUpdated(setting: "consentsToTracking", value: consentString)) + } + + /// Indicates the CCPA status both as a boolean and as an IAB US privacy string. + /// - parameter hasGivenConsent: A boolean indicating if the user has given consent. + /// - parameter privacyString: An IAB-compliant string indicating the CCPA status. + func setCCPA(hasGivenConsent: Bool, privacyString: String) { +// log(.privacyUpdated(setting: "ccpaConsent", value: consent)) + } + + /// Indicates if the user is subject to COPPA or not. + /// - parameter isChildDirected: `true` if the user is subject to COPPA, `false` otherwise. + func setCOPPA(isChildDirected: Bool) { +// log(.privacyUpdated(setting: "coppaExempt", value: !isChildDirected)) + } + + /// Creates a new ad object in charge of communicating with a single partner SDK ad instance. + /// Chartboost Mediation SDK calls this method to create a new ad for each new load request. Ad instances are never reused. + /// Chartboost Mediation SDK takes care of storing and disposing of ad instances so you don't need to. + /// `invalidate()` is called on ads before disposing of them in case partners need to perform any custom logic before the object gets destroyed. + /// If, for some reason, a new ad cannot be provided, an error should be thrown. + /// - parameter request: Information about the ad load request. + /// - parameter delegate: The delegate that will receive ad life-cycle notifications. + func makeAd(request: PartnerAdLoadRequest, delegate: PartnerAdDelegate) throws -> PartnerAd { + switch request.format { + case .interstitial: + return BidMachineAdapterInterstitialAd(adapter: self, request: request, delegate: delegate) + case .rewarded: + return BidMachineAdapterRewardedAd(adapter: self, request: request, delegate: delegate) + case .banner: + return BidMachineAdapterBannerAd(adapter: self, request: request, delegate: delegate) + default: + throw error(.loadFailureUnsupportedAdFormat) + } + } +} diff --git a/Source/BidMachineAdapterAd.swift b/Source/BidMachineAdapterAd.swift new file mode 100644 index 0000000..75a3aff --- /dev/null +++ b/Source/BidMachineAdapterAd.swift @@ -0,0 +1,37 @@ +// Copyright 2022-2023 Chartboost, Inc. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +import ChartboostMediationSDK +import Foundation + +class BidMachineAdapterAd: NSObject { + + /// The partner ad view to display inline. E.g. a banner view. + /// Should be nil for full-screen ads. + var inlineView: UIView? + + /// The partner adapter that created this ad. + let adapter: PartnerAdapter + + /// The ad load request associated to the ad. + /// It should be the one provided on `PartnerAdapter.makeAd(request:delegate:)`. + let request: PartnerAdLoadRequest + + /// The partner ad delegate to send ad life-cycle events to. + /// It should be the one provided on `PartnerAdapter.makeAd(request:delegate:)`. + weak var delegate: PartnerAdDelegate? + + /// The completion for the ongoing load operation. + var loadCompletion: ((Result) -> Void)? + + /// The completion for the ongoing show operation. + var showCompletion: ((Result) -> Void)? + + init(adapter: PartnerAdapter, request: PartnerAdLoadRequest, delegate: PartnerAdDelegate) { + self.adapter = adapter + self.request = request + self.delegate = delegate + } +} diff --git a/Source/BidMachineAdapterBannerAd.swift b/Source/BidMachineAdapterBannerAd.swift new file mode 100644 index 0000000..bae8f23 --- /dev/null +++ b/Source/BidMachineAdapterBannerAd.swift @@ -0,0 +1,149 @@ +// Copyright 2022-2023 Chartboost, Inc. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +import ChartboostMediationSDK +import Foundation +import BidMachine +import BidMachineApiCore // Needed for the PlacementFormat type + +final class BidMachineAdapterBannerAd: BidMachineAdapterAd, PartnerAd { + + /// The BidMachineSDK ad instance. + private var ad: BidMachineBanner? + + /// Loads an ad. + /// - parameter viewController: The view controller on which the ad will be presented on. Needed on load for some banners. + /// - parameter completion: Closure to be performed once the ad has been loaded. + func load(with viewController: UIViewController?, completion: @escaping (Result) -> Void) { + log(.loadStarted) + + let bannerType = BidMachineApiCore.PlacementFormat.from(size: request.size) + + let config: BidMachineRequestConfigurationProtocol + do { + config = try BidMachineSdk.shared.requestConfiguration(bannerType) + } catch { + let chartboostMediationError = self.error(.loadFailureUnknown, error: error) + log(.loadFailed(chartboostMediationError)) + completion(.failure(chartboostMediationError)) + return + } + + BidMachineSdk.shared.banner(config) { [weak self] ad, error in + guard let self else { + return + } + guard let ad else { + let chartboostMediationError = self.error(.loadFailureUnknown, error: error) + log(.loadFailed(chartboostMediationError)) + completion(.failure(chartboostMediationError)) + return + } + self.loadCompletion = completion + self.ad = ad + ad.controller = viewController + ad.delegate = self + ad.loadAd() + } + } + + /// Shows a loaded ad. + /// It will never get called for banner ads. You may leave the implementation blank for that ad format. + /// - parameter viewController: The view controller on which the ad will be presented on. + /// - parameter completion: Closure to be performed once the ad has been shown. + func show(with viewController: UIViewController, completion: @escaping (Result) -> Void) { + // no-op + } +} + +extension BidMachineAdapterBannerAd: BidMachineAdDelegate { + func didLoadAd(_ ad: BidMachine.BidMachineAdProtocol) { + // Because 'show' isn't a separate step for banners, we don't declare a load success until + // after any show checks are done + guard let bannerAdView = ad as? UIView, + ad.canShow else { + let loadError = error(.loadFailureUnknown) + log(.loadFailed(loadError)) + loadCompletion?(.failure(loadError)) + loadCompletion = nil + let showError = error(.showFailureAdNotReady) + log(.showFailed(showError)) + return + } + log(.loadSucceeded) + loadCompletion?(.success([:])) ?? log(.loadResultIgnored) + loadCompletion = nil + + log(.showStarted) + // 'ad' parameter has already been cast as a UIView so it can be passed to addSubview() + self.inlineView?.addSubview(bannerAdView) + } + + func didFailLoadAd(_ ad: BidMachine.BidMachineAdProtocol, _ error: Error) { + log(.loadFailed(error)) + loadCompletion?(.failure(error)) ?? log(.loadResultIgnored) + loadCompletion = nil + } + + func didPresentAd(_ ad: BidMachineAdProtocol) { + log(.showSucceeded) + showCompletion?(.success([:])) ?? log(.showResultIgnored) + showCompletion = nil + } + + func didFailPresentAd(_ ad: BidMachineAdProtocol, _ error: Error) { + log(.showFailed(error)) + showCompletion?(.failure(error)) ?? log(.showResultIgnored) + showCompletion = nil + } + + func didDismissAd(_ ad: BidMachineAdProtocol) { + // TODO: ? https://github.com/ChartBoost/chartboost-mediation-ios-adapter-vungle/pull/44#discussion_r1271012031 + log(.delegateCallIgnored) + } + + func willPresentScreen(_ ad: BidMachineAdProtocol) { + log(.delegateCallIgnored) + } + + func didDismissScreen(_ ad: BidMachineAdProtocol) { + log(.delegateCallIgnored) + } + + func didUserInteraction(_ ad: BidMachineAdProtocol) { + log(.didClick(error: nil)) + delegate?.didClick(self, details: [:]) ?? log(.delegateUnavailable) + } + + func didExpired(_ ad: BidMachineAdProtocol) { + log(.didExpire) + delegate?.didExpire(self, details: [:]) ?? log(.delegateUnavailable) + } + + func didTrackImpression(_ ad: BidMachineAdProtocol) { + log(.didTrackImpression) + self.delegate?.didTrackImpression(self, details: [:]) ?? log(.delegateUnavailable) + } + + func didTrackInteraction(_ ad: BidMachineAdProtocol) { + log(.delegateCallIgnored) + } +} + +extension BidMachineApiCore.PlacementFormat { + static func from(size: CGSize?) -> BidMachineApiCore.PlacementFormat { + let height = size?.height ?? 50 + switch height { + case 50...89: + return .banner320x50 + case 90...249: + return .banner728x90 + case 250...: + return .banner300x250 + default: + return .banner320x50 + } + } +} diff --git a/Source/BidMachineAdapterConfig.swift b/Source/BidMachineAdapterConfig.swift new file mode 100644 index 0000000..d605408 --- /dev/null +++ b/Source/BidMachineAdapterConfig.swift @@ -0,0 +1,78 @@ +// Copyright 2022-2023 Chartboost, Inc. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +import Foundation + + +/// A list of externally configurable properties pertaining to the partner SDK that can be retrieved and set by publishers. +@objc public class BidMachineAdapterConfiguration: NSObject { + private static let COPPA_KEY = "com.chartboost.adapter.bidmachine.coppa" + private static let GDPR_ZONE_KEY = "com.chartboost.adapter.bidmachine.gdprzone" + private static let GDPR_CONSENT_KEY = "com.chartboost.adapter.bidmachine.gdprconsent" + private static let US_PRIVACY_STRING = "com.chartboost.adapter.bidmachine.usprivacystring" + // IAB US Privacy String defined at: https://github.com/InteractiveAdvertisingBureau/USPrivacy/blob/master/CCPA/US%20Privacy%20String.md + // "1NN-" = Specification v1, no notice given, user has not opted-out, LSPA not applicable + private static let DEFAULT_PRIVACY_STRING = "1NN-" + + /// Flag that specifies whether the BidMachine SDK starts up in test mode + /// Default value is 'false' + @objc public static var testMode: Bool = false + + /// Flag that specifies whether BidMachine SDK logging mode is on + /// Default value is 'false' + @objc public static var logging: Bool = false + + /// Flag that specifies whether the BidMachine SDK bid logging is on + /// Default value is 'false' + @objc public static var bidLogging: Bool = false + + /// Flag that specifies whether the BidMachine SDK event logging is on + /// Default value is 'false' + @objc public static var eventLogging: Bool = false + + + // 'UserDefaults.standard.string(forKey:)' returns 'nil' if there is no value for a key. + // But 'UserDefaults.standard.bool(forKey:)' returns 'false' if there is no value for a key. + // So for bool values we need to explicitly check to see if there's already a + // stored value in order to apply default values other than false. + + @objc public static var coppa: Bool { + get { + // If the value hasn't been stored during a previous launch, default to true + if UserDefaults.standard.object(forKey: COPPA_KEY) == nil { + UserDefaults.standard.setValue(true, forKey: COPPA_KEY) + } + return UserDefaults.standard.bool(forKey: COPPA_KEY) + } + set { UserDefaults.standard.setValue(newValue, forKey: COPPA_KEY) } + } + + @objc public static var gdprZone: Bool { + get { + // If the value hasn't been stored during a previous launch, default to true + if UserDefaults.standard.object(forKey: GDPR_ZONE_KEY) == nil { + UserDefaults.standard.setValue(true, forKey: GDPR_ZONE_KEY) + } + return UserDefaults.standard.bool(forKey: GDPR_ZONE_KEY) + } + set { UserDefaults.standard.setValue(newValue, forKey: GDPR_ZONE_KEY) } + } + + @objc public static var gdprConsent: Bool { + get { + // Will default to 'false' if there is no stored value + return UserDefaults.standard.bool(forKey: GDPR_CONSENT_KEY) + } + set { UserDefaults.standard.setValue(newValue, forKey: GDPR_CONSENT_KEY) } + } + + @objc public static var usPrivacyString: String { + get { + // If the value hasn't been stored during a previous launch, use safe default + return UserDefaults.standard.string(forKey: US_PRIVACY_STRING) ?? DEFAULT_PRIVACY_STRING + } + set { UserDefaults.standard.setValue(newValue, forKey: US_PRIVACY_STRING) } + } +} diff --git a/Source/BidMachineAdapterInterstitialAd.swift b/Source/BidMachineAdapterInterstitialAd.swift new file mode 100644 index 0000000..18ce902 --- /dev/null +++ b/Source/BidMachineAdapterInterstitialAd.swift @@ -0,0 +1,132 @@ +// Copyright 2022-2023 Chartboost, Inc. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +import ChartboostMediationSDK +import Foundation +import BidMachine + +final class BidMachineAdapterInterstitialAd: BidMachineAdapterAd, PartnerAd { + + /// The BidMachineSDK ad instance. + private var ad: BidMachineInterstitial? + + /// Loads an ad. + /// - parameter viewController: The view controller on which the ad will be presented on. Needed on load for some banners. + /// - parameter completion: Closure to be performed once the ad has been loaded. + func load(with viewController: UIViewController?, completion: @escaping (Result) -> Void) { + log(.loadStarted) + + // Make request configuration + let config: BidMachineRequestConfigurationProtocol + do { + config = try BidMachineSdk.shared.requestConfiguration(.interstitial) + } catch { + let chartboostMediationError = self.error(.loadFailureUnknown, error: error) + log(.loadFailed(chartboostMediationError)) + completion(.failure(chartboostMediationError)) + return + } + + // https://docs.bidmachine.io/docs/ad-request + // Once our placements are live, there are a few things to figure out with trial + // and error. First, see if '.populate' can be called multiple times. + // That way, with.Payload() could only be called when there's an ADM (assuming I'm + // correct about that being what they mean by payload. Maybe PlacementID isn't needed + // when we're loading an ADM, and vice versa? + config.populate { + $0.withPlacementId(request.partnerPlacement) + } + + BidMachineSdk.shared.interstitial(config) { [weak self] ad, error in + guard let self = self else { + return + } + guard let ad = ad else { + let chartboostMediationError = self.error(.loadFailureUnknown, error: error) + log(.loadFailed(chartboostMediationError)) + completion(.failure(chartboostMediationError)) + return + } + loadCompletion = completion + self.ad = ad + ad.controller = viewController + ad.delegate = self + ad.loadAd() + } + } + + /// Shows a loaded ad. + /// It will never get called for banner ads. You may leave the implementation blank for that ad format. + /// - parameter viewController: The view controller on which the ad will be presented on. + /// - parameter completion: Closure to be performed once the ad has been shown. + func show(with viewController: UIViewController, completion: @escaping (Result) -> Void) { + log(.showStarted) + guard let ad = ad, ad.canShow else { + let error = error(.showFailureAdNotReady) + log(.showFailed(error)) + completion(.failure(error)) + return + } + showCompletion = completion + ad.presentAd() + } +} + +extension BidMachineAdapterInterstitialAd: BidMachineAdDelegate { + func didLoadAd(_ ad: BidMachine.BidMachineAdProtocol) { + log(.loadSucceeded) + loadCompletion?(.success([:])) ?? log(.loadResultIgnored) + loadCompletion = nil + } + + func didFailLoadAd(_ ad: BidMachine.BidMachineAdProtocol, _ error: Error) { + log(.loadFailed(error)) + loadCompletion?(.failure(error)) ?? log(.loadResultIgnored) + loadCompletion = nil + } + + func didPresentAd(_ ad: BidMachineAdProtocol) { + log(.showSucceeded) + showCompletion?(.success([:])) ?? log(.showResultIgnored) + showCompletion = nil + } + + func didFailPresentAd(_ ad: BidMachineAdProtocol, _ error: Error) { + log(.showFailed(error)) + showCompletion?(.failure(error)) ?? log(.showResultIgnored) + showCompletion = nil + } + + func didDismissAd(_ ad: BidMachineAdProtocol) { + log(.delegateCallIgnored) + } + + func willPresentScreen(_ ad: BidMachineAdProtocol) { + log(.delegateCallIgnored) + } + + func didDismissScreen(_ ad: BidMachineAdProtocol) { + log(.delegateCallIgnored) + } + + func didUserInteraction(_ ad: BidMachineAdProtocol) { + log(.didClick(error: nil)) + delegate?.didClick(self, details: [:]) ?? log(.delegateUnavailable) + } + + func didExpired(_ ad: BidMachineAdProtocol) { + log(.didExpire) + delegate?.didExpire(self, details: [:]) ?? log(.delegateUnavailable) + } + + func didTrackImpression(_ ad: BidMachineAdProtocol) { + log(.didTrackImpression) + self.delegate?.didTrackImpression(self, details: [:]) ?? log(.delegateUnavailable) + } + + func didTrackInteraction(_ ad: BidMachineAdProtocol) { + log(.delegateCallIgnored) + } +} diff --git a/Source/BidMachineAdapterRewardedAd.swift b/Source/BidMachineAdapterRewardedAd.swift new file mode 100644 index 0000000..b8564c4 --- /dev/null +++ b/Source/BidMachineAdapterRewardedAd.swift @@ -0,0 +1,29 @@ +// Copyright 2022-2023 Chartboost, Inc. +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +import ChartboostMediationSDK +import Foundation +import BidMachine + +final class BidMachineAdapterRewardedAd: BidMachineAdapterAd, PartnerAd { + + /// The BidMachineSDK ad instance. + var ad: BidMachineRewarded? + + /// Loads an ad. + /// - parameter viewController: The view controller on which the ad will be presented on. Needed on load for some banners. + /// - parameter completion: Closure to be performed once the ad has been loaded. + func load(with viewController: UIViewController?, completion: @escaping (Result) -> Void) { + log(.loadStarted) + } + + /// Shows a loaded ad. + /// It will never get called for banner ads. You may leave the implementation blank for that ad format. + /// - parameter viewController: The view controller on which the ad will be presented on. + /// - parameter completion: Closure to be performed once the ad has been shown. + func show(with viewController: UIViewController, completion: @escaping (Result) -> Void) { + log(.showStarted) + } +} From 8c9b81ff149513be8dedd6c4d9b3a71db979b8b1 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Wed, 11 Oct 2023 16:47:59 -0700 Subject: [PATCH 02/23] Fix version number --- README.md | 10 +++++----- Source/BidMachineAdapter.swift | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 27f5763..5ae128d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Chartboost Mediation {Partner} Adapter +# Chartboost Mediation BidMachine Adapter -The Chartboost Mediation {Partner} adapter mediates {Partner} via the Chartboost Mediation SDK. +The Chartboost Mediation BidMachine adapter mediates BidMachine via the Chartboost Mediation SDK. ## Minimum Requirements @@ -8,14 +8,14 @@ The Chartboost Mediation {Partner} adapter mediates {Partner} via the Chartboost | ------ | ------ | | Chartboost Mediation SDK | 4.0.0+ | | Cocoapods | 1.11.3+ | -| iOS | {Partner's minimum iOS version} | +| iOS | 12 | | Xcode | 14.1+ | ## Integration In your `Podfile`, add the following entry: ``` -pod 'ChartboostMediationAdapter{Partner}' +pod 'ChartboostMediationAdapterBidMachine' ``` ## Contributions @@ -26,4 +26,4 @@ Refer to our [CONTRIBUTING](CONTRIBUTING.md) file for more information on how to ## License -Refer to our [LICENSE](LICENSE.md) file for more information. \ No newline at end of file +Refer to our [LICENSE](LICENSE.md) file for more information. diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift index f3f45c0..945e49a 100644 --- a/Source/BidMachineAdapter.swift +++ b/Source/BidMachineAdapter.swift @@ -18,7 +18,7 @@ final class BidMachineAdapter: PartnerAdapter { /// The version of the adapter. /// It should have either 5 or 6 digits separated by periods, where the first digit is Chartboost Mediation SDK's major version, the last digit is the adapter's build version, and intermediate digits are the partner SDK's version. /// Format: `.....` where `.` is optional. - let adapterVersion = "4.2.3.0.1" + let adapterVersion = "4.2.3.0.0" /// The partner's unique identifier. let partnerIdentifier = "bidmachine" From 597973d32fa2b98190de15f7cb4c3ec9f09c27d6 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Wed, 11 Oct 2023 16:48:10 -0700 Subject: [PATCH 03/23] Fix interstitial ad loading --- Source/BidMachineAdapterInterstitialAd.swift | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Source/BidMachineAdapterInterstitialAd.swift b/Source/BidMachineAdapterInterstitialAd.swift index 18ce902..ed4d666 100644 --- a/Source/BidMachineAdapterInterstitialAd.swift +++ b/Source/BidMachineAdapterInterstitialAd.swift @@ -29,14 +29,13 @@ final class BidMachineAdapterInterstitialAd: BidMachineAdapterAd, PartnerAd { return } - // https://docs.bidmachine.io/docs/ad-request - // Once our placements are live, there are a few things to figure out with trial - // and error. First, see if '.populate' can be called multiple times. - // That way, with.Payload() could only be called when there's an ADM (assuming I'm - // correct about that being what they mean by payload. Maybe PlacementID isn't needed - // when we're loading an ADM, and vice versa? - config.populate { - $0.withPlacementId(request.partnerPlacement) + loadCompletion = completion + + // There's no harm in setting the placement ID when loading a bidding ad, but calling + // .withPayload(request.adm ?? "") causes an error when BidMachine parses the empty string + config.populate { $0.withPlacementId(request.partnerPlacement) } + if let adm = request.adm { + config.populate { $0.withPayload(adm) } } BidMachineSdk.shared.interstitial(config) { [weak self] ad, error in @@ -49,9 +48,7 @@ final class BidMachineAdapterInterstitialAd: BidMachineAdapterAd, PartnerAd { completion(.failure(chartboostMediationError)) return } - loadCompletion = completion self.ad = ad - ad.controller = viewController ad.delegate = self ad.loadAd() } @@ -63,6 +60,7 @@ final class BidMachineAdapterInterstitialAd: BidMachineAdapterAd, PartnerAd { /// - parameter completion: Closure to be performed once the ad has been shown. func show(with viewController: UIViewController, completion: @escaping (Result) -> Void) { log(.showStarted) + ad?.controller = viewController guard let ad = ad, ad.canShow else { let error = error(.showFailureAdNotReady) log(.showFailed(error)) From c4bccf91fc292e6049618cb8914d4d012c1bd591 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Thu, 12 Oct 2023 14:40:19 -0700 Subject: [PATCH 04/23] Add rewarded ad --- Source/BidMachineAdapterRewardedAd.swift | 115 ++++++++++++++++++++++- 1 file changed, 111 insertions(+), 4 deletions(-) diff --git a/Source/BidMachineAdapterRewardedAd.swift b/Source/BidMachineAdapterRewardedAd.swift index b8564c4..6a2d0db 100644 --- a/Source/BidMachineAdapterRewardedAd.swift +++ b/Source/BidMachineAdapterRewardedAd.swift @@ -8,22 +8,129 @@ import Foundation import BidMachine final class BidMachineAdapterRewardedAd: BidMachineAdapterAd, PartnerAd { - + /// The BidMachineSDK ad instance. - var ad: BidMachineRewarded? - + private var ad: BidMachineRewarded? + /// Loads an ad. /// - parameter viewController: The view controller on which the ad will be presented on. Needed on load for some banners. /// - parameter completion: Closure to be performed once the ad has been loaded. func load(with viewController: UIViewController?, completion: @escaping (Result) -> Void) { log(.loadStarted) + + // Make request configuration + let config: BidMachineRequestConfigurationProtocol + do { + config = try BidMachineSdk.shared.requestConfiguration(.rewarded) + } catch { + let chartboostMediationError = self.error(.loadFailureUnknown, error: error) + log(.loadFailed(chartboostMediationError)) + completion(.failure(chartboostMediationError)) + return + } + + loadCompletion = completion + + // There's no harm in setting the placement ID when loading a bidding ad, but calling + // .withPayload(request.adm ?? "") causes an error when BidMachine parses the empty string + config.populate { $0.withPlacementId(request.partnerPlacement) } + if let adm = request.adm { + config.populate { $0.withPayload(adm) } + } + + BidMachineSdk.shared.rewarded(config) { [weak self] ad, error in + guard let self = self else { + return + } + guard let ad = ad else { + let chartboostMediationError = self.error(.loadFailureUnknown, error: error) + log(.loadFailed(chartboostMediationError)) + completion(.failure(chartboostMediationError)) + return + } + self.ad = ad + ad.delegate = self + ad.loadAd() + } } - + /// Shows a loaded ad. /// It will never get called for banner ads. You may leave the implementation blank for that ad format. /// - parameter viewController: The view controller on which the ad will be presented on. /// - parameter completion: Closure to be performed once the ad has been shown. func show(with viewController: UIViewController, completion: @escaping (Result) -> Void) { log(.showStarted) + ad?.controller = viewController + guard let ad = ad, ad.canShow else { + let error = error(.showFailureAdNotReady) + log(.showFailed(error)) + completion(.failure(error)) + return + } + showCompletion = completion + ad.presentAd() } } + +extension BidMachineAdapterRewardedAd: BidMachineAdDelegate { + func didLoadAd(_ ad: BidMachine.BidMachineAdProtocol) { + log(.loadSucceeded) + loadCompletion?(.success([:])) ?? log(.loadResultIgnored) + loadCompletion = nil + } + + func didFailLoadAd(_ ad: BidMachine.BidMachineAdProtocol, _ error: Error) { + log(.loadFailed(error)) + loadCompletion?(.failure(error)) ?? log(.loadResultIgnored) + loadCompletion = nil + } + + func didPresentAd(_ ad: BidMachineAdProtocol) { + log(.showSucceeded) + showCompletion?(.success([:])) ?? log(.showResultIgnored) + showCompletion = nil + } + + func didFailPresentAd(_ ad: BidMachineAdProtocol, _ error: Error) { + log(.showFailed(error)) + showCompletion?(.failure(error)) ?? log(.showResultIgnored) + showCompletion = nil + } + + func didDismissAd(_ ad: BidMachineAdProtocol) { + log(.delegateCallIgnored) + } + + func willPresentScreen(_ ad: BidMachineAdProtocol) { + log(.delegateCallIgnored) + } + + func didDismissScreen(_ ad: BidMachineAdProtocol) { + log(.delegateCallIgnored) + } + + func didUserInteraction(_ ad: BidMachineAdProtocol) { + log(.didClick(error: nil)) + delegate?.didClick(self, details: [:]) ?? log(.delegateUnavailable) + } + + func didExpired(_ ad: BidMachineAdProtocol) { + log(.didExpire) + delegate?.didExpire(self, details: [:]) ?? log(.delegateUnavailable) + } + + func didTrackImpression(_ ad: BidMachineAdProtocol) { + log(.didTrackImpression) + self.delegate?.didTrackImpression(self, details: [:]) ?? log(.delegateUnavailable) + } + + func didTrackInteraction(_ ad: BidMachineAdProtocol) { + log(.delegateCallIgnored) + } + + func didReceiveReward(_ ad: BidMachineAdProtocol) { + log(.didReward) + delegate?.didReward(self, details: [:]) ?? log(.delegateUnavailable) + } +} + From 8cd5cf205e02fecebad42fa562363acec8d987e1 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Thu, 12 Oct 2023 14:40:42 -0700 Subject: [PATCH 05/23] Banner ads now work --- Source/BidMachineAdapterBannerAd.swift | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Source/BidMachineAdapterBannerAd.swift b/Source/BidMachineAdapterBannerAd.swift index bae8f23..f913f7d 100644 --- a/Source/BidMachineAdapterBannerAd.swift +++ b/Source/BidMachineAdapterBannerAd.swift @@ -21,6 +21,7 @@ final class BidMachineAdapterBannerAd: BidMachineAdapterAd, PartnerAd { let bannerType = BidMachineApiCore.PlacementFormat.from(size: request.size) + // Make request configuration let config: BidMachineRequestConfigurationProtocol do { config = try BidMachineSdk.shared.requestConfiguration(bannerType) @@ -31,6 +32,15 @@ final class BidMachineAdapterBannerAd: BidMachineAdapterAd, PartnerAd { return } + self.loadCompletion = completion + + // There's no harm in setting the placement ID when loading a bidding ad, but calling + // .withPayload(request.adm ?? "") causes an error when BidMachine parses the empty string + config.populate { $0.withPlacementId(request.partnerPlacement) } + if let adm = request.adm { + config.populate { $0.withPayload(adm) } + } + BidMachineSdk.shared.banner(config) { [weak self] ad, error in guard let self else { return @@ -41,8 +51,7 @@ final class BidMachineAdapterBannerAd: BidMachineAdapterAd, PartnerAd { completion(.failure(chartboostMediationError)) return } - self.loadCompletion = completion - self.ad = ad + self.inlineView = ad ad.controller = viewController ad.delegate = self ad.loadAd() @@ -62,8 +71,8 @@ extension BidMachineAdapterBannerAd: BidMachineAdDelegate { func didLoadAd(_ ad: BidMachine.BidMachineAdProtocol) { // Because 'show' isn't a separate step for banners, we don't declare a load success until // after any show checks are done - guard let bannerAdView = ad as? UIView, - ad.canShow else { + guard let bannerAdView = ad as? BidMachineBanner, + bannerAdView.canShow else { let loadError = error(.loadFailureUnknown) log(.loadFailed(loadError)) loadCompletion?(.failure(loadError)) @@ -77,8 +86,6 @@ extension BidMachineAdapterBannerAd: BidMachineAdDelegate { loadCompletion = nil log(.showStarted) - // 'ad' parameter has already been cast as a UIView so it can be passed to addSubview() - self.inlineView?.addSubview(bannerAdView) } func didFailLoadAd(_ ad: BidMachine.BidMachineAdProtocol, _ error: Error) { From 3f82eb4192a59b8c333860cb259bd0ea4e6f4e3c Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Thu, 12 Oct 2023 14:50:59 -0700 Subject: [PATCH 06/23] Connect privacy methods --- Source/BidMachineAdapter.swift | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift index 945e49a..4bc2175 100644 --- a/Source/BidMachineAdapter.swift +++ b/Source/BidMachineAdapter.swift @@ -87,20 +87,34 @@ final class BidMachineAdapter: PartnerAdapter { /// - parameter applies: `true` if GDPR applies, `false` if not, `nil` if the publisher has not provided this information. /// - parameter status: One of the `GDPRConsentStatus` values depending on the user's preference. func setGDPR(applies: Bool?, status: GDPRConsentStatus) { -// log(.privacyUpdated(setting: "consentsToTracking", value: consentString)) + if let applies = applies { + BidMachineAdapterConfiguration.gdprZone = applies + log(.privacyUpdated(setting: "gdprZone", value: applies)) + } + + // In the case where status == .unknown, we do nothing + if status == .denied { + BidMachineAdapterConfiguration.gdprConsent = false + log(.privacyUpdated(setting: "gdprConsent", value: false)) + } else if status == .granted { + BidMachineAdapterConfiguration.gdprConsent = true + log(.privacyUpdated(setting: "gdprConsent", value: true)) + } } /// Indicates the CCPA status both as a boolean and as an IAB US privacy string. /// - parameter hasGivenConsent: A boolean indicating if the user has given consent. /// - parameter privacyString: An IAB-compliant string indicating the CCPA status. func setCCPA(hasGivenConsent: Bool, privacyString: String) { -// log(.privacyUpdated(setting: "ccpaConsent", value: consent)) + BidMachineAdapterConfiguration.usPrivacyString = privacyString + log(.privacyUpdated(setting: "UserDefaults CCPA string", value: privacyString)) } /// Indicates if the user is subject to COPPA or not. /// - parameter isChildDirected: `true` if the user is subject to COPPA, `false` otherwise. func setCOPPA(isChildDirected: Bool) { -// log(.privacyUpdated(setting: "coppaExempt", value: !isChildDirected)) + BidMachineAdapterConfiguration.coppa = isChildDirected + log(.privacyUpdated(setting: "coppa", value: isChildDirected)) } /// Creates a new ad object in charge of communicating with a single partner SDK ad instance. From f272dd8883e0cef6233b2905e17ef7f3cbf55244 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Thu, 12 Oct 2023 19:16:30 -0700 Subject: [PATCH 07/23] Remove comment --- Source/BidMachineAdapterBannerAd.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/BidMachineAdapterBannerAd.swift b/Source/BidMachineAdapterBannerAd.swift index f913f7d..68c1172 100644 --- a/Source/BidMachineAdapterBannerAd.swift +++ b/Source/BidMachineAdapterBannerAd.swift @@ -107,7 +107,6 @@ extension BidMachineAdapterBannerAd: BidMachineAdDelegate { } func didDismissAd(_ ad: BidMachineAdProtocol) { - // TODO: ? https://github.com/ChartBoost/chartboost-mediation-ios-adapter-vungle/pull/44#discussion_r1271012031 log(.delegateCallIgnored) } From a550cd3b3522c306fa0d0abff691c695f904b9ac Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Thu, 12 Oct 2023 22:38:18 -0700 Subject: [PATCH 08/23] Cleanup --- ChartboostMediationAdapterBidMachine.podspec | 2 +- Source/BidMachineAdapter.swift | 4 ++-- Source/BidMachineAdapterConfig.swift | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ChartboostMediationAdapterBidMachine.podspec b/ChartboostMediationAdapterBidMachine.podspec index 33c4fb1..7fdc45d 100644 --- a/ChartboostMediationAdapterBidMachine.podspec +++ b/ChartboostMediationAdapterBidMachine.podspec @@ -4,7 +4,7 @@ Pod::Spec.new do |spec| spec.license = { :type => 'MIT', :file => 'LICENSE.md' } spec.homepage = 'https://github.com/ChartBoost/chartboost-mediation-ios-adapter-bidmachine' spec.authors = { 'Chartboost' => 'https://www.chartboost.com/' } - spec.summary = 'Chartboost Mediation iOS SDK Reference adapter.' + spec.summary = 'Chartboost Mediation iOS SDK BidMachine adapter.' spec.description = 'BidMachine Adapters for mediating through Chartboost Mediation. Supported ad formats: banner, interstitial, rewarded.' # Source diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift index 4bc2175..7a6d618 100644 --- a/Source/BidMachineAdapter.swift +++ b/Source/BidMachineAdapter.swift @@ -107,14 +107,14 @@ final class BidMachineAdapter: PartnerAdapter { /// - parameter privacyString: An IAB-compliant string indicating the CCPA status. func setCCPA(hasGivenConsent: Bool, privacyString: String) { BidMachineAdapterConfiguration.usPrivacyString = privacyString - log(.privacyUpdated(setting: "UserDefaults CCPA string", value: privacyString)) + log(.privacyUpdated(setting: "usPrivacyString", value: privacyString)) } /// Indicates if the user is subject to COPPA or not. /// - parameter isChildDirected: `true` if the user is subject to COPPA, `false` otherwise. func setCOPPA(isChildDirected: Bool) { BidMachineAdapterConfiguration.coppa = isChildDirected - log(.privacyUpdated(setting: "coppa", value: isChildDirected)) + log(.privacyUpdated(setting: "COPPA", value: isChildDirected)) } /// Creates a new ad object in charge of communicating with a single partner SDK ad instance. diff --git a/Source/BidMachineAdapterConfig.swift b/Source/BidMachineAdapterConfig.swift index d605408..c58b45c 100644 --- a/Source/BidMachineAdapterConfig.swift +++ b/Source/BidMachineAdapterConfig.swift @@ -16,20 +16,20 @@ import Foundation // "1NN-" = Specification v1, no notice given, user has not opted-out, LSPA not applicable private static let DEFAULT_PRIVACY_STRING = "1NN-" - /// Flag that specifies whether the BidMachine SDK starts up in test mode - /// Default value is 'false' + /// Init flag for starting up BidMachine SDK in test mode. + /// Default value is 'false'. @objc public static var testMode: Bool = false - /// Flag that specifies whether BidMachine SDK logging mode is on - /// Default value is 'false' + /// Init flag for turning on BidMachine SDK general logging. + /// Default value is 'false'. @objc public static var logging: Bool = false - /// Flag that specifies whether the BidMachine SDK bid logging is on - /// Default value is 'false' + /// Init flag for turning on BidMachine SDK bidding logging. + /// Default value is 'false'. @objc public static var bidLogging: Bool = false - /// Flag that specifies whether the BidMachine SDK event logging is on - /// Default value is 'false' + /// Init flag for turning on BidMachine SDK event logging. + /// Default value is 'false'. @objc public static var eventLogging: Bool = false From b9a0bbd50681c2aaf58f21c0c1e94030b844b3f6 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Tue, 17 Oct 2023 16:11:11 -0700 Subject: [PATCH 09/23] Prevent multiple loads --- Source/BidMachineAdapter.swift | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift index 7a6d618..d4ee304 100644 --- a/Source/BidMachineAdapter.swift +++ b/Source/BidMachineAdapter.swift @@ -25,12 +25,16 @@ final class BidMachineAdapter: PartnerAdapter { /// The human-friendly partner name. let partnerDisplayName = "BidMachine" - + + /// Ad storage managed by Chartboost Mediation SDK. + let storage: PartnerAdapterStorage + /// The designated initializer for the adapter. /// Chartboost Mediation SDK will use this constructor to create instances of conforming types. /// - parameter storage: An object that exposes storage managed by the Chartboost Mediation SDK to the adapter. /// It includes a list of created `PartnerAd` instances. You may ignore this parameter if you don't need it. init(storage: PartnerAdapterStorage) { + self.storage = storage } /// Does any setup needed before beginning to load ads. @@ -125,6 +129,16 @@ final class BidMachineAdapter: PartnerAdapter { /// - parameter request: Information about the ad load request. /// - parameter delegate: The delegate that will receive ad life-cycle notifications. func makeAd(request: PartnerAdLoadRequest, delegate: PartnerAdDelegate) throws -> PartnerAd { + // Prevent multiple loads for the same partner placement, since the partner SDK cannot handle them. + // Banner loads are allowed so a banner prefetch can happen during auto-refresh. + // ChartboostMediationSDK 4.x does not support loading more than 2 banners with the same placement, and the partner may or may not support it. + guard !storage.ads.contains(where: { $0.request.partnerPlacement == request.partnerPlacement }) + || request.format == .banner + else { + log("Failed to load ad for already loading placement \(request.partnerPlacement)") + throw error(.loadFailureLoadInProgress) + } + switch request.format { case .interstitial: return BidMachineAdapterInterstitialAd(adapter: self, request: request, delegate: delegate) From 5afd05ca57d3996c6a7aba3a1d9a990f487a4e9c Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Tue, 17 Oct 2023 17:02:20 -0700 Subject: [PATCH 10/23] Suggestions from PR --- Source/BidMachineAdapterBannerAd.swift | 6 ++---- Source/BidMachineAdapterInterstitialAd.swift | 3 ++- Source/BidMachineAdapterRewardedAd.swift | 3 ++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/BidMachineAdapterBannerAd.swift b/Source/BidMachineAdapterBannerAd.swift index 68c1172..5f89f96 100644 --- a/Source/BidMachineAdapterBannerAd.swift +++ b/Source/BidMachineAdapterBannerAd.swift @@ -41,7 +41,7 @@ final class BidMachineAdapterBannerAd: BidMachineAdapterAd, PartnerAd { config.populate { $0.withPayload(adm) } } - BidMachineSdk.shared.banner(config) { [weak self] ad, error in + BidMachineSdk.shared.banner(config) { [weak self, weak viewController] ad, error in guard let self else { return } @@ -75,7 +75,7 @@ extension BidMachineAdapterBannerAd: BidMachineAdDelegate { bannerAdView.canShow else { let loadError = error(.loadFailureUnknown) log(.loadFailed(loadError)) - loadCompletion?(.failure(loadError)) + loadCompletion?(.failure(loadError)) ?? log(.loadResultIgnored) loadCompletion = nil let showError = error(.showFailureAdNotReady) log(.showFailed(showError)) @@ -97,13 +97,11 @@ extension BidMachineAdapterBannerAd: BidMachineAdDelegate { func didPresentAd(_ ad: BidMachineAdProtocol) { log(.showSucceeded) showCompletion?(.success([:])) ?? log(.showResultIgnored) - showCompletion = nil } func didFailPresentAd(_ ad: BidMachineAdProtocol, _ error: Error) { log(.showFailed(error)) showCompletion?(.failure(error)) ?? log(.showResultIgnored) - showCompletion = nil } func didDismissAd(_ ad: BidMachineAdProtocol) { diff --git a/Source/BidMachineAdapterInterstitialAd.swift b/Source/BidMachineAdapterInterstitialAd.swift index ed4d666..cb17b9c 100644 --- a/Source/BidMachineAdapterInterstitialAd.swift +++ b/Source/BidMachineAdapterInterstitialAd.swift @@ -98,7 +98,8 @@ extension BidMachineAdapterInterstitialAd: BidMachineAdDelegate { } func didDismissAd(_ ad: BidMachineAdProtocol) { - log(.delegateCallIgnored) + log(.didDismiss(error: nil)) + delegate?.didDismiss(self, details: [:], error: nil) ?? log(.delegateUnavailable) } func willPresentScreen(_ ad: BidMachineAdProtocol) { diff --git a/Source/BidMachineAdapterRewardedAd.swift b/Source/BidMachineAdapterRewardedAd.swift index 6a2d0db..3c42efb 100644 --- a/Source/BidMachineAdapterRewardedAd.swift +++ b/Source/BidMachineAdapterRewardedAd.swift @@ -98,7 +98,8 @@ extension BidMachineAdapterRewardedAd: BidMachineAdDelegate { } func didDismissAd(_ ad: BidMachineAdProtocol) { - log(.delegateCallIgnored) + log(.didDismiss(error: nil)) + delegate?.didDismiss(self, details: [:], error: nil) ?? log(.delegateUnavailable) } func willPresentScreen(_ ad: BidMachineAdProtocol) { From d8566bcabd48c1d3ab857ac6e4d96bbc6d2bfc97 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Wed, 18 Oct 2023 15:25:53 -0700 Subject: [PATCH 11/23] Support adaptive banners --- Source/BidMachineAdapter.swift | 7 +++++- Source/BidMachineAdapterBannerAd.swift | 35 +++++++++++++++++++------- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift index d4ee304..f1565ee 100644 --- a/Source/BidMachineAdapter.swift +++ b/Source/BidMachineAdapter.swift @@ -147,7 +147,12 @@ final class BidMachineAdapter: PartnerAdapter { case .banner: return BidMachineAdapterBannerAd(adapter: self, request: request, delegate: delegate) default: - throw error(.loadFailureUnsupportedAdFormat) + // Not using the `.adaptiveBanner` case directly to maintain backward compatibility with Chartboost Mediation 4.0 + if request.format.rawValue == "adaptive_banner" { + return BidMachineAdapterBannerAd(adapter: self, request: request, delegate: delegate) + } else { + throw error(.loadFailureUnsupportedAdFormat) + } } } } diff --git a/Source/BidMachineAdapterBannerAd.swift b/Source/BidMachineAdapterBannerAd.swift index 5f89f96..7bf265a 100644 --- a/Source/BidMachineAdapterBannerAd.swift +++ b/Source/BidMachineAdapterBannerAd.swift @@ -19,7 +19,12 @@ final class BidMachineAdapterBannerAd: BidMachineAdapterAd, PartnerAd { func load(with viewController: UIViewController?, completion: @escaping (Result) -> Void) { log(.loadStarted) - let bannerType = BidMachineApiCore.PlacementFormat.from(size: request.size) + guard let bannerType = BidMachineApiCore.PlacementFormat.from(size: request.size ?? IABStandardAdSize) else { + let error = error(.loadFailureInvalidBannerSize) + log(.loadFailed(error)) + completion(.failure(error)) + return + } // Make request configuration let config: BidMachineRequestConfigurationProtocol @@ -137,17 +142,29 @@ extension BidMachineAdapterBannerAd: BidMachineAdDelegate { } extension BidMachineApiCore.PlacementFormat { - static func from(size: CGSize?) -> BidMachineApiCore.PlacementFormat { - let height = size?.height ?? 50 - switch height { - case 50...89: + static func from(size requestedSize: CGSize) -> BidMachineApiCore.PlacementFormat? { + let sizes = [IABLeaderboardAdSize, IABMediumAdSize, IABStandardAdSize] + // Find the largest size that can fit in the requested size. + var bestFit: CGSize? = nil + for size in sizes { + // If height is 0, the pub has requested an ad of any height, so only the width matters. + if requestedSize.width >= size.width && + (size.height == 0 || requestedSize.height >= size.height) { + bestFit = size + } + } + + // Translate IAB size to a BidMachine placement format + switch bestFit { + case IABStandardAdSize: return .banner320x50 - case 90...249: - return .banner728x90 - case 250...: + case IABMediumAdSize: return .banner300x250 + case IABLeaderboardAdSize: + return .banner728x90 default: - return .banner320x50 + // The requested size cannot fit any fixed size banners. + return nil } } } From 5769e6494e3a384e5ec3e2c32e2f6b1d98b030e4 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Wed, 18 Oct 2023 16:45:25 -0700 Subject: [PATCH 12/23] Privacy changes discussed in PR --- Source/BidMachineAdapter.swift | 32 +++++++++++++++---- Source/BidMachineAdapterConfig.swift | 47 ++++++---------------------- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift index f1565ee..41344da 100644 --- a/Source/BidMachineAdapter.swift +++ b/Source/BidMachineAdapter.swift @@ -42,18 +42,28 @@ final class BidMachineAdapter: PartnerAdapter { /// - parameter completion: Closure to be performed by the adapter when it's done setting up. It should include an error indicating the cause for failure or `nil` if the operation finished successfully. func setUp(with configuration: PartnerConfiguration, completion: @escaping (Error?) -> Void) { log(.setUpStarted) + + // Populate only the info we have non-nil saved values for + if let coppa = BidMachineAdapterConfiguration.coppa { + BidMachineSdk.shared.regulationInfo.populate { $0.withCOPPA(coppa) } + } + if let gdprZone = BidMachineAdapterConfiguration.gdprZone { + BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRZone(gdprZone) } + } + if let gdprConsent = BidMachineAdapterConfiguration.gdprConsent { + BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRConsent(gdprConsent) } + } + if let usPrivacyString = BidMachineAdapterConfiguration.usPrivacyString { + BidMachineSdk.shared.regulationInfo.populate { $0.withUSPrivacyString(usPrivacyString) } + } + // These settings all have default values BidMachineSdk.shared.populate { $0.withTestMode(BidMachineAdapterConfiguration.testMode) .withLoggingMode(BidMachineAdapterConfiguration.logging) .withBidLoggingMode(BidMachineAdapterConfiguration.bidLogging) .withEventLoggingMode(BidMachineAdapterConfiguration.eventLogging) } - BidMachineSdk.shared.regulationInfo.populate { - $0.withCOPPA(BidMachineAdapterConfiguration.coppa) - .withGDPRZone(BidMachineAdapterConfiguration.gdprZone) - .withGDPRConsent(BidMachineAdapterConfiguration.gdprConsent) - .withUSPrivacyString(BidMachineAdapterConfiguration.usPrivacyString) - } + guard let sourceID = configuration.credentials[SOURCE_ID_KEY] as? String else { let error = error(.initializationFailureInvalidCredentials, description: "The 'source ID' was invalid") log(.setUpFailed(error)) @@ -94,15 +104,21 @@ final class BidMachineAdapter: PartnerAdapter { if let applies = applies { BidMachineAdapterConfiguration.gdprZone = applies log(.privacyUpdated(setting: "gdprZone", value: applies)) + // It is unknown whether populating this value after initializing the BidMachine SDK does anything + BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRZone(applies) } } // In the case where status == .unknown, we do nothing if status == .denied { BidMachineAdapterConfiguration.gdprConsent = false log(.privacyUpdated(setting: "gdprConsent", value: false)) + // It is unknown whether populating this value after initializing the BidMachine SDK does anything + BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRConsent(false) } } else if status == .granted { BidMachineAdapterConfiguration.gdprConsent = true log(.privacyUpdated(setting: "gdprConsent", value: true)) + // It is unknown whether populating this value after initializing the BidMachine SDK does anything + BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRConsent(true) } } } @@ -112,6 +128,8 @@ final class BidMachineAdapter: PartnerAdapter { func setCCPA(hasGivenConsent: Bool, privacyString: String) { BidMachineAdapterConfiguration.usPrivacyString = privacyString log(.privacyUpdated(setting: "usPrivacyString", value: privacyString)) + // It is unknown whether populating this value after initializing the BidMachine SDK does anything + BidMachineSdk.shared.regulationInfo.populate { $0.withUSPrivacyString(privacyString) } } /// Indicates if the user is subject to COPPA or not. @@ -119,6 +137,8 @@ final class BidMachineAdapter: PartnerAdapter { func setCOPPA(isChildDirected: Bool) { BidMachineAdapterConfiguration.coppa = isChildDirected log(.privacyUpdated(setting: "COPPA", value: isChildDirected)) + // It is unknown whether populating this value after initializing the BidMachine SDK does anything + BidMachineSdk.shared.regulationInfo.populate { $0.withCOPPA(isChildDirected) } } /// Creates a new ad object in charge of communicating with a single partner SDK ad instance. diff --git a/Source/BidMachineAdapterConfig.swift b/Source/BidMachineAdapterConfig.swift index c58b45c..584d7f9 100644 --- a/Source/BidMachineAdapterConfig.swift +++ b/Source/BidMachineAdapterConfig.swift @@ -11,10 +11,7 @@ import Foundation private static let COPPA_KEY = "com.chartboost.adapter.bidmachine.coppa" private static let GDPR_ZONE_KEY = "com.chartboost.adapter.bidmachine.gdprzone" private static let GDPR_CONSENT_KEY = "com.chartboost.adapter.bidmachine.gdprconsent" - private static let US_PRIVACY_STRING = "com.chartboost.adapter.bidmachine.usprivacystring" - // IAB US Privacy String defined at: https://github.com/InteractiveAdvertisingBureau/USPrivacy/blob/master/CCPA/US%20Privacy%20String.md - // "1NN-" = Specification v1, no notice given, user has not opted-out, LSPA not applicable - private static let DEFAULT_PRIVACY_STRING = "1NN-" + private static let US_PRIVACY_STRING_KEY = "com.chartboost.adapter.bidmachine.usprivacystring" /// Init flag for starting up BidMachine SDK in test mode. /// Default value is 'false'. @@ -32,47 +29,23 @@ import Foundation /// Default value is 'false'. @objc public static var eventLogging: Bool = false - - // 'UserDefaults.standard.string(forKey:)' returns 'nil' if there is no value for a key. - // But 'UserDefaults.standard.bool(forKey:)' returns 'false' if there is no value for a key. - // So for bool values we need to explicitly check to see if there's already a - // stored value in order to apply default values other than false. - - @objc public static var coppa: Bool { - get { - // If the value hasn't been stored during a previous launch, default to true - if UserDefaults.standard.object(forKey: COPPA_KEY) == nil { - UserDefaults.standard.setValue(true, forKey: COPPA_KEY) - } - return UserDefaults.standard.bool(forKey: COPPA_KEY) - } + public static var coppa: Bool? { + get { UserDefaults.standard.value(forKey: COPPA_KEY) as? Bool } set { UserDefaults.standard.setValue(newValue, forKey: COPPA_KEY) } } - @objc public static var gdprZone: Bool { - get { - // If the value hasn't been stored during a previous launch, default to true - if UserDefaults.standard.object(forKey: GDPR_ZONE_KEY) == nil { - UserDefaults.standard.setValue(true, forKey: GDPR_ZONE_KEY) - } - return UserDefaults.standard.bool(forKey: GDPR_ZONE_KEY) - } + public static var gdprZone: Bool? { + get { UserDefaults.standard.value(forKey: GDPR_ZONE_KEY) as? Bool } set { UserDefaults.standard.setValue(newValue, forKey: GDPR_ZONE_KEY) } } - @objc public static var gdprConsent: Bool { - get { - // Will default to 'false' if there is no stored value - return UserDefaults.standard.bool(forKey: GDPR_CONSENT_KEY) - } + public static var gdprConsent: Bool? { + get { UserDefaults.standard.value(forKey: GDPR_CONSENT_KEY) as? Bool } set { UserDefaults.standard.setValue(newValue, forKey: GDPR_CONSENT_KEY) } } - @objc public static var usPrivacyString: String { - get { - // If the value hasn't been stored during a previous launch, use safe default - return UserDefaults.standard.string(forKey: US_PRIVACY_STRING) ?? DEFAULT_PRIVACY_STRING - } - set { UserDefaults.standard.setValue(newValue, forKey: US_PRIVACY_STRING) } + public static var usPrivacyString: String? { + get { UserDefaults.standard.string(forKey: US_PRIVACY_STRING_KEY) } + set { UserDefaults.standard.setValue(newValue, forKey: US_PRIVACY_STRING_KEY) } } } From e2353772729fb12d49b47ac078df16ad30ba9b26 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Thu, 26 Oct 2023 15:18:28 -0700 Subject: [PATCH 13/23] Move privacy settings --- Source/BidMachineAdapter.swift | 44 +++++++++++++++++++++------- Source/BidMachineAdapterConfig.swift | 25 +--------------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift index 41344da..809d658 100644 --- a/Source/BidMachineAdapter.swift +++ b/Source/BidMachineAdapter.swift @@ -9,9 +9,33 @@ import UIKit import BidMachine final class BidMachineAdapter: PartnerAdapter { + private let COPPA_KEY = "com.chartboost.adapter.bidmachine.coppa" + private let GDPR_ZONE_KEY = "com.chartboost.adapter.bidmachine.gdprzone" + private let GDPR_CONSENT_KEY = "com.chartboost.adapter.bidmachine.gdprconsent" + private let US_PRIVACY_STRING_KEY = "com.chartboost.adapter.bidmachine.usprivacystring" private let SOURCE_ID_KEY = "source_id" - + + private var coppa: Bool? { + get { UserDefaults.standard.value(forKey: COPPA_KEY) as? Bool } + set { UserDefaults.standard.setValue(newValue, forKey: COPPA_KEY) } + } + + private var gdprZone: Bool? { + get { UserDefaults.standard.value(forKey: GDPR_ZONE_KEY) as? Bool } + set { UserDefaults.standard.setValue(newValue, forKey: GDPR_ZONE_KEY) } + } + + private var gdprConsent: Bool? { + get { UserDefaults.standard.value(forKey: GDPR_CONSENT_KEY) as? Bool } + set { UserDefaults.standard.setValue(newValue, forKey: GDPR_CONSENT_KEY) } + } + + private var usPrivacyString: String? { + get { UserDefaults.standard.string(forKey: US_PRIVACY_STRING_KEY) } + set { UserDefaults.standard.setValue(newValue, forKey: US_PRIVACY_STRING_KEY) } + } + /// The version of the partner SDK. let partnerSDKVersion = BidMachineSdk.sdkVersion @@ -44,16 +68,16 @@ final class BidMachineAdapter: PartnerAdapter { log(.setUpStarted) // Populate only the info we have non-nil saved values for - if let coppa = BidMachineAdapterConfiguration.coppa { + if let coppa = coppa { BidMachineSdk.shared.regulationInfo.populate { $0.withCOPPA(coppa) } } - if let gdprZone = BidMachineAdapterConfiguration.gdprZone { + if let gdprZone = gdprZone { BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRZone(gdprZone) } } - if let gdprConsent = BidMachineAdapterConfiguration.gdprConsent { + if let gdprConsent = gdprConsent { BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRConsent(gdprConsent) } } - if let usPrivacyString = BidMachineAdapterConfiguration.usPrivacyString { + if let usPrivacyString = usPrivacyString { BidMachineSdk.shared.regulationInfo.populate { $0.withUSPrivacyString(usPrivacyString) } } // These settings all have default values @@ -102,7 +126,7 @@ final class BidMachineAdapter: PartnerAdapter { /// - parameter status: One of the `GDPRConsentStatus` values depending on the user's preference. func setGDPR(applies: Bool?, status: GDPRConsentStatus) { if let applies = applies { - BidMachineAdapterConfiguration.gdprZone = applies + gdprZone = applies log(.privacyUpdated(setting: "gdprZone", value: applies)) // It is unknown whether populating this value after initializing the BidMachine SDK does anything BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRZone(applies) } @@ -110,12 +134,12 @@ final class BidMachineAdapter: PartnerAdapter { // In the case where status == .unknown, we do nothing if status == .denied { - BidMachineAdapterConfiguration.gdprConsent = false + gdprConsent = false log(.privacyUpdated(setting: "gdprConsent", value: false)) // It is unknown whether populating this value after initializing the BidMachine SDK does anything BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRConsent(false) } } else if status == .granted { - BidMachineAdapterConfiguration.gdprConsent = true + gdprConsent = true log(.privacyUpdated(setting: "gdprConsent", value: true)) // It is unknown whether populating this value after initializing the BidMachine SDK does anything BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRConsent(true) } @@ -126,7 +150,7 @@ final class BidMachineAdapter: PartnerAdapter { /// - parameter hasGivenConsent: A boolean indicating if the user has given consent. /// - parameter privacyString: An IAB-compliant string indicating the CCPA status. func setCCPA(hasGivenConsent: Bool, privacyString: String) { - BidMachineAdapterConfiguration.usPrivacyString = privacyString + usPrivacyString = privacyString log(.privacyUpdated(setting: "usPrivacyString", value: privacyString)) // It is unknown whether populating this value after initializing the BidMachine SDK does anything BidMachineSdk.shared.regulationInfo.populate { $0.withUSPrivacyString(privacyString) } @@ -135,7 +159,7 @@ final class BidMachineAdapter: PartnerAdapter { /// Indicates if the user is subject to COPPA or not. /// - parameter isChildDirected: `true` if the user is subject to COPPA, `false` otherwise. func setCOPPA(isChildDirected: Bool) { - BidMachineAdapterConfiguration.coppa = isChildDirected + coppa = isChildDirected log(.privacyUpdated(setting: "COPPA", value: isChildDirected)) // It is unknown whether populating this value after initializing the BidMachine SDK does anything BidMachineSdk.shared.regulationInfo.populate { $0.withCOPPA(isChildDirected) } diff --git a/Source/BidMachineAdapterConfig.swift b/Source/BidMachineAdapterConfig.swift index 584d7f9..61d61da 100644 --- a/Source/BidMachineAdapterConfig.swift +++ b/Source/BidMachineAdapterConfig.swift @@ -8,11 +8,6 @@ import Foundation /// A list of externally configurable properties pertaining to the partner SDK that can be retrieved and set by publishers. @objc public class BidMachineAdapterConfiguration: NSObject { - private static let COPPA_KEY = "com.chartboost.adapter.bidmachine.coppa" - private static let GDPR_ZONE_KEY = "com.chartboost.adapter.bidmachine.gdprzone" - private static let GDPR_CONSENT_KEY = "com.chartboost.adapter.bidmachine.gdprconsent" - private static let US_PRIVACY_STRING_KEY = "com.chartboost.adapter.bidmachine.usprivacystring" - /// Init flag for starting up BidMachine SDK in test mode. /// Default value is 'false'. @objc public static var testMode: Bool = false @@ -29,23 +24,5 @@ import Foundation /// Default value is 'false'. @objc public static var eventLogging: Bool = false - public static var coppa: Bool? { - get { UserDefaults.standard.value(forKey: COPPA_KEY) as? Bool } - set { UserDefaults.standard.setValue(newValue, forKey: COPPA_KEY) } - } - - public static var gdprZone: Bool? { - get { UserDefaults.standard.value(forKey: GDPR_ZONE_KEY) as? Bool } - set { UserDefaults.standard.setValue(newValue, forKey: GDPR_ZONE_KEY) } - } - - public static var gdprConsent: Bool? { - get { UserDefaults.standard.value(forKey: GDPR_CONSENT_KEY) as? Bool } - set { UserDefaults.standard.setValue(newValue, forKey: GDPR_CONSENT_KEY) } - } - - public static var usPrivacyString: String? { - get { UserDefaults.standard.string(forKey: US_PRIVACY_STRING_KEY) } - set { UserDefaults.standard.setValue(newValue, forKey: US_PRIVACY_STRING_KEY) } - } + } From 570f70831c5e1ae8389c7c1eb446111cc00c7ad7 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Thu, 26 Oct 2023 16:30:06 -0700 Subject: [PATCH 14/23] Return loaded banner size --- Source/BidMachineAdapterBannerAd.swift | 42 +++++++++++++++++--------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/Source/BidMachineAdapterBannerAd.swift b/Source/BidMachineAdapterBannerAd.swift index 7bf265a..83401b7 100644 --- a/Source/BidMachineAdapterBannerAd.swift +++ b/Source/BidMachineAdapterBannerAd.swift @@ -19,7 +19,8 @@ final class BidMachineAdapterBannerAd: BidMachineAdapterAd, PartnerAd { func load(with viewController: UIViewController?, completion: @escaping (Result) -> Void) { log(.loadStarted) - guard let bannerType = BidMachineApiCore.PlacementFormat.from(size: request.size ?? IABStandardAdSize) else { + guard let size = request.size, + let bannerType = BidMachineApiCore.PlacementFormat.from(size: fixedBannerSize(for: size)) else { let error = error(.loadFailureInvalidBannerSize) log(.loadFailed(error)) completion(.failure(error)) @@ -87,6 +88,12 @@ extension BidMachineAdapterBannerAd: BidMachineAdDelegate { return } log(.loadSucceeded) + var partnerDetails: [String: String] = [:] + if let loadedSize = fixedBannerSize(for: request.size ?? IABStandardAdSize) { + partnerDetails["bannerWidth"] = "\(loadedSize.width)" + partnerDetails["bannerHeight"] = "\(loadedSize.height)" + partnerDetails["bannerType"] = "0" // Fixed banner + } loadCompletion?(.success([:])) ?? log(.loadResultIgnored) loadCompletion = nil @@ -142,20 +149,9 @@ extension BidMachineAdapterBannerAd: BidMachineAdDelegate { } extension BidMachineApiCore.PlacementFormat { - static func from(size requestedSize: CGSize) -> BidMachineApiCore.PlacementFormat? { - let sizes = [IABLeaderboardAdSize, IABMediumAdSize, IABStandardAdSize] - // Find the largest size that can fit in the requested size. - var bestFit: CGSize? = nil - for size in sizes { - // If height is 0, the pub has requested an ad of any height, so only the width matters. - if requestedSize.width >= size.width && - (size.height == 0 || requestedSize.height >= size.height) { - bestFit = size - } - } - + static func from(size: CGSize?) -> BidMachineApiCore.PlacementFormat? { // Translate IAB size to a BidMachine placement format - switch bestFit { + switch size { case IABStandardAdSize: return .banner320x50 case IABMediumAdSize: @@ -163,8 +159,24 @@ extension BidMachineApiCore.PlacementFormat { case IABLeaderboardAdSize: return .banner728x90 default: - // The requested size cannot fit any fixed size banners. + // Not a standard IAB size return nil } } } + +extension BidMachineAdapterBannerAd { + private func fixedBannerSize(for requestedSize: CGSize) -> CGSize? { + let sizes = [IABLeaderboardAdSize, IABMediumAdSize, IABStandardAdSize] + // Find the largest size that can fit in the requested size. + for size in sizes { + // If height is 0, the pub has requested an ad of any height, so only the width matters. + if requestedSize.width >= size.width && + (size.height == 0 || requestedSize.height >= size.height) { + return size + } + } + // The requested size cannot fit any fixed size banners. + return nil + } +} From e9842b13f3b41404873b6aa7bede38ddd9c8b2b5 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Fri, 27 Oct 2023 12:12:14 -0700 Subject: [PATCH 15/23] Remove 'show' logs from banner ad --- Source/BidMachineAdapterBannerAd.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Source/BidMachineAdapterBannerAd.swift b/Source/BidMachineAdapterBannerAd.swift index 83401b7..905ffcb 100644 --- a/Source/BidMachineAdapterBannerAd.swift +++ b/Source/BidMachineAdapterBannerAd.swift @@ -83,8 +83,6 @@ extension BidMachineAdapterBannerAd: BidMachineAdDelegate { log(.loadFailed(loadError)) loadCompletion?(.failure(loadError)) ?? log(.loadResultIgnored) loadCompletion = nil - let showError = error(.showFailureAdNotReady) - log(.showFailed(showError)) return } log(.loadSucceeded) @@ -107,13 +105,11 @@ extension BidMachineAdapterBannerAd: BidMachineAdDelegate { } func didPresentAd(_ ad: BidMachineAdProtocol) { - log(.showSucceeded) - showCompletion?(.success([:])) ?? log(.showResultIgnored) + log(.delegateCallIgnored) } func didFailPresentAd(_ ad: BidMachineAdProtocol, _ error: Error) { - log(.showFailed(error)) - showCompletion?(.failure(error)) ?? log(.showResultIgnored) + log(.delegateCallIgnored) } func didDismissAd(_ ad: BidMachineAdProtocol) { From 634deedee0807c85ecf0f2433b0394a1f45f60a0 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Fri, 27 Oct 2023 16:11:59 -0700 Subject: [PATCH 16/23] Update smoke-test.yml --- .github/workflows/smoke-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml index 764108c..ebbdd32 100644 --- a/.github/workflows/smoke-test.yml +++ b/.github/workflows/smoke-test.yml @@ -12,4 +12,4 @@ jobs: validate-podspec: runs-on: macos-latest steps: - - uses: chartboost/chartboost-mediation-ios-actions/adapter-smoke-test@v1 + - uses: chartboost/chartboost-ios-adapter-actions/adapter-smoke-test@v1 From 608b68f00f11660e7608305a9f7fdcbe8d8d4fa3 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Fri, 27 Oct 2023 16:15:06 -0700 Subject: [PATCH 17/23] Change copyright year --- Source/BidMachineAdapter.swift | 2 +- Source/BidMachineAdapterAd.swift | 2 +- Source/BidMachineAdapterBannerAd.swift | 2 +- Source/BidMachineAdapterConfig.swift | 2 +- Source/BidMachineAdapterInterstitialAd.swift | 2 +- Source/BidMachineAdapterRewardedAd.swift | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift index 809d658..22e4c80 100644 --- a/Source/BidMachineAdapter.swift +++ b/Source/BidMachineAdapter.swift @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Chartboost, Inc. +// Copyright 2023-2023 Chartboost, Inc. // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. diff --git a/Source/BidMachineAdapterAd.swift b/Source/BidMachineAdapterAd.swift index 75a3aff..6963ddd 100644 --- a/Source/BidMachineAdapterAd.swift +++ b/Source/BidMachineAdapterAd.swift @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Chartboost, Inc. +// Copyright 2023-2023 Chartboost, Inc. // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. diff --git a/Source/BidMachineAdapterBannerAd.swift b/Source/BidMachineAdapterBannerAd.swift index 905ffcb..dbcdab2 100644 --- a/Source/BidMachineAdapterBannerAd.swift +++ b/Source/BidMachineAdapterBannerAd.swift @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Chartboost, Inc. +// Copyright 2023-2023 Chartboost, Inc. // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. diff --git a/Source/BidMachineAdapterConfig.swift b/Source/BidMachineAdapterConfig.swift index 61d61da..675a22c 100644 --- a/Source/BidMachineAdapterConfig.swift +++ b/Source/BidMachineAdapterConfig.swift @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Chartboost, Inc. +// Copyright 2023-2023 Chartboost, Inc. // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. diff --git a/Source/BidMachineAdapterInterstitialAd.swift b/Source/BidMachineAdapterInterstitialAd.swift index cb17b9c..938ee06 100644 --- a/Source/BidMachineAdapterInterstitialAd.swift +++ b/Source/BidMachineAdapterInterstitialAd.swift @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Chartboost, Inc. +// Copyright 2023-2023 Chartboost, Inc. // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. diff --git a/Source/BidMachineAdapterRewardedAd.swift b/Source/BidMachineAdapterRewardedAd.swift index 3c42efb..6ed236f 100644 --- a/Source/BidMachineAdapterRewardedAd.swift +++ b/Source/BidMachineAdapterRewardedAd.swift @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Chartboost, Inc. +// Copyright 2023-2023 Chartboost, Inc. // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. From 0ad1281121a62821f18ad87928da185acd0c67cf Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Fri, 27 Oct 2023 16:48:18 -0700 Subject: [PATCH 18/23] Explicit self to silence warning --- Source/BidMachineAdapterBannerAd.swift | 4 ++-- Source/BidMachineAdapterInterstitialAd.swift | 4 ++-- Source/BidMachineAdapterRewardedAd.swift | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/BidMachineAdapterBannerAd.swift b/Source/BidMachineAdapterBannerAd.swift index dbcdab2..e21e21b 100644 --- a/Source/BidMachineAdapterBannerAd.swift +++ b/Source/BidMachineAdapterBannerAd.swift @@ -33,7 +33,7 @@ final class BidMachineAdapterBannerAd: BidMachineAdapterAd, PartnerAd { config = try BidMachineSdk.shared.requestConfiguration(bannerType) } catch { let chartboostMediationError = self.error(.loadFailureUnknown, error: error) - log(.loadFailed(chartboostMediationError)) + self.log(.loadFailed(chartboostMediationError)) completion(.failure(chartboostMediationError)) return } @@ -53,7 +53,7 @@ final class BidMachineAdapterBannerAd: BidMachineAdapterAd, PartnerAd { } guard let ad else { let chartboostMediationError = self.error(.loadFailureUnknown, error: error) - log(.loadFailed(chartboostMediationError)) + self.log(.loadFailed(chartboostMediationError)) completion(.failure(chartboostMediationError)) return } diff --git a/Source/BidMachineAdapterInterstitialAd.swift b/Source/BidMachineAdapterInterstitialAd.swift index 938ee06..6a1cc25 100644 --- a/Source/BidMachineAdapterInterstitialAd.swift +++ b/Source/BidMachineAdapterInterstitialAd.swift @@ -24,7 +24,7 @@ final class BidMachineAdapterInterstitialAd: BidMachineAdapterAd, PartnerAd { config = try BidMachineSdk.shared.requestConfiguration(.interstitial) } catch { let chartboostMediationError = self.error(.loadFailureUnknown, error: error) - log(.loadFailed(chartboostMediationError)) + self.log(.loadFailed(chartboostMediationError)) completion(.failure(chartboostMediationError)) return } @@ -44,7 +44,7 @@ final class BidMachineAdapterInterstitialAd: BidMachineAdapterAd, PartnerAd { } guard let ad = ad else { let chartboostMediationError = self.error(.loadFailureUnknown, error: error) - log(.loadFailed(chartboostMediationError)) + self.log(.loadFailed(chartboostMediationError)) completion(.failure(chartboostMediationError)) return } diff --git a/Source/BidMachineAdapterRewardedAd.swift b/Source/BidMachineAdapterRewardedAd.swift index 6ed236f..448d504 100644 --- a/Source/BidMachineAdapterRewardedAd.swift +++ b/Source/BidMachineAdapterRewardedAd.swift @@ -24,7 +24,7 @@ final class BidMachineAdapterRewardedAd: BidMachineAdapterAd, PartnerAd { config = try BidMachineSdk.shared.requestConfiguration(.rewarded) } catch { let chartboostMediationError = self.error(.loadFailureUnknown, error: error) - log(.loadFailed(chartboostMediationError)) + self.log(.loadFailed(chartboostMediationError)) completion(.failure(chartboostMediationError)) return } @@ -44,7 +44,7 @@ final class BidMachineAdapterRewardedAd: BidMachineAdapterAd, PartnerAd { } guard let ad = ad else { let chartboostMediationError = self.error(.loadFailureUnknown, error: error) - log(.loadFailed(chartboostMediationError)) + self.log(.loadFailed(chartboostMediationError)) completion(.failure(chartboostMediationError)) return } From 2e025c5f908a16ee99bff08c7732a277487b3a57 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Mon, 30 Oct 2023 12:24:46 -0700 Subject: [PATCH 19/23] Info doesn't need to be populated pre-init --- Source/BidMachineAdapter.swift | 49 ---------------------------------- 1 file changed, 49 deletions(-) diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift index 22e4c80..3869842 100644 --- a/Source/BidMachineAdapter.swift +++ b/Source/BidMachineAdapter.swift @@ -9,33 +9,8 @@ import UIKit import BidMachine final class BidMachineAdapter: PartnerAdapter { - private let COPPA_KEY = "com.chartboost.adapter.bidmachine.coppa" - private let GDPR_ZONE_KEY = "com.chartboost.adapter.bidmachine.gdprzone" - private let GDPR_CONSENT_KEY = "com.chartboost.adapter.bidmachine.gdprconsent" - private let US_PRIVACY_STRING_KEY = "com.chartboost.adapter.bidmachine.usprivacystring" - private let SOURCE_ID_KEY = "source_id" - private var coppa: Bool? { - get { UserDefaults.standard.value(forKey: COPPA_KEY) as? Bool } - set { UserDefaults.standard.setValue(newValue, forKey: COPPA_KEY) } - } - - private var gdprZone: Bool? { - get { UserDefaults.standard.value(forKey: GDPR_ZONE_KEY) as? Bool } - set { UserDefaults.standard.setValue(newValue, forKey: GDPR_ZONE_KEY) } - } - - private var gdprConsent: Bool? { - get { UserDefaults.standard.value(forKey: GDPR_CONSENT_KEY) as? Bool } - set { UserDefaults.standard.setValue(newValue, forKey: GDPR_CONSENT_KEY) } - } - - private var usPrivacyString: String? { - get { UserDefaults.standard.string(forKey: US_PRIVACY_STRING_KEY) } - set { UserDefaults.standard.setValue(newValue, forKey: US_PRIVACY_STRING_KEY) } - } - /// The version of the partner SDK. let partnerSDKVersion = BidMachineSdk.sdkVersion @@ -67,20 +42,6 @@ final class BidMachineAdapter: PartnerAdapter { func setUp(with configuration: PartnerConfiguration, completion: @escaping (Error?) -> Void) { log(.setUpStarted) - // Populate only the info we have non-nil saved values for - if let coppa = coppa { - BidMachineSdk.shared.regulationInfo.populate { $0.withCOPPA(coppa) } - } - if let gdprZone = gdprZone { - BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRZone(gdprZone) } - } - if let gdprConsent = gdprConsent { - BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRConsent(gdprConsent) } - } - if let usPrivacyString = usPrivacyString { - BidMachineSdk.shared.regulationInfo.populate { $0.withUSPrivacyString(usPrivacyString) } - } - // These settings all have default values BidMachineSdk.shared.populate { $0.withTestMode(BidMachineAdapterConfiguration.testMode) .withLoggingMode(BidMachineAdapterConfiguration.logging) @@ -126,22 +87,16 @@ final class BidMachineAdapter: PartnerAdapter { /// - parameter status: One of the `GDPRConsentStatus` values depending on the user's preference. func setGDPR(applies: Bool?, status: GDPRConsentStatus) { if let applies = applies { - gdprZone = applies log(.privacyUpdated(setting: "gdprZone", value: applies)) - // It is unknown whether populating this value after initializing the BidMachine SDK does anything BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRZone(applies) } } // In the case where status == .unknown, we do nothing if status == .denied { - gdprConsent = false log(.privacyUpdated(setting: "gdprConsent", value: false)) - // It is unknown whether populating this value after initializing the BidMachine SDK does anything BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRConsent(false) } } else if status == .granted { - gdprConsent = true log(.privacyUpdated(setting: "gdprConsent", value: true)) - // It is unknown whether populating this value after initializing the BidMachine SDK does anything BidMachineSdk.shared.regulationInfo.populate { $0.withGDPRConsent(true) } } } @@ -150,18 +105,14 @@ final class BidMachineAdapter: PartnerAdapter { /// - parameter hasGivenConsent: A boolean indicating if the user has given consent. /// - parameter privacyString: An IAB-compliant string indicating the CCPA status. func setCCPA(hasGivenConsent: Bool, privacyString: String) { - usPrivacyString = privacyString log(.privacyUpdated(setting: "usPrivacyString", value: privacyString)) - // It is unknown whether populating this value after initializing the BidMachine SDK does anything BidMachineSdk.shared.regulationInfo.populate { $0.withUSPrivacyString(privacyString) } } /// Indicates if the user is subject to COPPA or not. /// - parameter isChildDirected: `true` if the user is subject to COPPA, `false` otherwise. func setCOPPA(isChildDirected: Bool) { - coppa = isChildDirected log(.privacyUpdated(setting: "COPPA", value: isChildDirected)) - // It is unknown whether populating this value after initializing the BidMachine SDK does anything BidMachineSdk.shared.regulationInfo.populate { $0.withCOPPA(isChildDirected) } } From 9dd98213c1dde6ff0355320aa7a7a60871d2ba18 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Wed, 1 Nov 2023 11:37:17 -0700 Subject: [PATCH 20/23] cleanup --- Source/BidMachineAdapterBannerAd.swift | 7 ++----- Source/BidMachineAdapterConfig.swift | 2 -- Source/BidMachineAdapterInterstitialAd.swift | 5 ++--- Source/BidMachineAdapterRewardedAd.swift | 5 ++--- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/Source/BidMachineAdapterBannerAd.swift b/Source/BidMachineAdapterBannerAd.swift index e21e21b..9061107 100644 --- a/Source/BidMachineAdapterBannerAd.swift +++ b/Source/BidMachineAdapterBannerAd.swift @@ -32,9 +32,8 @@ final class BidMachineAdapterBannerAd: BidMachineAdapterAd, PartnerAd { do { config = try BidMachineSdk.shared.requestConfiguration(bannerType) } catch { - let chartboostMediationError = self.error(.loadFailureUnknown, error: error) - self.log(.loadFailed(chartboostMediationError)) - completion(.failure(chartboostMediationError)) + self.log(.loadFailed(error)) + completion(.failure(error)) return } @@ -94,8 +93,6 @@ extension BidMachineAdapterBannerAd: BidMachineAdDelegate { } loadCompletion?(.success([:])) ?? log(.loadResultIgnored) loadCompletion = nil - - log(.showStarted) } func didFailLoadAd(_ ad: BidMachine.BidMachineAdProtocol, _ error: Error) { diff --git a/Source/BidMachineAdapterConfig.swift b/Source/BidMachineAdapterConfig.swift index 675a22c..9eb4fbc 100644 --- a/Source/BidMachineAdapterConfig.swift +++ b/Source/BidMachineAdapterConfig.swift @@ -23,6 +23,4 @@ import Foundation /// Init flag for turning on BidMachine SDK event logging. /// Default value is 'false'. @objc public static var eventLogging: Bool = false - - } diff --git a/Source/BidMachineAdapterInterstitialAd.swift b/Source/BidMachineAdapterInterstitialAd.swift index 6a1cc25..e4fb3d7 100644 --- a/Source/BidMachineAdapterInterstitialAd.swift +++ b/Source/BidMachineAdapterInterstitialAd.swift @@ -23,9 +23,8 @@ final class BidMachineAdapterInterstitialAd: BidMachineAdapterAd, PartnerAd { do { config = try BidMachineSdk.shared.requestConfiguration(.interstitial) } catch { - let chartboostMediationError = self.error(.loadFailureUnknown, error: error) - self.log(.loadFailed(chartboostMediationError)) - completion(.failure(chartboostMediationError)) + self.log(.loadFailed(error)) + completion(.failure(error)) return } diff --git a/Source/BidMachineAdapterRewardedAd.swift b/Source/BidMachineAdapterRewardedAd.swift index 448d504..8235b84 100644 --- a/Source/BidMachineAdapterRewardedAd.swift +++ b/Source/BidMachineAdapterRewardedAd.swift @@ -23,9 +23,8 @@ final class BidMachineAdapterRewardedAd: BidMachineAdapterAd, PartnerAd { do { config = try BidMachineSdk.shared.requestConfiguration(.rewarded) } catch { - let chartboostMediationError = self.error(.loadFailureUnknown, error: error) - self.log(.loadFailed(chartboostMediationError)) - completion(.failure(chartboostMediationError)) + self.log(.loadFailed(error)) + completion(.failure(error)) return } From 3e5b1ca8567160bcffafc9bc8ed313c9166f7374 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Wed, 6 Dec 2023 15:16:14 -0800 Subject: [PATCH 21/23] 'support' rewarded_interstitial --- Source/BidMachineAdapter.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift index 3869842..7642b47 100644 --- a/Source/BidMachineAdapter.swift +++ b/Source/BidMachineAdapter.swift @@ -145,6 +145,8 @@ final class BidMachineAdapter: PartnerAdapter { // Not using the `.adaptiveBanner` case directly to maintain backward compatibility with Chartboost Mediation 4.0 if request.format.rawValue == "adaptive_banner" { return BidMachineAdapterBannerAd(adapter: self, request: request, delegate: delegate) + } else if request.format.rawValue == "rewarded_interstitial" { + return BidMachineAdapterRewardedAd(adapter: self, request: request, delegate: delegate) } else { throw error(.loadFailureUnsupportedAdFormat) } From 78752eeb9f9e8fd92079731c03db7760d7612b27 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Wed, 6 Dec 2023 16:08:18 -0800 Subject: [PATCH 22/23] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b4ccd1..8e81467 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,5 +3,5 @@ Note the first digit of every adapter version corresponds to the major version of the Chartboost Mediation SDK compatible with that adapter. Adapters are compatible with any Chartboost Mediation SDK version within that major version. -### {Adapter Version} -- Say something clever. \ No newline at end of file +### 4.2.3.0.0 +- This version of the adapter has been certified with BidMachine 2.3.0. From fa3a092df1743894c939b4dfcd9bd07d0133d1c0 Mon Sep 17 00:00:00 2001 From: Alex Rice Date: Thu, 7 Dec 2023 10:56:12 -0800 Subject: [PATCH 23/23] Use BidMachine 4.2.0.0 --- CHANGELOG.md | 4 ++-- ChartboostMediationAdapterBidMachine.podspec | 6 +++--- Source/BidMachineAdapter.swift | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e81467..d0544ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,5 +3,5 @@ Note the first digit of every adapter version corresponds to the major version of the Chartboost Mediation SDK compatible with that adapter. Adapters are compatible with any Chartboost Mediation SDK version within that major version. -### 4.2.3.0.0 -- This version of the adapter has been certified with BidMachine 2.3.0. +### 4.2.4.0.0.0 +- This version of the adapter has been certified with BidMachine 2.4.0.0. diff --git a/ChartboostMediationAdapterBidMachine.podspec b/ChartboostMediationAdapterBidMachine.podspec index 7fdc45d..cd3fe15 100644 --- a/ChartboostMediationAdapterBidMachine.podspec +++ b/ChartboostMediationAdapterBidMachine.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'ChartboostMediationAdapterBidMachine' - spec.version = '4.2.3.0.0' + spec.version = '4.2.4.0.0.0' spec.license = { :type => 'MIT', :file => 'LICENSE.md' } spec.homepage = 'https://github.com/ChartBoost/chartboost-mediation-ios-adapter-bidmachine' spec.authors = { 'Chartboost' => 'https://www.chartboost.com/' } @@ -13,7 +13,7 @@ Pod::Spec.new do |spec| spec.source_files = 'Source/**/*.{swift}' # Minimum supported versions - spec.swift_version = '5.0' + spec.swift_version = '5.1' spec.ios.deployment_target = '12.0' # System frameworks used @@ -23,7 +23,7 @@ Pod::Spec.new do |spec| spec.dependency 'ChartboostMediationSDK', '~> 4.0' # Partner network SDK and version that this adapter is certified to work with. - spec.dependency 'BidMachine', '2.3.0' + spec.dependency 'BidMachine', '~> 2.4.0.0' # Indicates, that if use_frameworks! is specified, the pod should include a static library framework. spec.static_framework = true diff --git a/Source/BidMachineAdapter.swift b/Source/BidMachineAdapter.swift index 7642b47..5b6be7a 100644 --- a/Source/BidMachineAdapter.swift +++ b/Source/BidMachineAdapter.swift @@ -17,7 +17,7 @@ final class BidMachineAdapter: PartnerAdapter { /// The version of the adapter. /// It should have either 5 or 6 digits separated by periods, where the first digit is Chartboost Mediation SDK's major version, the last digit is the adapter's build version, and intermediate digits are the partner SDK's version. /// Format: `.....` where `.` is optional. - let adapterVersion = "4.2.3.0.0" + let adapterVersion = "4.2.4.0.0.0" /// The partner's unique identifier. let partnerIdentifier = "bidmachine"