SwiftUI

This guide shows you how to integrate ads with the Google Mobile Ads SDK into your SwiftUI app. It covers integration steps for banner, interstitial, native, rewarded, and rewarded interstitial ad formats.

Prerequisites

Always test with test ads

When building and testing your apps, make sure you use test ads rather than live, production ads. Failure to do so can lead to suspension of your account.

We provide test ad unit IDs that have been specially configured to return test ads for every request which you can use in your own apps while coding, testing, and debugging. When you're ready to publish your app, be sure you replace those ad unit IDs with your own.

For more information about how the Mobile Ads SDK's test ads work, see Enabling test ads.

The primary steps to integrate banner ads in SwiftUI are as follows:

  • Determine your ad width.
  • Create a GAMBannerView in a custom UIViewControllerRepresentable type.
  • Create a Coordinator object to pass the width value from the UIViewController to the UIViewRepresentable.

Determine your ad width

To use our preferred adaptive banner format, you first define and implement a delegate to pass the device's frame width from a UIViewController to your SwiftUI class.

// Delegate methods for receiving width update messages.

protocol BannerViewControllerWidthDelegate: AnyObject {
  func bannerViewController(_ bannerViewController: BannerViewController, didUpdate width: CGFloat)
}
class BannerViewController: UIViewController {
  weak var delegate: BannerViewControllerWidthDelegate?

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

    // Tell the delegate the initial ad width.
    delegate?.bannerViewController(
      self, didUpdate: view.frame.inset(by: view.safeAreaInsets).size.width)
  }

  override func viewWillTransition(
    to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator
  ) {
    coordinator.animate { _ in
      // do nothing
    } completion: { _ in
      // Notify the delegate of ad width changes.
      self.delegate?.bannerViewController(
        self, didUpdate: self.view.frame.inset(by: self.view.safeAreaInsets).size.width)
    }
  }
}

Create a GAMBannerView

To represent your view controller in SwiftUI, create a type that conforms to the UIViewControllerRepresentable protocol. This type's primary responsibility is to create the relationship between the GAMBannerView and the content view. It keeps the viewWidth as @State, and requests a new banner ad whenever the view's width changes.

struct BannerView: UIViewControllerRepresentable {
  @State private var viewWidth: CGFloat = .zero
  private let bannerView = GAMBannerView()
  private let adUnitID = "/6499/example/banner"

  func makeUIViewController(context: Context) -> some UIViewController {
    let bannerViewController = BannerViewController()
    bannerView.adUnitID = adUnitID
    bannerView.rootViewController = bannerViewController
    bannerViewController.view.addSubview(bannerView)

    return bannerViewController
  }

  func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
    guard viewWidth != .zero else { return }

    // Request a banner ad with the updated viewWidth.
    bannerView.adSize = GADCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(viewWidth)
    bannerView.load(GAMRequest())
  }

Add to the view hierarchy

Add your UIViewControllerRepresentable type to your content view's view hierarchy like so:

struct ContentView: View {

  var body: some View {
    BannerView()
  }
}

Pass the width value to your UIViewControllerRepresentable

The makeCoordinator() method provided by UIViewControllerRepresentable lets you create a class to communicate changes from your view controller to your SwiftUI interface.

Create a nested Coordinator class inside your UIViewControllerRepresentable that conforms to the BannerViewControllerWidthDelegate protocol. It declares a property of its parent BannerView so the coordinator can modify the viewWidth value directly.

struct BannerView: UIViewControllerRepresentable {
  @State private var viewWidth: CGFloat = .zero
  ...

  func makeUIViewController(context: Context) -> some UIViewController {
    let bannerViewController = BannerViewController()
    bannerView.adUnitID = adUnitID
    bannerView.rootViewController = bannerViewController
    bannerViewController.view.addSubview(bannerView)
    // Tell the bannerViewController to update our Coordinator when the ad
    // width changes.
    bannerViewController.delegate = context.coordinator

    return bannerViewController
  }

  ...

  func makeCoordinator() -> Coordinator {
    Coordinator(self)
  }

  fileprivate class Coordinator: NSObject, BannerViewControllerWidthDelegate {
    let parent: BannerView

    init(_ parent: BannerView) {
      self.parent = parent
    }

    // MARK: - BannerViewControllerWidthDelegate methods

    func bannerViewController(_ bannerViewController: BannerViewController, didUpdate width: CGFloat) {
      // Pass the viewWidth from Coordinator to BannerView.
      parent.viewWidth = width
    }
  }
}

Ad events

To be notified of events related to banner ad interactions, set the delegate property of the banner view. Then implement GADBannerViewDelegate in your Coordinator class to receive the following delegate calls.

struct BannerView: UIViewControllerRepresentable {
  ...

