First Look
让 CloudX 优先填充广告位,然后回退到您现有的聚合设置
First Look 让 CloudX 优先获得广告位填充机会,同时保留您现有的聚合设置作为回退路径。建议先从一个广告位开始,验证加载和展示行为后,再扩展到更多广告位。
此示例使用 AdMob 插屏广告作为回退。相同模式也适用于其他回退聚合平台:先加载 CloudX;只有当 CloudX 未加载成功时才加载回退广告;当两个来源都未准备好时,让应用流程继续。
import CloudXCore
import GoogleMobileAds
import UIKit
@MainActor
final class FirstLookInterstitialController: NSObject {
enum Source {
case cloudX
case adMob
}
protocol Listener: AnyObject {
func firstLookInterstitialController(_ controller: FirstLookInterstitialController, didLoad source: Source)
func firstLookInterstitialController(_ controller: FirstLookInterstitialController, didFailToLoad source: Source, message: String?)
func firstLookInterstitialController(_ controller: FirstLookInterstitialController, didShow source: Source)
func firstLookInterstitialController(_ controller: FirstLookInterstitialController, didFailToShow source: Source, message: String?)
func firstLookInterstitialController(_ controller: FirstLookInterstitialController, didClose source: Source)
func firstLookInterstitialController(_ controller: FirstLookInterstitialController, didClick source: Source)
}
weak var listener: Listener?
private weak var viewController: UIViewController?
private let cloudXInterstitial: CLXInterstitial
private let adMobAdUnitId: String
private var adMobInterstitial: InterstitialAd?
private var isLoadingCloudX = false
private var isLoadingAdMob = false
init?(
viewController: UIViewController,
cloudXAdUnitId: String,
adMobAdUnitId: String
) {
guard let cloudXInterstitial = CloudXCore.shared.createInterstitial(adUnitId: cloudXAdUnitId) else {
return nil
}
self.viewController = viewController
self.cloudXInterstitial = cloudXInterstitial
self.adMobAdUnitId = adMobAdUnitId
super.init()
self.cloudXInterstitial.delegate = self
}
func load() {
guard !isLoadingCloudX,
!isLoadingAdMob,
!cloudXInterstitial.isReady,
adMobInterstitial == nil else {
return
}
isLoadingCloudX = true
cloudXInterstitial.load()
}
func show() -> Bool {
guard let viewController else {
return false
}
if cloudXInterstitial.isReady {
cloudXInterstitial.show(from: viewController)
return true
}
return showAdMobFallback(from: viewController)
}
func destroy() {
cloudXInterstitial.destroy()
adMobInterstitial = nil
listener = nil
}
private func loadAdMobFallback() {
guard !isLoadingAdMob, adMobInterstitial == nil else {
return
}
isLoadingAdMob = true
InterstitialAd.load(with: adMobAdUnitId, request: Request()) { [weak self] ad, error in
guard let self else { return }
self.isLoadingAdMob = false
if let error {
self.adMobInterstitial = nil
self.listener?.firstLookInterstitialController(self, didFailToLoad: .adMob, message: error.localizedDescription)
return
}
guard let ad else {
self.adMobInterstitial = nil
self.listener?.firstLookInterstitialController(self, didFailToLoad: .adMob, message: nil)
return
}
ad.fullScreenContentDelegate = self
self.adMobInterstitial = ad
self.listener?.firstLookInterstitialController(self, didLoad: .adMob)
}
}
private func showAdMobFallback(from viewController: UIViewController) -> Bool {
guard let adMobInterstitial else {
return false
}
adMobInterstitial.present(from: viewController)
return true
}
}
extension FirstLookInterstitialController: CLXInterstitialDelegate {
func didLoad(_ ad: CLXAd) {
isLoadingCloudX = false
listener?.firstLookInterstitialController(self, didLoad: .cloudX)
}
func didFailToLoadAd(_ adUnitId: String, error: CLXError) {
isLoadingCloudX = false
loadAdMobFallback()
}
func didDisplay(_ ad: CLXAd) {
listener?.firstLookInterstitialController(self, didShow: .cloudX)
}
func didFailToDisplay(_ ad: CLXAd, error: CLXError) {
if let viewController, showAdMobFallback(from: viewController) {
return
}
listener?.firstLookInterstitialController(self, didFailToShow: .cloudX, message: error.localizedDescription)
}
func didHide(_ ad: CLXAd) {
listener?.firstLookInterstitialController(self, didClose: .cloudX)
}
func didClick(_ ad: CLXAd) {
listener?.firstLookInterstitialController(self, didClick: .cloudX)
}
}
extension FirstLookInterstitialController: FullScreenContentDelegate {
func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
adMobInterstitial = nil
listener?.firstLookInterstitialController(self, didShow: .adMob)
}
func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
adMobInterstitial = nil
listener?.firstLookInterstitialController(self, didClose: .adMob)
}
func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
adMobInterstitial = nil
listener?.firstLookInterstitialController(self, didFailToShow: .adMob, message: error.localizedDescription)
}
func adDidRecordClick(_ ad: FullScreenPresentingAd) {
listener?.firstLookInterstitialController(self, didClick: .adMob)
}
}在广告位触发时调用 show()。如果返回 false,表示 CloudX 和回退广告都没有准备好,此时应继续应用流程,不展示广告。
if firstLookInterstitial?.show() != true {
continueToNextScreen()
}广告展示、关闭或最终失败后,请在您的应用准备好下一次广告位机会时再次调用 load()。
示例应用
CloudX iOS 示例应用在真机上运行本页的流程,提供 Swift 和 Objective-C 两个版本。它把流程拆成三个文件:控制器,以及每个 SDK 各一个来源;它的宿主界面会在加载或展示失败时以 2 到 60 秒的退避间隔重试。
FirstLookInterstitialController.swift
让 CloudX 先尝试加载。只有在 CloudX 失败或不可用时才加载 AdMob。
CloudXFirstLookSource.swift
流程中 CloudX 这一侧。
AdMobFirstLookSource.swift
懒加载的 AdMob 回退。加载好的广告只能展示一次,一小时后过期。
FirstLookViewController.swift
启动两个 SDK,带退避重试,并连接 Show 按钮。它只负责示例布局,请参考其中规则,不要复制该文件。
请把前三个文件一起复制。两个来源通过示例的 DemoAppLogger 记录日志,复制时请换成您自己的日志方式。Objective-C 版本位于 demo-app-objc/CloudXObjCRemotePods/Ads/FirstLook/。