iOSアプリ内広告の最小実装 - Google AdMob(Mobile Ads SDK)のインタースティシャル広告を表示する


## 事前準備

- AdMobアカウントを作成済み
- AdMobにアプリを登録していること

なお、記事内の広告テスト用IDを使えば試すだけなら可能。

### 環境
- iOS13.6.1
- Google-Mobile-Ads-SDK 7.64.0
- CocoaPods 1.9.3
- Xcode 11.6

## ATSを設定

[App Transport Security  \|  iOS  |  Google Developers](https://developers.google.com/admob/ios/app-transport-security?hl=ja)

ATSを切る、つまり安全でない通信やHTTP通信を**すべて許可**に設定する

info.plistに以下を追加

```xml
<dict>
	<key>NSAllowsArbitraryLoads</key>
	<true/>
</dict>
```

### 注意
Googleのドキュメントには

**NSAllowsArbitraryLoadsInWebContent**と**NSAllowsArbitraryLoadsForMedia**もTrueで追加する設定がある。

```xml
	<key>NSAllowsArbitraryLoadsInWebContent</key>
	<true/>
	<key>NSAllowsArbitraryLoadsForMedia</key>
	<true/>
```

しかしこの通り設定したところ、iOS13では広告をタップして遷移した際に

```
Cannot start load of Task XXXXXXX since it does not conform to ATS policy
```

エラーが発生。(広告ランディングページへの遷移自体には問題なかった)

これは、**NSAllowsArbitraryLoads以外の値**を設定すると、「ATS全て許可」設定が無視されてしまう仕様によるもの。

- **NSAllowsArbitraryLoadsInWebContent**： WebViewでの通信のみATSを切る。他はATSオン。
- **NSAllowsArbitraryLoadsForMedia**： リモート上のメディアへの通信のみATSを切る。他はATSオン

つまり、他の**すべての通信はATSオン状態**なので、Googleが裏でやっている通信が全てブロックされている状況。

**NSAllowsArbitraryLoadsInWebContent**と**NSAllowsArbitraryLoadsForMedia**は削除し、

**NSAllowsArbitraryLoads**のみをTrueで設定したところ、このエラーは消えた。

## AdMob SDKのインストール

CocoaPodsを使った方法を推奨とのこと。

Podfileに以下を追加して

```text
pod 'Google-Mobile-Ads-SDK'
```

インストール実行。

```shell
pod install
```

## AdMob アプリIDの設定

info.plistに以下を追加

```xml
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~1458002511</string>
```

ちなみにこの

```
ca-app-pub-3940256099942544~1458002511
```
は**テスト用のAdMobアプリID**。試したいだけであればこのIDでSDKを初期化が可能。

本番時は、AdMobの管理画面から取得したIDを設定する。

ちなみに、このIDをinfo.plistに設定し忘れるとクラッシュするとのこと。


## AdMobを初期化

**```GADMobileAds```**のインスタンスメソッド、**```start(completionHandler:)```**を呼び出して初期化。

初期化が完了(または30秒タイムアウト)すると、**```completionHandler```**が呼ばれる。

この初期化処理は

- **1回だけ**

- **アプリの起動時が理想的**

- **できるだけ早く呼び出す**

というのが推奨されるとのこと。

以下は、```AppDelegate```の```application(_:didFinishLaunchingWithOptions:)```で初期化するパターン

```swift
import GoogleMobileAds // AdMobのインポート

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        GADMobileAds.sharedInstance().start { (status) in
            // 初期化が完了(or タイムアウト)
            debugPrint("AdMob Initialization Completed")
            for (k,v) in status.adapterStatusesByClassName {
                debugPrint("\(k) >> \(v.state.rawValue == 1 ? "Ready" : "NotReady")")
            }
        }
    }
```

デバッグエリアにこの様に出れば初期化成功。

```
AdMob Initialization Completed
GADMobileAds >> Ready
```

## 広告の表示

今回は、UIの変更が少なくて済む**インタースティシャル広告**を使って表示テスト。

- インタースティシャル広告： 画面遷移時の途中などで全画面表示されるタイプの広告

### まず、広告オブジェクトを初期化

広告オブジェクト**```GADInterstitial```**を広告ユニットIDで初期化。

```swift
let interstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/4411468910")
//  はテスト専用ID
```
この**```ca-app-pub-3940256099942544/4411468910```**は、**テスト専用のインタースティシャル広告ユニットID**。

なお
> アプリの開発とテストでは実際の広告ではなく、必ずテスト広告を使ってください。実際の広告でテストすると、アカウントが停止される可能性があります。

と言うことなので、テスト時には**必ずテスト用の広告ユニットIDが指定**される様に実装すること。


### 次に、広告を読み込む

リクエストオブジェクト```GADRequest```のインスタンスを作成して、広告を事前にロードしておく。

```swift
interstitial.load(GADRequest())
```

### そして、広告を表示

表示する直前でも、```GADInterstitial```の**```isReady```**で表示可能かどうかをチェックしてから、

同じく```GADInterstitial```のインスタンスメソッド、```present(fromRootViewController:)```で表示します

```swift
guard interstitial.isReady else {
    debugPrint("Interstitial Ad is NOT Available")
    return
}
interstitial.present(fromRootViewController: self)
```

### 留意点

- **GADInterstitial**のインスタンスは、一度広告表示したら**使い捨て**る

- 初期化とリロードは**```GADInterstitialDelegate```**の**```func interstitialDidDismissScreen(_ :)```**でやる


## ViewControllerに実装

```swift:SomeViewController.swift

import UIKit
import GoogleMobileAds

class SomeViewController: UIViewController {
    var interstitialAd: GADInterstitial?

    override func viewDidLoad() {
        super.viewDidLoad()
        interstitialAd = loadInterstitialAd()
    }
    
    @IBAction fileprivate func someAction(){
        showInterstitialAd()
    }
}

extension SomeViewController: GADInterstitialDelegate {
    
    // 広告オブジェクトの生成とロード
    fileprivate func loadInterstitialAd() -> GADInterstitial {
        let interstitial = GADInterstitial(adUnitID: interstitialAdID)
        interstitial.delegate = self
        interstitial.load(GADRequest())
        return interstitial
    }
    
    // 広告を表示
    fileprivate func showInterstitialAd(){
        guard let _ad = interstitialAd, _ad.isReady else {
            debugPrint("Interstitial Ad Does NOT Available")
            return
        }
        _ad.present(fromRootViewController: self)
    }
    
    // 広告を見せた後のスキームの再開
    fileprivate func resumeSuspendedUserScheme(){
        debugPrint("Resume suspended user scheme")
    }
    
    //MARK: GADInterstitialDelegate
    /// Tells the delegate an ad request succeeded.
    func interstitialDidReceiveAd(_ ad: GADInterstitial) {
      debugPrint("interstitial Did ReceiveAd")
    }

    /// Tells the delegate an ad request failed.
    func interstitial(_ ad: GADInterstitial, didFailToReceiveAdWithError error: GADRequestError) {
      debugPrint("interstitial:didFailToReceiveAdWithError: \(error.localizedDescription)")
    }

    /// Tells the delegate that an interstitial will be presented.
    func interstitialWillPresentScreen(_ ad: GADInterstitial) {
      debugPrint("interstitial Will PresentScreen")
    }

    /// Tells the delegate the interstitial is to be animated off the screen.
    func interstitialWillDismissScreen(_ ad: GADInterstitial) {
      debugPrint("interstitial Will Dismiss Screen")
    }

    /// Tells the delegate the interstitial had been animated off the screen.
    func interstitialDidDismissScreen(_ ad: GADInterstitial) {
        debugPrint("interstitial Did Dismiss Screen")
        interstitialAd = loadInterstitialAd()
        resumeSuspendedUserScheme()
    }

    /// Tells the delegate that a user click will open another app
    /// (such as the App Store), backgrounding the current app.
    func interstitialWillLeaveApplication(_ ad: GADInterstitial) {
      debugPrint("User Click Ad")
      debugPrint("interstitial Will Leave Application")
    }
    
}
```

----
参考：
[Mobile Ads SDK スタートガイド](https://developers.google.com/admob/ios/quick-start?hl=ja)
[インタースティシャル広告](https://developers.google.com/admob/ios/interstitial?hl=ja)