SwiftUI APP 集成 Google 移动广告 Admob (一)

@高效码农  December 8, 2021


随着越来越多的应用程序使用 SwiftUI 构建,我想提供一种简单的方法将 Admob 直接集成到您的应用程序中。为了让大家集成的更容易,我计划将它作为 Cocoapod 发布。



设置 Google 移动广告 (Admob) SDK

申请应用,请参考 Google 的官方文档地址为:https://developers.google.com/admob/ios/quick-start

使用 Cocoapods 导入 SDK

pod 'Google-Mobile-Ads-SDK'


pod install --repo-update

更新 Info.plist

请更新应用的 Info.plist 文件以添加以下两个键:

  • 一个字符串值为您的 AdMob 应用 ID 的 GADApplicationIdentifier 键(在 AdMob 界面中标识)。
  • 一个 SKAdNetworkIdentifier 值为 Google(cstr6suwn9.skadnetwork),并选择向 Google 提供了这些值的其他买家的 SKAdNetworkItems 键。

初始化 Google 移动广告 (Admob) SDK

import SwiftUI
import AppTrackingTransparency
import GoogleMobileAds

struct ExampleApp: App {
    //在 App Delegate 中使用 init() 代替 ApplicationDidFinishLaunchWithOptions
    init() {
        if ATTrackingManager.trackingAuthorizationStatus == .notDetermined {
            // TODO 这里可以弹出隐私协议
        } else {
            ATTrackingManager.requestTrackingAuthorization { status in                                
                GADMobileAds.sharedInstance().start(completionHandler: nil)
    var body: some Scene {
        WindowGroup {


创建一个 BannerAd继承 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

    override func viewDidAppear(_ animated: Bool) {

    //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

    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)


要在 SwiftUI 中使用新创建的UIViewController,您需要创建一个UIViewControllerRepresentable

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) {

现在你的横幅广告是一个 SwiftUI 视图,但我们仍然需要设置一些东西,以便你可以将它添加到你的应用程序中。让我们创建一个名为SwiftUIBannerAd的新 SwiftUI 视图。此视图将管理您的广告的位置和框架。

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
            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
                //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
            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


测试您的广告时,请务必将adUnitId设置为横幅测试广告单元 ID:ca-app-pub-3940256099942544/2934735716

您可能已经注意到,我使用NotificationCenter和UIApplication来获取安全区域和方向。虽然您可以使用GeometryReader来执行此操作,但我不建议将其用于生产应用程序。在测试我自己的 SwiftUI 应用程序WidgeTube 时,我发现GeometryReader对于生产来说根本不可靠。很多时候,它是 SwiftUI 的属性图崩溃的根源。虽然有些人已经找到了避免这种情况的方法,但通过试验视图堆栈的顺序,我决定最好完全避免使用GeometryReader。



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)")
            self.interstitialAd = interstitialAd


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
        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")
    func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
        //Prepares another ad for the next time view presented
        InterstitialAd.shared.loadAd(withAdUnitId: adUnitId)
        //Dismisses the view once ad dismissed

测试时,使用插页式测试广告单元 ID:ca-app-pub-3940256099942544/4411468910