SwiftUI APP integrates Google mobile advertising Admob

For all articles, please move to:

SwiftUI APP integrates Google mobile advertising Admob (I): https://www.xugj520.cn/archives/swiftui-admob-1.html
SwiftUI APP integrates Google mobile advertising Admob (II): https://www.xugj520.cn/archives/swiftui-admob-2.html

Introduction:

As more and more applications are built using SwiftUI, I want to provide an easy way to integrate Admob directly into your application. In order to make it easier for everyone to integrate, I plan to release it as Cocoapod.

Let's take a look at the effect after integration:

Set up Google mobile advertising (Admob) SDK

For application, please refer to Google's official documentation at: https://developers.google.com/admob/ios/quick-start .

Importing SDK using Cocoapods

pod 'Google-Mobile-Ads-SDK'

Then run using the command line

pod install --repo-update

Update Info.plist

Please update the applied Info.plist file to add the following two keys:

  • A string value is the GADApplicationIdentifier key of your AdMob application ID (identified in the AdMob interface).
  • A SKAdNetworkIdentifier value is Google (cstr6suwn9.skadnetwork), and select the SKAdNetworkItems key of other buyers who provide these values to Google.
 <key>GADApplicationIdentifier</key>
 <string>ca-app-pub-3940256099942544~2435281174</string>

Initialize Google mobile advertising (Admob) SDK

import SwiftUI
import AppTrackingTransparency
import GoogleMobileAds

@main
struct ExampleApp: App {
    
