Search code examples
swiftswiftuiadmob

How to implement AdMob Open Ad in a SwiftUI project with SwiftUI App Cycle?


I'm trying to implement AdMob open ad in a SwiftUI project using Google's documentation: https://developers.google.com/admob/ios/app-open-ads. The problem is that the documentation is fully written using AppDelegate.

I tried to implement the open ad by adding the AppDelegate class with this method above the @main but it doesn't working at all. There's no error, but also no ad.

class AppDelegate: UIResponder, UIApplicationDelegate, GADFullScreenContentDelegate {
   let nc = NotificationCenter.default
   func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
       GADMobileAds.sharedInstance().start(completionHandler: nil)
       return true
   }
   
   var appOpenAd: GADAppOpenAd?
   var loadTime = Date()
   
   func requestAppOpenAd() {
       let request = GADRequest()
       GADAppOpenAd.load(withAdUnitID: "ca-app-pub-3940256099942544/5662855259",
                         request: request,
                         orientation: UIInterfaceOrientation.portrait,
                         completionHandler: { (appOpenAdIn, _) in
                           self.appOpenAd = appOpenAdIn
                           self.appOpenAd?.fullScreenContentDelegate = self
                           self.loadTime = Date()
                           print("Ad is ready")
                         })
   }
   
   func tryToPresentAd() {
       if let gOpenAd = self.appOpenAd, let rwc = UIApplication.shared.windows.last!.rootViewController, wasLoadTimeLessThanNHoursAgo(thresholdN: 4) {
           gOpenAd.present(fromRootViewController: rwc)
       } else {
           self.requestAppOpenAd()
       }
   }
   
   func wasLoadTimeLessThanNHoursAgo(thresholdN: Int) -> Bool {
       let now = Date()
       let timeIntervalBetweenNowAndLoadTime = now.timeIntervalSince(self.loadTime)
       let secondsPerHour = 3600.0
       let intervalInHours = timeIntervalBetweenNowAndLoadTime / secondsPerHour
       return intervalInHours < Double(thresholdN)
   }
   
   func applicationDidBecomeActive(_ application: UIApplication) {
       self.tryToPresentAd()
   }
   
   func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
       requestAppOpenAd()
   }
   
   func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
       requestAppOpenAd()
   }
   
   func adDidPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {
       print("Ad did present")
   }
}

How can I can implement successfully an AdMob open ad in a SwiftUI project that use SwiftUI App Cycle instead of AppDelegate?


Solution

  • Implementing an Admob Open Ad in a SwiftUI app with Swift UI Lifecycle can be done in this way:

    1. Create a new OpenAd class: (Remember to import GoogleMobileAds)
    final class OpenAd: NSObject, GADFullScreenContentDelegate {
       var appOpenAd: GADAppOpenAd?
       var loadTime = Date()
       
       func requestAppOpenAd() {
           let request = GADRequest()
           GADAppOpenAd.load(withAdUnitID: "ca-app-pub-3940256099942544/5662855259",
                             request: request,
                             orientation: UIInterfaceOrientation.portrait,
                             completionHandler: { (appOpenAdIn, _) in
                               self.appOpenAd = appOpenAdIn
                               self.appOpenAd?.fullScreenContentDelegate = self
                               self.loadTime = Date()
                               print("[OPEN AD] Ad is ready")
                             })
       }
       
       func tryToPresentAd() {
           if let gOpenAd = self.appOpenAd, wasLoadTimeLessThanNHoursAgo(thresholdN: 4) {
               gOpenAd.present(fromRootViewController: (UIApplication.shared.windows.first?.rootViewController)!)
           } else {
               self.requestAppOpenAd()
           }
       }
       
       func wasLoadTimeLessThanNHoursAgo(thresholdN: Int) -> Bool {
           let now = Date()
           let timeIntervalBetweenNowAndLoadTime = now.timeIntervalSince(self.loadTime)
           let secondsPerHour = 3600.0
           let intervalInHours = timeIntervalBetweenNowAndLoadTime / secondsPerHour
           return intervalInHours < Double(thresholdN)
       }
       
       func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
           print("[OPEN AD] Failed: \(error)")
           requestAppOpenAd()
       }
       
       func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
           requestAppOpenAd()
           print("[OPEN AD] Ad dismissed")
       }
       
       func adDidPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {
           print("[OPEN AD] Ad did present")
       }
    }
    
    1. Create an ad object from the OpenAd class in the App struct:
    var ad = OpenAd()
    
    1. Present ad when app became active:
    ad.tryToPresentAd()
    

    The struct App should be structured like this:

    @main
    struct MyApp: App {
        @Environment(\.scenePhase) private var scenePhase
        var ad = OpenAd()
        
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
            .onChange(of: scenePhase) { phase in
                if phase == .active {
                    ad.tryToPresentAd()
                }
            }
        }
    }