はじめに
SwiftUI製のアプリにAdMobのバナーを実装したく調べてみました。
公式でSwiftUIのViewが用意されているのかな?と思いましたが、なかったです。代わりにUIViewControllerRepresentable
を使う方法がドキュメントにまとまっていたのでこちらでやっていきます。
登録方法は以前UIKitを使ったAdMobの導入方法をまとめたのでこちらを
環境
Xcode 14.3
内容
実装手順以下の通り
- 広告の幅を決定
-
UIViewControllerRepresentable
でGADBannerView
を作成 -
UIViewController
からUIViewRepresentable
に幅の値を渡すCoordinator
を作成
広告の幅を決定する
UIViewController
からSwiftUI
にデバイスのフレーム幅を渡すデリゲートを定義して実装します。
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の幅が変更されるたびに新しいバナー広告を要求するようにします。
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のプロパティを宣言します。
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の広告が表示されました🎉
各イベントを受け取りたい場合
GADBannerViewDelegate
をCoordinator
に追加することで実現できる。
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の実装方法もドキュメントに記載ありましたので、必要になったタイミングで見ていこうと思います。
参考