  func makeUIViewController(context: Context) -> some UIViewController {
    let bannerViewController = BannerViewController()
    bannerView.adUnitID = adUnitID
    bannerView.rootViewController = bannerViewController
    bannerView.delegate = context.coordinator
    ...
  }

  fileprivate class Coordinator: NSObject, BannerViewControllerWidthDelegate, GADBannerViewDelegate
  {
    ...

    // MARK: - GADBannerViewDelegate methods

    func bannerViewDidReceiveAd(_ bannerView: GADBannerView) {
      print("\(#function) called")
    }

    func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: Error) {
      print("\(#function) called")
    }

    func bannerViewDidRecordImpression(_ bannerView: GADBannerView) {
      print("\(#function) called")
    }

    func bannerViewWillPresentScreen(_ bannerView: GADBannerView) {
      print("\(#function) called")
    }

    func bannerViewWillDismissScreen(_ bannerView: GADBannerView) {
      print("\(#function) called")
    }

    func bannerViewDidDismissScreen(_ bannerView: GADBannerView) {
      print("\(#function) called")
    }
  }
}

This concludes the SwiftUI implementation for banner ads. The complete example for banner ads is available on GitHub.

Full-screen ads

This section covers SwiftUI implementations for the following ad formats:

The code snippets below demonstrate these concepts for interstitial ads as an example.

Implementation

In order to integrate full-screen ads into your SwiftUI project, you need the following:

  • A class to load and present ads
  • A UIViewControllerRepresentable type to manage the presenting view controller
  • A content view to manage the view itself

Create an ad coordinator

Create a class, named AdCoordinator, that is responsible for loading and presenting ads.

class AdCoordinator: NSObject {
  private var ad: GAMInterstitialAd?

  func loadAd() {
    GAMInterstitialAd.load(
      withAdUnitID: "/6499/example/interstitial", request: GAMRequest()
    ) { ad, error in
      if let error = error {
        return print("Failed to load ad with error: \(error.localizedDescription)")
      }

      self.ad = ad
    }
  }

  func presentAd(from viewController: UIViewController) {
    guard let fullScreenAd = ad else {
      return print("Ad wasn't ready")
    }

    fullScreenAd.present(fromRootViewController: viewController)
  }
}

Present an ad

Because the present(fromRootViewController:) method requires a UIViewController reference, create a type that conforms to the UIViewControllerRepresentable protocol. This type will hold a reference to a UIViewController that is passed to the previously created AdCoordinator method, presentAd(viewcontroller:).

struct AdViewControllerRepresentable: UIViewControllerRepresentable {
  let viewController = UIViewController()

  func makeUIViewController(context: Context) -> some UIViewController {
    return viewController
  }

  func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
    // No implementation needed. Nothing to update.
  }
}

Add to the view hierarchy

AdViewControllerRepresentable doesn't have any significance to the content on screen, but still needs to be added to the view hierarchy. Because of this, add it within the view modifier background(alignment:content:) so it doesn't take up screen real estate.

Here's an example of how to implement AdViewControllerRepresentable in a content view:

struct ContentView: View {
  private let adViewControllerRepresentable = AdViewControllerRepresentable()

  var body: some View {
     Text("Hello, World!")
      .background {
        // Add the adViewControllerRepresentable to the background so it
        // doesn't influence the placement of other views in the view hierarchy.
        adViewControllerRepresentable
          .frame(width: .zero, height: .zero)
      }
  }
}

Manage the ad's view lifecycle

Your content view is responsible for calling methods in AdCoordinator to load and show ads. You can call these methods from action events such as buttons, or any other actions from your app.

struct ContentView: View {
  private let adViewControllerRepresentable = AdViewControllerRepresentable()
  private let adCoordinator = AdCoordinator()

  var body: some View {
     Text("Hello, World!")
      .background {
        // Add the adViewControllerRepresentable to the background so it
        // does not influence the placement of other views in the view hierarchy.
        adViewControllerRepresentable
          .frame(width: .zero, height: .zero)
      }

     Button("Load an ad") {
       adCoordinator.loadAd()
     }

     Button("Watch an ad") {
       adCoordinator.presentAd(from: adViewControllerRepresentable.viewController)
     }
  }
}

Ad events

To be notified of events related to the full-screen ad interactions, set the fullScreenContentDelegate property of the ad. Then implement GADFullScreenContentDelegate to receive the following delegate calls.

class AdCoordinator: NSObject, GADFullScreenContentDelegate {
  ...

