Trusted Arbiter 会比较已加载的 CloudX 出价和受支持的第三方出价,并返回被选中的平台。CloudX iOS SDK 3.4.0 支持 CloudX、Unity LevelPlay 和 PubMatic 出价输入。
请在 CloudX 初始化完成后再调用 CloudXCore.shared.arbiter(with:completion:)。在仲裁服务可用之前,SDK 会在传入的出价中回退选择可比较美元出价最高的平台。
基础 API
从已加载的广告创建出价候选项,然后传给仲裁器。
// cloudXAd 是 CloudX 加载回调中的 CLXAd 对象。
// levelPlayAdInfo 是 Unity LevelPlay 的广告信息对象。
// pubMaticPrice 和 pubMaticPartnerName 来自 PubMatic/OpenWrap 出价对象。
CLXArbiterBid *cloudXBid = [CLXArbiterBid cloudXBidWithAd:cloudXAd];
CLXArbiterBid *levelPlayBid =
[CLXArbiterBid levelPlayBidWithNetworkName:levelPlayAdInfo.adNetwork
revenue:levelPlayAdInfo.revenue.doubleValue
precision:levelPlayAdInfo.precision];
CLXArbiterBid *pubMaticBid =
[CLXArbiterBid pubMaticBidWithPrice:pubMaticPrice
partnerName:pubMaticPartnerName
extras:nil];
CLXArbiterConfiguration *configuration =
[CLXArbiterConfiguration configurationWithBids:@[cloudXBid, levelPlayBid, pubMaticBid]];
[[CloudXCore shared] arbiterWithConfiguration:configuration completion:^(CLXArbiterResult *result) {
NSLog(@"Selected platform: %@", result.platform.name);
}];
CLXArbiterBid.cloudX 接收 CloudX 加载回调中的 CLXAd 对象。CLXArbiterBid.levelPlay 接收 Unity LevelPlay 广告信息值。CLXArbiterBid.pubMatic 接收 PubMatic OpenWrap 出价价格和可选的合作伙伴名称。extras 映射在 LevelPlay 和 PubMatic 出价中均为可选参数,partnerName 在 PubMatic 出价中为可选参数。完成回调在主线程上执行,因此你可以直接在其中展示广告或更新 UI。
result.platform 在选中平台时为 CLXArbiterPlatform.cloudX、levelPlay 或 pubMatic;当无法选出获胜平台时(例如未传入任何出价),则为 CLXArbiterPlatform.none。
分步示例:在 CloudX 与 LevelPlay 之间仲裁
本演练展示了应读取哪个 Unity LevelPlay 回调,以及应将哪些值传入仲裁器。示例使用插屏广告,但相同的字段映射适用于任何广告格式。
加载两个候选项
创建 CloudX 和 LevelPlay 插屏广告,设置各自的 delegate,并为每个平台启动加载。self.cloudXInterstitial = [[CloudXCore shared] createInterstitialWithAdUnitId:@"YOUR_CLOUDX_AD_UNIT_ID"];
self.cloudXInterstitial.delegate = self;
[self.cloudXInterstitial load];
self.levelPlayInterstitial = [[LPMInterstitialAd alloc] initWithAdUnitId:@"YOUR_LEVELPLAY_AD_UNIT_ID"];
self.levelPlayInterstitial.delegate = self;
[self.levelPlayInterstitial loadAd];
保存每个平台已加载的广告
LevelPlay 在其加载回调中提供 LPMAdInfo;CloudX 提供 CLXAd。请保存两者,下一步会从中读取仲裁输入。// 属性:@property (nonatomic, strong) CLXAd *cloudXAd;
// @property (nonatomic, strong) LPMAdInfo *levelPlayInfo;
// CLXInterstitialDelegate
- (void)didLoadAd:(CLXAd *)ad {
self.cloudXAd = ad;
}
// LPMInterstitialAdDelegate
- (void)didLoadAdWithAdInfo:(LPMAdInfo *)adInfo {
self.levelPlayInfo = adInfo;
}
将值映射为出价
从 LPMAdInfo 读取 LevelPlay 字段,并传给 CLXArbiterBid.levelPlay。CloudX 出价直接接收 CLXAd。只提交实际加载成功的平台。LPMAdInfo 字段 | 类型 | CLXArbiterBid.levelPlay 参数 |
|---|
adNetwork | NSString * | networkName |
revenue | NSNumber * | revenue(用 .doubleValue 解包) |
precision | NSString * | precision |
NSMutableArray<CLXArbiterBid *> *bids = [NSMutableArray array];
if (self.cloudXAd) {
[bids addObject:[CLXArbiterBid cloudXBidWithAd:self.cloudXAd]];
}
if (self.levelPlayInfo) {
CLXArbiterBid *levelPlayBid =
[CLXArbiterBid levelPlayBidWithNetworkName:self.levelPlayInfo.adNetwork
revenue:self.levelPlayInfo.revenue.doubleValue
precision:self.levelPlayInfo.precision];
[bids addObject:levelPlayBid];
}
CLXArbiterConfiguration *configuration =
[CLXArbiterConfiguration configurationWithBids:bids];
运行仲裁器
将 configuration 连同完成回调传给仲裁器。请在两个插屏广告都加载完成(或加载失败)后再运行:跟踪各自的加载回调和加载失败,并且只提交成功加载的候选项。完成回调在主线程上执行。[[CloudXCore shared] arbiterWithConfiguration:configuration completion:^(CLXArbiterResult *result) {
[self showWinner:result];
}];
展示获胜平台
将 result.platform.name 与平台常量比较,展示获胜平台的广告。CLXArbiterPlatform.none 表示未选出获胜平台,此时不展示广告,继续应用流程。- (void)showWinner:(CLXArbiterResult *)result {
NSString *platform = result.platform.name;
if ([platform isEqualToString:CLXArbiterPlatform.cloudX.name]) {
[self.cloudXInterstitial showFromViewController:self];
} else if ([platform isEqualToString:CLXArbiterPlatform.levelPlay.name]) {
[self.levelPlayInterstitial showAdWithViewController:self placementName:nil];
}
// CLXArbiterPlatform.none: 无获胜平台;不展示广告
}
LPMAdInfo.revenue 是 NSNumber 且可能缺失,因此在传给非空的 revenue: 参数之前请将其解包为 double:Objective-C 中使用 revenue.doubleValue,Swift 中使用 revenue?.doubleValue ?? 0。
下方的 ArbiterInterstitialController 将上述步骤封装为一个可复用的组件,会在到达广告位之前提前准备好获胜平台。
插屏广告示例
这个插屏广告示例会在两个平台之间仲裁:CloudX 和 Unity LevelPlay。在到达广告位之前先准备好获胜平台:
- 并行加载 CloudX 和 LevelPlay。
- 等待两个平台都加载完成或加载失败。
- 只将已加载的候选项提交给 Trusted Arbiter。
- 缓存选中的平台。
- 到达广告位时,立即展示缓存的获胜广告。
如果两个平台都加载失败,开始新的加载周期。如果到达广告位时还没有准备好获胜平台,则继续应用流程,不展示广告。
ArbiterInterstitialController.swift
/// 提前准备好 Trusted Arbiter 的获胜平台,以便在到达广告位时立即展示插屏广告。
///
/// 并行加载 CloudX 和 LevelPlay 插屏广告,等待两者都加载完成或加载失败,
/// 将已加载的候选项提交给 CloudXCore.shared.arbiter,并将选中的
/// CLXArbiterPlatform 缓存到 nextWinner 中。
final class ArbiterInterstitialController: NSObject {
protocol Listener: AnyObject {
/// 当仲裁器为下一次展示选出平台后调用。
func arbiterInterstitialController(
_ controller: ArbiterInterstitialController,
didPrepareWinner platform: CLXArbiterPlatform
)
}
weak var listener: Listener?
private let cloudXInterstitial: CLXInterstitial
private let levelPlayInterstitial: LPMInterstitialAd
private var cloudXAd: CLXAd?
private var cloudXLoadDone = false
private var levelPlayAdInfo: LPMAdInfo?
private var levelPlayLoadDone = false
private var nextWinner: CLXArbiterPlatform?
init(cloudXInterstitial: CLXInterstitial, levelPlayInterstitial: LPMInterstitialAd) {
self.cloudXInterstitial = cloudXInterstitial
self.levelPlayInterstitial = levelPlayInterstitial
super.init()
self.cloudXInterstitial.delegate = self
self.levelPlayInterstitial.setDelegate(self)
}
/// 为每个当前没有缓存广告的平台启动加载。
func loadMissingAds() {
if cloudXAd == nil { cloudXInterstitial.load() }
if levelPlayAdInfo == nil { levelPlayInterstitial.loadAd() }
}
/// 展示已准备好的获胜广告,仅当确实发起了展示调用时返回 true。
///
/// 当没有准备好获胜平台或缓存的广告已不可用时返回 false,
/// 此时会启动一次新的加载周期。
func showAtPlacement(from viewController: UIViewController, placementName: String? = nil) -> Bool {
guard let platformName = nextWinner?.name else { return false }
if platformName == CLXArbiterPlatform.cloudX.name {
return showCloudX(from: viewController, placementName: placementName)
}
if platformName == CLXArbiterPlatform.levelPlay.name {
return showLevelPlay(from: viewController, placementName: placementName)
}
return false
}
/// 在两个平台都完成后运行仲裁器,然后缓存获胜平台。
///
/// 在两个加载都完成之前提前返回。如果两个平台都没有加载成功,则重启加载周期;
/// 否则将已加载的候选项提交给 CloudXCore.shared.arbiter。
private func maybePrepareWinner() {
guard cloudXLoadDone, levelPlayLoadDone else { return }
if cloudXAd == nil && levelPlayAdInfo == nil {
cloudXLoadDone = false
levelPlayLoadDone = false
loadMissingAds()
return
}
var bids: [CLXArbiterBid] = []
if let cloudXAd {
bids.append(CLXArbiterBid.cloudX(ad: cloudXAd))
}
if let levelPlayAdInfo {
bids.append(CLXArbiterBid.levelPlay(
networkName: levelPlayAdInfo.adNetwork,
revenue: levelPlayAdInfo.revenue?.doubleValue ?? 0,
precision: levelPlayAdInfo.precision
))
}
let configuration = CLXArbiterConfiguration.configuration(bids: bids, builderBlock: nil)
CloudXCore.shared.arbiter(with: configuration) { [weak self] result in
guard let self else { return }
nextWinner = result.platform
listener?.arbiterInterstitialController(self, didPrepareWinner: result.platform)
}
}
private func showCloudX(from viewController: UIViewController, placementName: String?) -> Bool {
if cloudXInterstitial.isReady {
if let placementName {
cloudXInterstitial.show(from: viewController, placement: placementName, customData: nil)
} else {
cloudXInterstitial.show(from: viewController)
}
return true
}
clearCloudXAndLoadMissingAds()
return false
}
private func showLevelPlay(from viewController: UIViewController, placementName: String?) -> Bool {
if levelPlayInterstitial.isAdReady() {
levelPlayInterstitial.showAd(viewController: viewController, placementName: placementName)
return true
}
clearLevelPlayAndLoadMissingAds()
return false
}
private func clearCloudXAndLoadMissingAds() {
cloudXAd = nil
cloudXLoadDone = false
nextWinner = nil
loadMissingAds()
}
private func clearLevelPlayAndLoadMissingAds() {
levelPlayAdInfo = nil
levelPlayLoadDone = false
nextWinner = nil
loadMissingAds()
}
}
extension ArbiterInterstitialController: CLXInterstitialDelegate {
func didLoad(_ ad: CLXAd) {
cloudXAd = ad
cloudXLoadDone = true
maybePrepareWinner()
}
func didFailToLoadAd(_ adUnitId: String, error: CLXError) {
cloudXAd = nil
cloudXLoadDone = true
maybePrepareWinner()
}
func didDisplay(_ ad: CLXAd) {}
func didFailToDisplay(_ ad: CLXAd, error: CLXError) {
clearCloudXAndLoadMissingAds()
}
func didHide(_ ad: CLXAd) {
clearCloudXAndLoadMissingAds()
}
func didClick(_ ad: CLXAd) {}
}
extension ArbiterInterstitialController: LPMInterstitialAdDelegate {
func didLoadAd(with adInfo: LPMAdInfo) {
levelPlayAdInfo = adInfo
levelPlayLoadDone = true
maybePrepareWinner()
}
func didFailToLoadAd(withAdUnitId adUnitId: String, error: Error) {
levelPlayAdInfo = nil
levelPlayLoadDone = true
maybePrepareWinner()
}
func didChangeAdInfo(_ adInfo: LPMAdInfo) {
levelPlayAdInfo = adInfo
}
func didDisplayAd(with adInfo: LPMAdInfo) {}
func didFailToDisplayAd(with adInfo: LPMAdInfo, error: Error) {
clearLevelPlayAndLoadMissingAds()
}
func didCloseAd(with adInfo: LPMAdInfo) {
clearLevelPlayAndLoadMissingAds()
}
func didClickAd(with adInfo: LPMAdInfo) {}
}
showAtPlacement(from:placementName:) 仅当确实发起了展示调用时返回 true。didChangeAdInfo(_:) 会在 LevelPlay 广告保持加载状态期间更新缓存的 LevelPlay 候选项值。
对于 PubMatic OpenWrap,使用 CLXArbiterBid.pubMatic(price:partnerName:extras:) 创建第三方出价。如果仲裁服务不可用,SDK 会在传入的受支持出价输入中回退选择可比较美元出价最高的平台。