I am having issues in my SwiftUi app when trying to show adMob ad. I was following instructions on official site and came up with this View, that should display ad.

import GoogleMobileAds
import SwiftUI


struct AdRewardedInterstitialView: View {

    private let coordinator =  RewardedInterstitialAdCoordinator()
    private let adViewControllerRepresentable  = AdViewControllerRepresentable()

    var adViewControllerRepresentableView: some View {
        adViewControllerRepresentable.frame(width: .zero, height: .zero)
    }


    var body: some View {
        VStack(spacing: 20) {
            Text("Interstitial Ad Example")
                .font(.largeTitle)
                .background(adViewControllerRepresentableView)
            Spacer()

            Button("Show Rewarded Interstitial Ad") {
                coordinator.showAd(from: adViewControllerRepresentable.viewController)
            }
            .font(.title2)

            Spacer()
        }
        .onAppear {
            coordinator.loadAd()
        }
    }
 }

Relevant classes are:

// allows us to integrate UIViewController into SwiftUI
private struct AdViewControllerRepresentable: UIViewControllerRepresentable {
    let viewController = UIViewController()

    func makeUIViewController(context: Context) -> some UIViewController {
        print("AdViewControllerRepresentable.makeUiViewController")
        return viewController
    }

    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
}

//responsible for loading ad, displaying ad, and handling events
private class RewardedInterstitialAdCoordinator: NSObject, GADFullScreenContentDelegate {

private var rewardedInterstitialAd: GADRewardedInterstitialAd?

private var adId: String? = ""


func loadAd() {
    GADRewardedInterstitialAd.load(withAdUnitID: ADMOB_TEST_REWARDED_INTERSTITIAL,
        request: GADRequest()) { ad, error in

        print("coordinator.loading rewardedInterstitialAd")

        if let error = error {
            return print("Failed to load rewarded interstitial ad with error: \(error.localizedDescription)")
        }

        self.rewardedInterstitialAd = ad
        self.rewardedInterstitialAd?.fullScreenContentDelegate = self
        
        self.adId = ad?.adUnitID




        print("coordinator.rewardedInterstitialAd loaded")
    }
}

func showAd(from viewController: UIViewController) {

    print ("coordinator.showAd")

    guard let rewardedInterstitialAd = rewardedInterstitialAd else {
        return print("Ad wasn't ready.")
    }

    rewardedInterstitialAd.present(fromRootViewController: viewController) {
        let reward = rewardedInterstitialAd.adReward
        let adId = rewardedInterstitialAd.adUnitID
        // TODO: Reward the user!
        print("reward: \(reward)")
        print("adId: \(adId)")

    }

}


func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
    print("coordinator.adDidDismissFullScreenContent")
    rewardedInterstitialAd = nil
}

/// Tells the delegate that the ad will present full screen content.
func adWillPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {
    print("coordinator.adWillPresentFullScreenContent.")
}
}

This works ok most of the time, however, there's a scenario, where ad doesn't get displayed and I get the error in showAd(from viewController: UIViewController): Ad wasn't ready. I did quite some testing, and this seems to happen in a case where I get navigated to the view in which I am calling AdRewardedInterstitialView, but there's still some action going on in onDisappear of that View, which triggers updateUIViewController from AdViewControllerRepresentable and obviously instance of UIViewController in showAd consequently is not the same as it was when the ad loaded.

So my question is - is there a way to somehow "lock" the instance of UIViewController and make sure to use the same in loadAd and showAd. Or if there's some other way I can solve this?

0

There are 0 answers