React Native + Expo のアプリに Admob 広告を設定する

React Native + Expo で開発したアプリに広告をつける方法を紹介します。イメージとしては、以下のような感じで広告を出せます。下はバナー広告の例ですが、他の広告タイプにも対応しています。

react-native-google-mobile-ads をインストールする

shell
npm i react-native-google-mobile-ads

expo-build-properties をインストールする

shell
npm i expo-build-properties

expo-build-properties は Prebuild のときに native build properties を設定するためのプラグインです。

Prebuild とは、ネイティブアプリをコンパイルする前に、ネイティブコードを生成することを指します。

app.json ファイルに設定を追加する

プロジェクトのルートディレクトリにある app.json ファイルに設定を追加します。
expo のひとつ下の階層です。 配列の中に配列を入れるような形になります。

app.json
{
  "expo": {
    "plugins": [
      [
        "expo-build-properties",
        {
          "ios": {
            "useFrameworks": "static"
          }
        }
      ]
    ]
  }
}

useFrameworksというのは Podfile の設定のようです(自分は Pod はよく知らない)
use_frameworks! を設定すると、CocoaPodsに静的ライブラリ(コンパイル済のライブラリ?)を使うように指示できるらしい。

Static Libraries(静的ライブラリ)については、以下の StackOverFlow が詳しかったです。

Admob で広告コードを取得する

Admob を開きます。

左のメニューで、 アプリアプリを追加 をクリックします。

プラットフォームごとに広告の設定が必要です。ここでは iOS を選択します。

アプリはサポートされているアプリストアに登録されていますか? という質問については、既にアプリストアにリリース済の場合は はい、まだリリースされていない場合は いいえ を選択します。

アプリ名を入力して、 アプリを追加 ボタンをクリックします。すると、以下のような作業内容が表示されます。

  1. 広告ユニットを作成し、SDK の統合をテストしてください
  2. テストが完了したら、サポート対象のアプリストアにアプリを公開してください
  3. AdMob アプリにストア情報を追加してください。広告配信が可能な状態であるかチェックされます。通常、審査は数日で完了しますが、アプリの評価にそれ以上かかる場合もあります。

広告ユニットを追加

上の画面で「完了」をクリックすると、アプリの概要 に飛びます。間違ってブラウザを閉じた場合でも、対象のアプリを選択した上で、左メニューから アプリの概要 をクリックすればよいです。

広告ユニットを追加 をクリックします。広告のタイプを色々と選択できます。

  • バナー
  • インタースティシャル
  • リワードインタースティシャル
  • リワード
  • ネイティブ アドバンス
  • アプリ起動

自分のアプリでは バナー にしました。

作成すると、 AdMob アプリID と、 Admob広告ユニットIDが表示されます。これをコピペしておきます。

ca-app-pub-XXXXX~XXXXXXX となっているのが AdMobアプリIDで、
ca-app-pub-XXXX/XXXXX となっているのが AdMob 広告ユニットIDです。

app.json に react-native-google-mobile-ads の設定を追加

AdMobアプリIDの設定を app.json に追加します。注意すべき点は、 "expo" と同じ階層に配置する、という点です。

app.json
{
  "expo": {},
  "react-native-google-mobile-ads": {
    "android_app_id": "ca-app-pub-xxxxxxxx~xxxxxxxx",
    "ios_app_id": "ca-app-pub-xxxxxxxx~xxxxxxxx",
    "user_tracking_usage_description": "This identifier will be used to deliver personalized ads to you."
  }
}

広告用のコンポーネントの作成

広告を差し込みたい場所に上のコンポーネントを挿入します。

EmptyBannerAd を用意しておいて、開発の初期は __DEV__true のときに EmptyBannerAd を表示すれば、はかどります。
development build をしなくても EmptyBannerAd は表示できるので、広告の大きさを意識しながら開発できます。

自分は react-native-google-mobile-ads は開発の終盤で入れるようにしています。初期は EmptyBannerAd のみを表示させてます。

AdMobBanner.tsx
import { StyleSheet, View } from 'react-native';
import { BannerAd, BannerAdSize, TestIds } from 'react-native-google-mobile-ads';

const iosAdUnitId = 'ca-app-pub-6608697068906117/4057639475';
const adUnitId = __DEV__ ? TestIds.BANNER : iosAdUnitId;

function EmptyBannerAd() {
  return (
    <View style={styles.container}>
      <View style={styles.bannerAd}></View>
    </View>
  );
}

export default function AdMobBanner() {
  return (
    <BannerAd
      unitId={adUnitId}
      size={BannerAdSize.MEDIUM_RECTANGLE}
      requestOptions={{
        requestNonPersonalizedAdsOnly: true,
      }}
    />
  );
}

export const MEDIUM_RECTANGLE = {
  width: 300,
  height: 250,
};

const styles = StyleSheet.create({
  container: {
    justifyContent: 'center',
    alignItems: 'center',
  },
  bannerAd: {
    ...MEDIUM_RECTANGLE,
    backgroundColor: 'gray',
  },
});

App Store Connect に広告付きのスクリーンショットを上げると審査で落とされるので、スクリーンショットを撮るときは screenShotModetrue にして、空のバナーを出します(空白になります)

development-build の設定

react-native-google-mobile-ads のコンポーネントを導入して、 npx expo start --tunnel でシミュレーターを立ち上げると、以下のようなエラーが出ます。

