> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cloudx.io/llms.txt
> Use this file to discover all available pages before exploring further.

# First Look

> Try CloudX first while keeping an existing iOS mediation placement as fallback

First Look gives CloudX the first chance to fill a placement while preserving your existing mediation setup as the fallback path. Start with one placement, verify load and show behavior, then expand to more placements.

This example uses an AdMob interstitial fallback. The same pattern applies to other fallback mediators: load CloudX first, load the fallback only if CloudX does not load, and keep the app flow moving when neither source is ready.

<Info>
  Initialize CloudX and Google Mobile Ads before creating the controller. Keep your existing pacing, retry, and placement timing rules around this controller.
</Info>

```swift FirstLookInterstitialController.swift theme={null}
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)
    }
}
```

Use `show()` at the placement moment. If it returns `false`, neither CloudX nor the fallback had a ready ad, so continue the app flow without showing an ad.

```swift theme={null}
if firstLookInterstitial?.show() != true {
    continueToNextScreen()
}
```

After a show, close, or terminal failure, call `load()` again when your app is ready to prepare the next placement opportunity.

<Tip>
  This sample keeps the fallback lazy: AdMob loads only after CloudX fails to load. Do not parallel-load both sources unless CloudX recommends a different rollout strategy for your app.
</Tip>
