7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[iOS] Google AdMobのバナーをSwiftUIで実装する

Last updated at Posted at 2023-08-17

はじめに

SwiftUI製のアプリにAdMobのバナーを実装したく調べてみました。

公式でSwiftUIのViewが用意されているのかな?と思いましたが、なかったです。代わりにUIViewControllerRepresentableを使う方法がドキュメントにまとまっていたのでこちらでやっていきます。

登録方法は以前UIKitを使ったAdMobの導入方法をまとめたのでこちらを

環境

Xcode 14.3

内容

実装手順以下の通り

  • 広告の幅を決定
  • UIViewControllerRepresentableGADBannerViewを作成
  • UIViewControllerからUIViewRepresentableに幅の値を渡すCoordinatorを作成

広告の幅を決定する

UIViewControllerからSwiftUIにデバイスのフレーム幅を渡すデリゲートを定義して実装します。

BannerViewController
import UIKit

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

GADBannerViewを作成

SwiftUIでViewを表示するには、UIViewControllerRepresentableプロトコルに準拠した型を作成。Viewの幅が変更されるたびに新しいバナー広告を要求するようにします。

BannerView
import SwiftUI
import GoogleMobileAds

struct BannerView: UIViewControllerRepresentable {
    @State private var viewWidth: CGFloat = .zero
    private let bannerView = GADBannerView()
    private let adUnitID = "ca-app-pub-3940256099942544/2934735716"  // Test用のIDが入力されています

    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(GADRequest())
    }
}

幅の値を渡すCoordinatorを作成

BannerViewControllerWidthDelegateに準拠するCoordinatorを作成。Coordinatorが幅の値を直接変更できるように、親BannerViewのプロパティを宣言します。

BannerView
import SwiftUI
import GoogleMobileAds

struct BannerView: UIViewControllerRepresentable {
    @State private var viewWidth: CGFloat = .zero
    private let bannerView = GADBannerView()
    private let adUnitID = Bundle.main.object(forInfoDictionaryKey: "MainBannerAdUnitId") as? String

    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 updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        guard viewWidth != .zero else { return }

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

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

+   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
+       }
+   }
}

以上で準備は完了

BannerViewを追加することで広告が表示される。

(ただし、BannerViewを直接配置したいView、VStackなどに追加したところ広告が表示されなかったのでSwiftUIのViewで一度ラップしてあげると良いかと思います)

struct ContentView: View {

  var body: some View {
    BannerView()
  }
}

画面下部にTest modeの広告が表示されました🎉

各イベントを受け取りたい場合

GADBannerViewDelegateCoordinatorに追加することで実現できる。

  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")
    }
  }

おわりに

SwiftUIのViewを用意してくれたら良いなと思いつつも、ドキュメントに丁寧に実装方法が載っていたので助かりました。
バナー以外のViewの実装方法もドキュメントに記載ありましたので、必要になったタイミングで見ていこうと思います。

参考

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?