SwiftUI: Admob Interstitial Ad is not being presented on rootViewController

2k views Asked by At

CODED IN SWIFTUI

I have implemented a settings page within my SwiftUI app that is presented upon clicking the button within the view. This view is wrapped within a sheet. You can see the code below:

TabsBar(showOptionsTab: $showOptionsTab)
    .sheet(isPresented: $showOptionsTab)
    {
        OptionsTab(showOptionsTab: self.$showOptionsTab)
    }

I have implemented an interstitial within this "OptionsTab" that is created when the options tab is loaded within the .onAppear() call and presented when the back button is selected. You can the code from the OptionsTab below:

@State private var interstitial : GADInterstitial!

VStack
{
    ... other code

    Button(action:
    {
        if ( self.interstitial.isReady)
        { 
            let root = UIApplication.shared.windows.first?.rootViewController
            self.interstitial.present(fromRootViewController: root!)               
        }

        self.showOptionsTab.toggle()
    })
    {
        Image(systemName: "xmark")
            .font(.largeTitle)
    }
}
.onAppear
{
    self.interstitial = GADInterstitial(adUnitID: "addID")
    let req = GADRequest()
    self.interstitial.load(req) 
}

I have additional code within the button that logs the status of the interstitial, and it shows it as being READY, and the present code is being hit... but it never presents itself.

I have implemented this exact code in the ContentView, and the interstitial loads perfectly fine. Something about this code being within the sheet is causing the interstitial not to present.

Is the rootViewController the incorrect view to load the interstitial on when in a view that is presented from a sheet? Otherwise, what else am I doing wrong?

Thanks for the help in advance!

2

There are 2 answers

0
AudioBubble On BEST ANSWER

Try this

TabsBar(showOptionsTab: $showOptionsTab)
    .sheet(isPresented: $showOptionsTab)
    {
        OptionsTab(showOptionsTab: self.$showOptionsTab)
            .OnDisapear 
            {    
                if ( self.interstitial.isReady)
                { 
                    let root = UIApplication.shared.windows.first?.rootViewController
                    self.interstitial.present(fromRootViewController: root!)               
                }
            } 
        }
    }

And then add the state interstitial variable within this view, and onAppear of the ContextView, create the interstitial there.

0
Mostfa Essam On

Solution 2:

(not tested but should work): present on the presentingViewController instead of the rootViewController

  if ( self.interstitial.isReady)
        { 
            let root = UIApplication.shared.windows.first?.rootViewController?.presentingViewController
            self.interstitial.present(fromRootViewController: root!)               
        }

check the reason, at the end of the answer

Solution 1:

it sounds like that there's something that prevents Interstitial ad to be presented when a sheet view is already presented

in my case i wanted to present the ad immediately after the sheet view is presented, which didn't work

what worked is, Presenting the ad, and after it being dismissed, the sheet view will be presented , and it worked!

for the Interstitial i used this code

import SwiftUI
import GoogleMobileAds
import UIKit

final class Interstitial:NSObject, GADInterstitialDelegate{
  var interstitialID = "ca-app-pub-3940256099942544/4411468910"
  var interstitial:GADInterstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/4411468910")
  var sheet: (() -> Void)? = nil  //this closure will be executed after dismissing the ad

  override init() {
    super.init()
    LoadInterstitial()
  }

  func LoadInterstitial(){
    let req = GADRequest()
    self.interstitial.load(req)
    self.interstitial.delegate = self
  }

  func showAd(then sheet:(() -> Void)?){
    if self.interstitial.isReady{
      let root = UIApplication.shared.windows.first?.rootViewController
      self.interstitial.present(fromRootViewController: root!)
      self.sheet = sheet
    }
    else{
      print("Not Ready")
    }
  }

  func interstitialDidDismissScreen(_ ad: GADInterstitial) {
    self.interstitial = GADInterstitial(adUnitID: interstitialID)
    LoadInterstitial()
    if let presentSheet = sheet {
      presentSheet()
    }
  }
}

here we pass whatever we want to happen to the closure which will be called when interstitialDidDismissScreen is called

now instead of switching the state when button is pressed, it will be switched after the ad is dimissed

at the top of your contentView:

    struct HistoryView: View {
      private var fullScreenAd: Interstitial!
    init() {
fullScreenAd = Interstitial()
}
    }

then in your button:

   Button(action: {
            self.interstitial.showAd {
              self.showOptionsTab.toggle()

            }              

        })

Note: i used the code of interstitial Ad from this gist

Also, AFAIK we can't tell if it's an bug in swiftUI, or it's something googleAdmob was supposed to take care of ?, there's no SwiftUI support From google so we can't blame them.

the problem AFAIK, that in iOS 13, grabbing the root ViewController will return the View Controller that presented the sheet, and when trying to present the ad, UIKit will complain that you are trying to present A view controller while a view controller is already present(the sheet), Solution ? simple, present the Ad, on the presented view Controller(The sheet)