◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SONNET 4.5 — Claude API での提供終了は 11/30、残り27日。API 連携を足したアプリは移行先の確認を●SDK 58 — React Native 0.88 に更新されました。Android のリリース版で R8 が既定オンになり、iOS は SceneDelegate を生成します●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SONNET 4.5 — Claude API での提供終了は 11/30、残り27日。API 連携を足したアプリは移行先の確認を●SDK 58 — React Native 0.88 に更新されました。Android のリリース版で R8 が既定オンになり、iOS は SceneDelegate を生成します●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/開発ツール
⬡ 開発ツール/2026-06-16上級

課金復元より先に広告を初期化したら、有料ユーザに一瞬広告が出た — Rork(Expo)アプリの起動順序設計

同意取得・ATT・広告初期化・課金復元・リモート設定を、起動の数百ミリ秒のあいだにどの順番で走らせるか。順序を1つ間違えるだけで、有料ユーザに広告が一瞬出たり、EEA で同意なしの計測が走ったりします。Rork で生成した Expo アプリの起動処理を1つのオーケストレータに畳み、競合を設計で潰す手順を実装メモとしてまとめます。

Rork611Expo234AdMob75ATT9起動シーケンスRevenueCat30状態管理12

✦ プレミアム記事

Rork で生成した Expo アプリに広告と課金を入れたあと、TestFlight のレビュアーから「起動直後に一瞬だけバナーが出て、すぐ消える」という報告をもらったことがあります。課金済みのアカウントでした。再現は難しく、コールドスタート、つまりアプリを完全に終了してから初めて開いたときだけ起きます。

原因は機能のバグではなく、起動処理の順番でした。広告 SDK の初期化が先に終わり、最初のバナーが描画を始めたあとで、課金状態の復元が完了して「このユーザは広告非表示」と判定された。だから一瞬出てから消えたのです。

起動の数百ミリ秒のあいだには、同意取得・ATT の許可ダイアログ・広告 SDK の初期化・課金の復元・リモート設定の取得が同時に走ろうとします。それぞれは正しく実装されていても、走る順番が噛み合っていないと、お金を払った読者に広告を見せたり、EEA の読者に同意前の計測を走らせたりします。この記事は、その起動シーケンスを1つのオーケストレータに畳んで、順序の事故を設計で潰すための実装メモです。個人開発で6本のアプリを並行運用してきたなかで、ここを直列に整理し直したことが、起動まわりの不具合がいちばん減った転換点でした。

起動の数百ミリ秒で、何と何が競合するのか

コールドスタートで走らせたい初期化を並べると、だいたい次の5つになります。

  • ユーザ同意の取得(EEA なら UMP、GDPR/UMP の詳しい設定は別記事に譲ります)
  • ATT(App Tracking Transparency)の許可ダイアログ
  • 広告 SDK(AdMob / メディエーション)の初期化
  • 課金状態の復元(サブスク・買い切り・リワード時限解除)
  • リモート設定とフィーチャーフラグの取得

問題は、これらを useEffect のなかでそれぞれ独立に発火させると、終わる順番が実行ごとにばらつくことです。ネットワークが速い日は課金復元が先に終わり、遅い日は広告初期化が先に終わる。再現しないバグの典型で、私自身もしばらく「たまに起きる」で片付けていました。

順番がばらつくと困るのは、段と段のあいだに「依存」があるからです。広告は同意の後でなければ出してはいけない。広告を出すかどうかは課金状態が確定していなければ決められない。この依存を無視して並列に走らせると、依存先がまだ終わっていない一瞬に、依存元が動いてしまいます。冒頭のバナーちらつきは、まさにこの「課金復元が広告初期化に間に合わなかった一瞬」でした。

なぜ「同意 → ATT → 広告初期化」は崩せないのか

まず外せない直列の鎖が、同意と ATT と広告初期化の3つです。

EEA の読者に対しては、同意を取る前に計測(App Measurement)を走らせてはいけません。react-native-google-mobile-ads では、設定で delay_app_measurement_init を true にして、最初の広告リクエストまで計測の初期化を遅らせます。これをしないと、同意ダイアログを出しているそばから計測が始まってしまいます。

