SwiftUIでAdMob対応(バナー編)


要約

UIViewControllerRepresentableを使ってSwiftUIでいい感じにバナーを含んだ画面を表示します。

はじめに

SwiftUIでAdMobのバナーが表示したかったので、実装してみました。
AdMobの導入が済んでいる事が前提となります。
https://developers.google.com/admob/ios/quick-start?hl=ja

結果

こんな感じでプレビューでバナーが表示できました。

Repository

概要

SwiftUIでAdMob対応をします。
手始めにバナーを表示します。

Life Cycle SwiftUI App対応

リポジトリではプロジェクトはSwiftUI Appでのプロジェクトになります。
AdMobはAppDelegateでGADMobileAds.sharedInstance().start(completionHandler: nil)の初期化を行う必要があるので、ひと手間必要です。

AppDelegateを作成し、プロジェクトのAppにUIApplicationDelegateAdaptorを追加して、AppDelegate内で初期化するようにします。

@main
struct SwiftUIAdMobApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        GADMobileAds.sharedInstance().start(completionHandler: nil)
        return true
    }
}

UIViewControllerRepresentableで広告表示用UIViewControllerを作成

AdMobのバナーには、広告の処理を行うためのフルスクリーンなrootViewControllerが必要です。
SwiftUI単体ではUIViewControllerを用意することができないため、これをUIViewControllerRepresentable経由で用意します。

ContainedAdViewControllerでUIViewControllerRepresentableに適合する処理を書きます。
makeUIViewControllerで提供するUIViewControllerはStoryboardで用意しました。

struct ContainedAdViewController<Content: View>: UIViewControllerRepresentable {
    let rootView: Content

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

    func makeUIViewController(context: Context) -> UIViewController {
        let adViewController: AdViewController = UIStoryboard(name: "AdViewController", bundle: nil).instantiateInitialViewController()!
        adViewController.rootView = AnyView(rootView)
        return adViewController
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {

    }

    typealias UIViewControllerType = UIViewController

    class Coordinator: NSObject {
        var parent: ContainedAdViewController

        init(_ containedAdViewController: ContainedAdViewController) {
            parent = containedAdViewController
        }
    }
}

Storyboardの初期表示はAdViewControllerというUIViewController継承クラスで、その内部ではSwiftUIを表示するUIHostingControllerコンテナとその下で表示を行うバナーを格納しています。
広告バナーに対するrootViewControllerはAdViewControllerになります。

SwiftUI側の表示

SwiftUI側はContainedAdViewControllerのrootViewに表示したいViewを実装すればOKです。

struct ContentView: View {
    var body: some View {
        ContainedAdViewController(rootView:
                                    Text("Hello, world!").padding()
        )

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

動作

表示できました。

上記のRepositoryのコードはSceneもサポートしているので、複数画面でもばっちりアダプティブ広告が表示できます。