console
 ERROR  Invariant Violation: Your JavaScript code tried to access a native module that doesn't exist. 

If you're trying to use a module that is not supported in Expo Go, you need to create a development build of your app. See https://docs.expo.dev/development/introduction/ for more info., js engine: hermes
 ERROR  Invariant Violation: "main" has not been registered. This can happen if:
* Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.
* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called., js engine: hermes

Expo Go でサポートされていないモジュールを使う場合は、 development build を作成しなければならない、と書かれています。

以下のコマンドを実行します。

shell
eas build --profile development --platform ios --local

上のコマンドが完了すると、ターミナルにQRコードが表示されるので、QRコードを端末のカメラで読み取ります。

すると、アプリがインストールされます。

次にターミナルで開発用のサーバを立ち上げます。

shell
npx expo start --dev-client --tunnel

コマンド実行後に表示されるQRコードを読み込めば、端末に広告付きの画面が表示されます。

デバイスに ipa ファイルをインストールできなかった場合は、以下のコマンドでデバイスを登録してください。

eas device:create

ローカルでビルドする場合は各種セットアップも事前にする必要があります。

xcode-select --install
brew install cocoapods
brew install fastlane

https://zenn.dev/tadaedo/scraps/6f989f2a104f76

シミュレーターで Admob の動作を確認する

eas.json に以下の設定を追加します。

eas.json
{
  "build": {
    "development-simulator": {
      "developmentClient": true,
      "distribution": "internal",
      "ios": {
        "simulator": true
      }
    }
  }
}

eas.json 全体は以下のようになります。

eas.json
{
  "cli": {
    "version": ">= 3.10.2",
    "promptToConfigurePushNotifications": false
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "development-simulator": {
      "developmentClient": true,
      "distribution": "internal",
      "ios": {
        "simulator": true
      }
    },
    "preview": {
      "distribution": "internal"
    },
    "production": {}
  },
  "submit": {
    "production": {
      "ios": {
        "metadataPath": "./store.config.json"
      }
    }
  }
}

次に、以下のコマンドを実行します。

shell
eas build --profile development-simulator --platform ios

Install and run the iOS build on a simulator? › と表示されたらそのまま Enter を押せば、シミュレーターに Development Build されたアプリがインストールされます。

複数のシミュレーターを立ち上げていて、別のシミュレーターにもインストールしたい場合は、ターミナルに表示される https://expo.dev/articacts/eas/xxxx.tag.gz をダウンロードして、解凍して、中にある xxxx.app をシミュレーターにドラッグ・アンド・ドロップすれば、シミュレーターにアプリがインストールされます。

npx expo start --dev-client --tunnel を実行して、 i を入力してシミュレーターを開けばOKです。

ユーザーにトラッキングの許可を要求する

広告を表示させるためには、「トラッキングによるデータ収集を許可しますか?」のようなメッセージを出さなければいけません。そのための手順は以下の記事にまとめています。

上のGDPRメッセージ自体は1度作れば使い回しができますが、新しいアプリを作るたびに、 Admob の管理画面上で、そのアプリにGDPRメッセージを表示させるようにチェックを入れる必要があります。

App.tsx に以下のように書けば、「次の表示される permission で “Allow” を選択してください」→「Allow “App Name” to track your activity accross other …” みたいなポップアップが表示されるようになります。

App.tsx
export default function App() {
  
  const requestConsent = async () => {
    try {
      const consentInfo = await AdsConsent.requestInfoUpdate({
        debugGeography: AdsConsentDebugGeography.EEA,
      });

      const status = consentInfo.status;
      if (
        (consentInfo.isConsentFormAvailable && status === AdsConsentStatus.UNKNOWN) ||
        (consentInfo.isConsentFormAvailable && status === AdsConsentStatus.REQUIRED)
      ) {
        await AdsConsent.showForm();
      }
    } catch (err) {
      console.log(err);
    }
  };

  useEffect(() => {
    requestConsent();
  }, []);

Admob の「プライバシーとメッセージ」で、「GDPR」と「IDFA説明メッセージ」を選択して、作成したアプリを追加するのを忘れないでください。

Admob 側でアプリを追加するのを忘れて、自分は2回リジェクトされました。リジェクトの内容は以下です。

Guideline 2.1 – Information Needed

We’re looking forward to completing our review, but we need more information to continue. Your app uses the AppTrackingTransparency framework, but we are unable to locate the App Tracking Transparency permission request when reviewed on iOS 17.0.1.

user_tracking_usage_descriptionを英語にしていてリジェクトされたとき

日本語のアプリで、トラッキングの許可を得るためのメッセージが英語のままにしてしまい、リジェクトされました。

We noticed an issue in your app that contributes to a lower-quality user experience than App Store users expect:

– Your app’s permissions requests are not written in the same language as your app’s Japanese localization. To help users understand why your app is requesting access to a specific feature, your app’s permission requests should be in the same language as your app’s current localization.

Since App Store users expect apps to be simple, refined, and easy to use, we want to call your attention to this design issue so you can make the appropriate changes.

添付してくれたスクリーンショットを見ると、たしかに部分的に英語があります。

原因は、 app.json に設定した "user_tracking_usage_description" が英語で書かれていたためです。

ここの文言を "user_tracking_usage_description": "サービス運営維持のため広告を表示させていただいています。個人の特定につながることはありませんのでご安心ください。" に変更して、再度レビューを依頼しました。