ATT は iOS のトラッキング許可です。AdMob の管理画面で ATT メッセージを設定しておくと、UMP の同意フローが ATT ダイアログも続けて出してくれるので、自前で順番を組まなくてもこの2つは1つの流れにまとまります。重要なのは、この同意・ATT の流れが完了してから MobileAds().initialize() を呼ぶことです。順番が逆だと、同意の結果を読む前に SDK が初期化され、パーソナライズの可否が正しく反映されません。

最初に同意と ATT をまとめて取り、それが解決してから広告 SDK を初期化する。この鎖は短いですが、絶対に並列化してはいけない部分です。コードにすると次のようになります。

import mobileAds from "react-native-google-mobile-ads";
import {
  AdsConsent,
  AdsConsentStatus,
} from "react-native-google-mobile-ads";
 
// 同意 + ATT を1つの流れで解決する。
// AdMob 側に ATT メッセージを設定しておくと、UMP が ATT ダイアログも続けて出す。
async function gatherConsentThenInitAds(): Promise<void> {
  try {
    // 1) 同意情報を更新し、必要なら同意フォーム(と ATT)を提示する
    const consentInfo = await AdsConsent.requestInfoUpdate();
    if (
      consentInfo.isConsentFormAvailable &&
      consentInfo.status === AdsConsentStatus.REQUIRED
    ) {
      await AdsConsent.showForm();
    }
  } catch (e) {
    // 同意フローが失敗しても起動は止めない。
    // 広告はパーソナライズなし扱いで続行する。
    console.warn("[bootstrap] consent flow failed, continuing non-personalized", e);
  }
 
  // 2) 同意・ATT が解決してから、初めて広告 SDK を初期化する
  await mobileAds().initialize();
}

ここで try/catch を広く取っているのは意図的です。同意フローはユーザのネットワークや地域に依存して失敗しうるので、失敗しても起動そのものは止めず、広告を「パーソナライズなし」で続行させます。起動シーケンスでは、各段の失敗が全体を巻き込まないように、段ごとに握りつぶす範囲を決めておくのが要点です。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦同意 → ATT → 広告初期化 → 課金復元 の順序を1つの bootstrap 関数に畳んだ、コピーして動かせる完全な実装コードを手に入れられる
✦「並列化してよい段」と「絶対に直列でなければならない段」を見分ける判断基準が分かり、起動時間を伸ばさずに順序事故を消せる
✦有料ユーザに広告が一瞬出る/EEA で同意前に計測が走る、という気づきにくい順序ミスを、再現条件つきで事前に塞げる
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

⬡ 開発ツール2026-06-12
Rork 製アプリに開発者デバッグメニューを実装する — 広告・課金・Remote Config の検証をリリース前に終わらせる設計
リリースビルドでしか再現しない広告・課金・Remote Config の不具合に毎回本番で向き合うのは消耗します。壁紙アプリ6本の運用で固まった、ストア提出ビルドには存在ごと残さない開発者デバッグメニューの設計と実装を、動くコードと落とし穴付きで共有します。
⬡ 開発ツール2026-04-24
Rork × AdMob UMP × ATT 同意管理の本番実装ガイド — グローバル配信で収益を落とさないための設計パターン
AdMobのUMPとATTをRorkアプリに正しく組み込み、GDPRに準拠しながら収益を落とさないための本番実装パターンです。3つの同意レイヤーと初期化順序、地域判定と分岐、同意状態のSDKへの伝播、設定画面からの撤回UI、詰まりやすい落とし穴6つを解説します。
⬡ 開発ツール2026-10-30
開発ビルドでは動くのに、Release の iOS だけ起動直後に落ちる — scheme を外した app.json が原因の場合の確かめ方
expo-router のアプリで app.json に scheme がないと、Release の iOS だけ起動直後に落ちることがあります。Debug と Android が無事な理由、直し方の二択、scheme を外したい場合の考え方、提出前に見つける確認手順を書き残します。