  func loadAd() {
  GAMInterstitialAd.load(
    withAdUnitID: "/6499/example/interstitial", request: GAMRequest()
  ) { ad, error in
    if let error = error {
      return print("Failed to load ad with error: \(error.localizedDescription)")
    }

    self.ad = ad
    self.ad?.fullScreenContentDelegate = self
  }
  ...

  // MARK: - GADFullScreenContentDelegate methods

  func adDidRecordImpression(_ ad: GADFullScreenPresentingAd) {
    print("\(#function) called")
  }

  func adDidRecordClick(_ ad: GADFullScreenPresentingAd) {
    print("\(#function) called")
  }

  func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
    print("\(#function) called")
  }

  func adWillPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {
    print("\(#function) called")
  }


  func adWillDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
    print("\(#function) called")
  }

  func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
    print("\(#function) called")
  }
}

This concludes the SwiftUI implementation for full-screen ads. The complete examples for each respective ad format are available on GitHub.

Native ads

The primary steps to integrate native ads in SwiftUI are as follows:

  • Create a GADNativeAdView with UIViewRepresentable.
  • Create a view model class to handle data changes.
  • Subscribe to data changes using SwiftUI property wrappers.

Create a GADNativeAdView

Native ads are displayed in a GADNativeAdView, a UIView object. The first step toward integrating native ads is to represent a GADNativeAdView in your SwiftUI view hierarchy.

To represent a GADNativeAdView in SwiftUI, create a type that conforms to the UIViewRepresentable protocol. This type's primary responsibility is to create the relationship between the GADNativeAd and the content view.

struct NativeAdView: UIViewRepresentable {
  typealias UIViewType = GADNativeAdView

  @ObservedObject var nativeAdViewModel: ViewModel

   func makeUIView(context: Context) -> GADNativeAdView {
     // Link the outlets to the views in the GADNativeAdView.
     return
      Bundle.main.loadNibNamed(
        "NativeAdView",
        owner: nil,
        options: nil)?.first as! GADNativeAdView
  }

  func updateUIView(_ nativeAdView: GADNativeAdView, context: Context) {
    guard let nativeAd = nativeAdViewModel.nativeAd else { return }

    // Work with your native ad.
    nativeAdView.mediaView?.mediaContent = nativeAd.mediaContent
    ...
  }
}

Create a view model class

Your view model class is responsible for loading and maintaining the GADNativeAd data and handling GADNativeAdLoaderDelegate events, implemented as ViewModel in the example.

Publish the ad data

Once the view model receives an ad as part of the GADNativeAdLoaderDelegate, the next step is to publish the data.

class ViewModel: NSObject, ObservableObject, GADNativeAdLoaderDelegate {
  @Published var nativeAd: GADNativeAd?
  private var adLoader: GADAdLoader!

  func refreshAd() {
    adLoader = GADAdLoader(
      adUnitID:
        "/6499/example/native",
      rootViewController: nil,
      adTypes: [.native], options: nil)
    adLoader.delegate = self
    adLoader.load(GAMRequest())
  }

  func adLoader(_ adLoader: GADAdLoader, didReceive nativeAd: GADNativeAd) {
    self.nativeAd = nativeAd
  }

  func adLoader(_ adLoader: GADAdLoader, didFailToReceiveAdWithError error: Error) {
    print("\(adLoader) failed with error: \(error.localizedDescription)")
  }
}

Subscribe to data changes

In order for the content view to know what to display, it needs to subscribe to the data changes in ViewModel. The following code demonstrates adding the UIViewRepresentable to the view hierarchy with a binding on ViewModel:

struct ContentView: View {
  @StateObject private var viewModel = ViewModel()

  var body: some View {
    NativeAdView(nativeAdViewModel: viewModel)
  }
}

Ad events

To be notified of events related to the native ad interactions, set the delegate property of the native ad. Then implement GADNativeAdDelegate to receive the following delegate calls.

class ViewModel: NSObject, ObservableObject, GADNativeAdLoaderDelegate, GADNativeAdDelegate {
  ...

  func adLoader(_ adLoader: GADAdLoader, didReceive nativeAd: GADNativeAd) {
    self.nativeAd = nativeAd
    nativeAd.delegate = self
  }
  ...

  // MARK: - GADNativeAdDelegate methods

  func nativeAdDidRecordClick(_ nativeAd: GADNativeAd) {
    print("\(#function) called")
  }

  func nativeAdDidRecordImpression(_ nativeAd: GADNativeAd) {
    print("\(#function) called")
  }

  func nativeAdWillPresentScreen(_ nativeAd: GADNativeAd) {
    print("\(#function) called")
  }

  func nativeAdWillDismissScreen(_ nativeAd: GADNativeAd) {
    print("\(#function) called")
  }

  func nativeAdDidDismissScreen(_ nativeAd: GADNativeAd) {
    print("\(#function) called")
  }
}

This concludes the SwiftUI implementation for native ads. The complete example for native ads is available on GitHub.