【Swift UI】AdMobのリワード広告の実装方法!FullScreenContentDelegate

【Swift UI】AdMobのリワード広告の実装方法!FullScreenContentDelegate

この記事からわかること

  • Swift UIリワード広告の実装方法
  • AdMob導入
  • 動画視聴報酬を得られる広告
  • RewardedAdFullScreenContentDelegate使い方
  • RewardedAdクラスのloadメソッドやpresentメソッドの使い方
  • リワード広告に1日1回視聴回数制限を設ける方法
  • フリークエンシーキャップとは?

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

Swift UIでAdMobを使用したリワード広告の実装方法をまとめて行きたいと思います。Swift6対応済みのコードを紹介していきます。

リワード広告とは?

そもそも「リワード広告」とはアフィリエイト(成功報酬型)広告の1種で何かしらの報酬を付与する代わりに短い動画を視聴してもらう広告形態です。今回はiOSアプリに広告を掲載する方法の1つである「Google AdMob」を活用します。

Google AdMob」はGoogleが提供しているiOSやAndroidなどのモバイルアプリに広告を設置できるサービスで、バナー広告をはじめリワード広告、インタースティシャル広告の実装も簡単に実装可能になっています。

Google AdSenseのホーム画面

使用するための注意点とポリシー

リワード広告をアプリに組み込む上では取り扱いに注意が必要です。公式サイトで注意点やポリシーが公開されているので違反しないように設計してください。

重要なところだけまとめておきます。

注意点

基本的には短い動画広告が配信されることと貰える報酬の明記、そしてちゃんと報酬が与られる動作にすれば問題ありません

Swift UIでリワード広告を実装する方法

流れ

  1. AdMobへ登録と広告ユニットの作成
  2. プロジェクトへAdMobの導入
  3. AdMob用のファイルを作成
  4. リワードクラスの作成
  5. 広告を読み込むメソッドの実装
  6. 読み込んだ広告を表示するメソッドの実装
  7. ビューとして呼び出す

今回はSwift UIを使用している場合のリワード広告の実装方法を見ていきます。AdMobが未登録の場合や広告ユニットが未作成、プロジェクトへ未導入の場合は下記記事をご覧ください。

AdMobでは開発途中のテスト実装の際は本番用の広告ユニットIDを使用せずテスト用の広告ユニットIDを使用することを推奨しています。テスト実装の際は以下のIDを然るべき箇所に記述してください。

// テスト用広告ユニットID
ca-app-pub-3940256099942544/1712485313

リワード広告のロードと表示ロジックの実装

広告ユニットの作成とプロジェクトへAdMobの導入が完了したら実際にコードを書いていきます。リワード広告のロードと表示ロジッを持ったサービスクラスを実装します。RewardedAd.loadasync/awaitが用意されていますが、presentは用意されていなかったのでwithCheckedThrowingContinuationでラップしておきました。


import SwiftUI
import UIKit
import GoogleMobileAds

@MainActor
protocol RewardServiceProtocol {
    func loadReward() async throws -> RewardedAd
    func showReward(_ ad: RewardedAd) async throws
}

@MainActor
final class RewardService: NSObject, RewardServiceProtocol, FullScreenContentDelegate {
    /// 広告を取得
    func loadReward() async throws -> RewardedAd {
        let request = Request()
        request.scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
        // 広告ユニットIDを指定
        let ad = try await RewardedAd.load(with: "ca-app-pub-3940256099942544/1712485313", request: request)
        ad.fullScreenContentDelegate = self
        return ad
    }

    /// 広告を表示
    func showReward(_ ad: RewardedAd) async throws {
        let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
        guard let rootVC = windowScene?.windows.first?.rootViewController else { return }

        try await withCheckedThrowingContinuation { continuation in
            ad.present(from: rootVC) {
                // 広告視聴完了
                continuation.resume()
            }
        }
    }
}

FullScreenContentDelegate広告表示関連の通知を受け取るデリゲートメソッドが定義されているプロトコルです。今回は特に定義してなかったですが一応準拠させておきました。

中間となるViewModelを実装する