    //Use init() in App Delegate instead of ApplicationDidFinishLaunchWithOptions
    init() {
        if ATTrackingManager.trackingAuthorizationStatus == .notDetermined {
            // TODO, you can pop up the privacy agreement here
        } else {
            ATTrackingManager.requestTrackingAuthorization { status in                                
                GADMobileAds.sharedInstance().start(completionHandler: nil)
            }
        }
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Banner ad (adaptive)

Create a BannerAd inheriting UIViewController

class BannerAdVC: UIViewController {
    let adUnitId: String
    
    //Initialize variable
    init(adUnitId: String) {
        self.adUnitId = adUnitId
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    var bannerView: GADBannerView = GADBannerView() //Creates your BannerView
    override func viewDidLoad() {
        bannerView.adUnitID = adUnitId
        bannerView.rootViewController = self
      
        //Add our BannerView to the VC
        view.addSubview(bannerView)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        loadBannerAd()
    }

    //Allows the banner to resize when transition from portrait to landscape orientation
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        coordinator.animate { _ in
            self.bannerView.isHidden = true //So banner doesn't disappear in middle of animation
        } completion: { _ in
            self.bannerView.isHidden = false
            self.loadBannerAd()
        }
    }

    func loadBannerAd() {
        let frame = view.frame.inset(by: view.safeAreaInsets)
        let viewWidth = frame.size.width

        //Updates the BannerView size relative to the current safe area of device (This creates the adaptive banner)
        bannerView.adSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(viewWidth)

        bannerView.load(GADRequest())
    }
}

To use the newly created UIViewController in SwiftUI, you need to create a uiviewcontrollerrepresententable

final class BannerAd: UIViewControllerRepresentable {
    let adUnitId: String
    
    init(adUnitId: String) {
        self.adUnitId = adUnitId
    }
    
    
    func makeUIViewController(context: Context) -> BannerAdVC {
        return BannerAdVC(adUnitId: adUnitId)
    }

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

Now your banner ad is a swift UI view, but we still need to set up something so that you can add it to your application. Let's create a new SwiftUI view called SwiftUI banner ad. This view will manage the location and frame of your ads.

struct SwiftUIBannerAd: View {
    @State var height: CGFloat = 0 //Height of ad
    @State var width: CGFloat = 0 //Width of ad
    @State var adPosition: AdPosition
    let adUnitId: String
    
    init(adPosition: AdPosition, adUnitId: String) {
        self.adPosition = adPosition
        self.adUnitId = adUnitId
    }
    
    enum AdPosition {
        case top
        case bottom
    }
    
    public var body: some View {
        VStack {
            if adPosition == .bottom {
                Spacer() //Pushes ad to bottom
            }
            
            //Ad
            BannerAd(adUnitId: adUnitId)
                .frame(width: width, height: height, alignment: .center)
                .onAppear {
                    //Call this in .onAppear() b/c need to load the initial frame size
                    //.onReceive() will not be called on initial load
                    setFrame()
                }
                //Changes the frame of the ad whenever the device is rotated.
                //This is what creates the adaptive ad
                .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
                    setFrame()
                }
            
            if adPosition == .top {
                Spacer() //Pushes ad to top
            }
        }
    }
    
    func setFrame() {
      
        //Get the frame of the safe area
        let safeAreaInsets = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.safeAreaInsets ?? .zero
        let frame = UIScreen.main.bounds.inset(by: safeAreaInsets)
        
        //Use the frame to determine the size of the ad
        let adSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(frame.width)
        
        //Set the ads frame
        self.width = adSize.size.width
        self.height = adSize.size.height
    }
}

this is it! You can add ads to your app. I recommend that you do this by placing it at the highest level of ZStack. (refer to the example at the beginning)

When testing your advertisement, be sure to set adUnitId as banner test advertisement unit ID: CA app pub 3940256099942544 / 2934735716

You may have noticed that I use NotificationCenter and UIApplication to get security zones and directions. Although you can use GeometryReader to do this, I do not recommend using it for production applications. When testing my own SwiftUI application, WidgeTube, I found that GeometryReader was simply unreliable for production. Many times, it is the root cause of the property graph crash of SwiftUI. Although some people have found a way to avoid this situation, by experimenting with the order of the view stack, I decided that it was best to avoid using GeometryReader completely.

Interstitial Ad

First, we need to create an InterstitialAdObject. This object handles the loading of insert ads in your application.

class InterstitialAd: NSObject {
    var interstitialAd: GADInterstitialAd?
    
    //Want to have one instance of the ad for the entire app
    //We can do this b/c you will never show more than 1 ad at once so only 1 ad needs to be loaded
    static let shared = InterstitialAd()
    
    func loadAd(withAdUnitId id: String) {
        let req = GADRequest()
        GADInterstitialAd.load(withAdUnitID: id, request: req) { interstitialAd, err in
            if let err = err {
                print("Failed to load ad with error: \(err)")
                return
            }
            
            self.interstitialAd = interstitialAd
        }
    }
}

We will now use this object to create an interstitialdiew. The view will be a UIViewControllerRepresentable and a GADFullScreenContentDelegate.

class InterstitialAdView: NSObject, UIViewControllerRepresentable, GADFullScreenContentDelegate {
    
    //Here's the Ad Object we just created
    let interstitialAd = InterstitialAd.shared.interstitialAd
    @Binding var isPresented: Bool
    var adUnitId: String
    
    init(isPresented: Binding<Bool>, adUnitId: String) {
        self._isPresented = isPresented
        self.adUnitId = adUnitId
        super.init()
        
        interstitialAd?.fullScreenContentDelegate = self //Set this view as the delegate for the ad
    }
    
    //Make's a SwiftUI View from a UIViewController
    func makeUIViewController(context: Context) -> UIViewController {
        let view = UIViewController()
        
        //Show the ad after a slight delay to ensure the ad is loaded and ready to present
        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1)) {
            self.showAd(from: view)
        }
        
        return view
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        
    }
    
    //Presents the ad if it can, otherwise dismisses so the user's experience is not interrupted
    func showAd(from root: UIViewController) {
        
        if let ad = interstitialAd {
            ad.present(fromRootViewController: root)
        } else {
            print("Ad not ready")
            self.isPresented.toggle()
        }
    }
    
    func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
        //Prepares another ad for the next time view presented
        InterstitialAd.shared.loadAd(withAdUnitId: adUnitId)
        
        //Dismisses the view once ad dismissed
        isPresented.toggle()
    }
}

Later, we will create a full screen modifier to render this view. Now we'll keep the existing content and create incentive ads (if you don't create incentive ads, skip to the full screen modifier).
During the test, insert test advertising unit ID: CA app pub 3940256099942544 / 4411468910 is used

Keywords: iOS Google xcode swiftui

Added by cyprus on Thu, 09 Dec 2021 03:53:38 +0200