ここは特に特殊なことはしていないです。

@MainActor
final class RewardViewModel: ObservableObject {
    @Published var rewardLoaded = false
    private var rewardedAd: RewardedAd?
    
    private let rewardService: RewardServiceProtocol
    
    init(rewardService: RewardServiceProtocol = RewardService()) {
        self.rewardService = rewardService
    }
    
    func loadReward() async {
        do {
            let ad = try await rewardService.loadReward()
            rewardedAd = ad
            rewardLoaded = true
        } catch {
            // リワード読み込み失敗
            rewardLoaded = false
        }
    }
    
    func showReward() async {      
        // 広告が未読み込みであれば読み込んでから再度表示を試みる
        guard let ad = rewardedAd else {
            await loadReward()
            await showReward()
            return
        }
        do {
            try await rewardService.showReward(ad)
            rewardLoaded = false
            // TODO:報酬付与処理
        } catch {
            // リワード表示失敗
        }
    }
}

Swift UIでビューとして呼び出す

作成したクラスを表示したいビューで呼び出していきます。ObservableObjectプロトコルに準拠させているのでインスタンス化するクラスには@StateObjectをつけておきます。


import SwiftUI

struct RewardView: View {
    // AdMob reward広告
    @StateObject private var viewModel = RewardViewModel()

    var body: some View {
        Button {
            Task {
                //  広告配信
                await viewModel.showReward()
            }
        } label: {
            Text("広告を視聴する")
        }.onAppear {
            Task {
                await viewModel.loadReward()
            }
        }.disabled(!reward.rewardLoaded)
    }
}

ビューが表示されるタイミングで処理を実行するonAppearの中に広告を読み込むloadRewardメソッドを記述しておきます。広告を表示するshowRewardメソッドはボタンのアクション部分に記述しておきます。

これでボタンをクリック時にリワード広告が再生されるようになります。広告の読み込みが終了していない場合に備えてボタンを非アクティブにするdisabledに読み込みフラグを渡しておきました。

1日1回の視聴回数制限を設ける

リワード広告では視聴回数制限を設けることが推奨されています。今回は「1日に1回の視聴回数制限」を設けていきたいと思います。ここで肝となるのは以下のポイントです。

@AppStorageはプロパティーラッパの1つでアプリが停止してもデータを保持することができます。ここに最後に視聴した日付を格納しておき、現在の日付と照らし合わせることで何回目の視聴か識別できるようにしています。

おすすめ記事:

まずは上部に必要となるプロパティを定義していきます。

struct RewardView: View {
  /// リワード広告視聴回数制限アラート
  @State private var isAlertReward:Bool = false   
  /// 最終視聴日時を保存する
  @AppStorage("LastAcquisitionDate") var lastAcquisitionDate = "" 
  /// 現在の日付を返す
  private func nowTime() -> String {
      let df = DateFormatter()
      df.calendar = Calendar(identifier: .gregorian)
      df.locale = Locale(identifier: "ja_JP")
      df.timeZone = TimeZone(identifier: "Asia/Tokyo")
      df.dateStyle = .short
      df.timeStyle = .none
      return df.string(from: Date())
  }
  // 〜〜〜〜〜〜〜〜〜〜
}

あとは日付を比較した結果によって処理を分岐させればOKです。(雑にUIに入れてしまいましたがViewModelに含めるべきです)

if lastAcquisitionDate != nowTime() {
  Task {
      //  広告配信
      await viewModel.showReward()
      lastAcquisitionDate = nowTime() // 最終視聴日を格納
  }
} else {
    isAlertReward = true
}

フリークエンシーキャップ

リワード広告の視聴制限は広告ユニットごとに設定することも可能です。

AdMobから広告ユニットを作成する際に「詳細設定」をクリックし「フリークエンシーキャップ」という項目を有効にすると時間単位の視聴回数制限設けることができます

しかしこれはあくまで配信されるかされないかなので先ほどのようなUI側で明示的に表示できるように何かしらの機構を作っておくことをおすすめします。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

私がSwift UI学習に使用した参考書

Search Box

Sponsor

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index