RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-05-07中級

Rorkで作ったiOSアプリで App Tracking Transparency のダイアログが出ない時の原因と対処法

Rorkで生成したiOSアプリでATT(App Tracking Transparency)のダイアログが表示されない問題を、Info.plist設定・呼び出しタイミング・AdMob連携の3つの観点から実機検証込みで解決します。

Rork515iOS108ATT9App Tracking Transparency3AdMob70IDFA2トラブルシューティング77

私自身、AdMobで広告収入を得る個人アプリを2014年から運営してきたのですが、Rorkで新しい広告付きアプリを作って実機で試した時、ATT(App Tracking Transparency)のダイアログが一度も出てこないことに気づきました。AdMobのコードは正しく入っているし、Info.plistにも見覚えのある設定を書いたつもりでしたが、何度起動してもあのおなじみの「Allow "アプリ名" to track your activity across other companies' apps and websites?」が表示されません。

コンソールにはエラーが出ない、ビルドも通る、AdMobの広告は表示される。それなのにIDFA(広告識別子)はゼロのまま、Firebase Analyticsの収益データも空欄のまま。「AppTrackingTransparency framework が動いていない」というのは、Rorkで生成されたコードでは意外なほど頻繁に起きる、見つけにくい不具合です。

ここでは私が実際にハマった3つの原因と、それぞれの修正手順を整理します。AdMobやFirebaseでマネタイズする方には、特に直結する内容かと思います。

原因①: Info.plist の NSUserTrackingUsageDescription が空・未設定

最も多いのがこのパターンです。Rorkは標準でATT用の説明文を app.json に書いてくれない場合があり、ビルド時に Info.plist が生成されても NSUserTrackingUsageDescription キー自体が抜け落ちることがあります。

iOS 14.5以降、このキーが Info.plist に存在しないアプリで requestTrackingAuthorization() を呼んでも、iOSはダイアログを表示せず、即座に .denied を返します。エラーは投げられないため、開発者は「呼び出しは成功した」と勘違いしやすい厄介な仕様です。

// app.json — ios.infoPlist セクションに必ず追加する
{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSUserTrackingUsageDescription": "あなたに合った広告を表示するため、この識別子の使用許可をお願いします。"
      }
    }
  }
}

説明文には実際の利用目的を、ユーザー目線で具体的に書きます。Appleの審査では「広告のため」とだけ書くと拒否されることもあるため、「あなたに合った広告」「より関連性の高いコンテンツのため」のような表現が安全です。

修正後は eas build または expo prebuild --clean で Info.plist を再生成します。私はこの「prebuild --clean」を忘れて2時間溶かしました。生成済みの ios/ フォルダがあるとそちらが優先され、app.json の変更が反映されないのです。

原因②: requestTrackingAuthorization() の呼び出しタイミングが早すぎる

ATTのダイアログは、アプリが完全にフォアグラウンドに来てからでないと表示されません。useEffect の中で何も考えずに呼び出すと、起動直後でアプリが「アクティブ」状態になる前に呼ばれてしまい、ダイアログが出ずに .notDetermined が返るだけで終わります。

これも本当に分かりにくいのですが、シミュレータでは正常に見える挙動が、実機では沈黙する形で失敗することが多いです。私の場合は、TestFlightでベータユーザーから「広告のダイアログが出ない」とフィードバックをもらって初めて気づきました。

// ✅ 正しい呼び出し方
import { useEffect, useRef } from "react";
import { AppState, Platform } from "react-native";
import {
  requestTrackingPermissionsAsync,
  getTrackingPermissionsAsync,
} from "expo-tracking-transparency";
 
export default function App() {
  const requestedRef = useRef(false);
 
  useEffect(() => {
    if (Platform.OS !== "ios") return;
 
    const subscription = AppState.addEventListener("change", async (state) => {
      if (state !== "active" || requestedRef.current) return;
      requestedRef.current = true;
 
      const current = await getTrackingPermissionsAsync();
      if (current.status === "undetermined") {
        const result = await requestTrackingPermissionsAsync();
        console.log("ATT result:", result.status);
        // 期待される出力: "granted" / "denied"
      }
    });
 
    return () => subscription.remove();
  }, []);
 
  return /* ... */;
}

ポイントは3つあります。AppStateactive を待つこと、一度だけ呼び出すこと(requestedRef)、そして既に決定済みなら再度呼ばないことです。Rorkに「ATTを実装して」と頼むと、たまに useEffect の中で直接呼び出すコードが生成されるので、上記のパターンに置き換えると安定します。

原因③: AdMob SDK の初期化順序が逆になっている

これがおそらく一番ハマりやすい落とし穴です。AdMob(Google Mobile Ads)は、SDKの初期化時にIDFAを取得しようとするため、ATTの結果が確定する前に初期化されると、その後に許可しても広告のパーソナライズが効かないケースがあります。

正しい順序は次の通りです。

  1. アプリ起動
  2. ATT ダイアログを表示し、ユーザーの選択を待つ
  3. 結果を確認した後で AdMob SDK を初期化する
import mobileAds from "react-native-google-mobile-ads";
import { requestTrackingPermissionsAsync } from "expo-tracking-transparency";
 
async function initAdsWithConsent() {
  if (Platform.OS === "ios") {
    const { status } = await requestTrackingPermissionsAsync();
    console.log("ATT decided:", status);
  }
  // ATT の結果が確定してから初期化することで、
  // パーソナライズ広告が正しく機能する
  await mobileAds().initialize();
  console.log("AdMob initialized");
}

Rorkに広告SDKの追加を依頼すると、しばしば mobileAds().initialize()App.tsx の最上部に置くコードが生成されます。これだとATTの結果を待たずに初期化されてしまうため、自分で順序を入れ替える必要があります。

テスト時の落とし穴: 一度決定すると再表示できない

ATTのダイアログは、ユーザーが一度「許可」または「拒否」を選ぶと、そのアプリでは二度と自動表示されません。開発中にこの仕様を忘れると、「修正したのにダイアログが出ない=直っていない」と勘違いします。

実機でリセットする手順は次の通りです。

  • 同じアプリで再テストしたい場合: 設定アプリ → プライバシーとセキュリティ → トラッキング → 該当アプリのトグルをオフ・オン
  • 完全にリセットしたい場合: 該当アプリをアンインストールして再インストール(ただしIDFAは保持されるため、完全な初期状態にはならない)
  • シミュレータの場合: メニューから Device → Erase All Content and Settings

私は最初これを知らず、コードを直すたびに「直ってない…」と1日ハマりました。実機の設定アプリから対象アプリをタップして、許可状態を直接確認するのが確実です。

デバッグ用: 現在の許可状態をログに出す

トラブルシューティング中は、起動のたびに現在のATT状態をログに残しておくと原因特定が早まります。

import { getTrackingPermissionsAsync } from "expo-tracking-transparency";
 
useEffect(() => {
  (async () => {
    const status = await getTrackingPermissionsAsync();
    console.log("=== ATT status ===");
    console.log("status:", status.status); // "granted" | "denied" | "undetermined"
    console.log("canAskAgain:", status.canAskAgain);
    console.log("expires:", status.expires);
    // 期待される出力例:
    // status: "undetermined"  → まだ聞いていない(リクエスト可能)
    // status: "denied" canAskAgain: false → ユーザーが拒否済み(再リクエスト不可)
  })();
}, []);

canAskAgain: false が返ってきたら、もうコード側からは表示できません。ユーザーに設定アプリでの操作をお願いするUIを用意するのが現実的な落とし所です。

私の経験からの提案

ATTの実装は、Rork の生成コードに任せきりにするのではなく、自分の手で app.json・呼び出しタイミング・SDK初期化順序の3点を必ず確認するクセをつけるのが安全です。私は今、Rorkで広告アプリを作るときには、まずこの3点だけを書いた最小サンプルを作って実機で動作確認してから、本番のロジックを足していくようにしています。

ATTの実装は地味で目立ちませんが、ここを正しく組んでおくとTik広告のCPMが体感で1.5〜2倍ほど変わってくることがあります。地道に整えるだけの価値はあります。

関連する記事として Rork でプッシュ通知が動かない時の対処法 や Rorkアプリでパーミッションが効かない時の解決ガイド も合わせて読んでいただけると、iOSのプライバシー関連の地雷を一通り回避できるかと思います。

最後までお付き合いいただき、ありがとうございました。同じ落とし穴で時間を溶かしている方の参考になれば嬉しいです。

シェア

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

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

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

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

開発ツール2026-04-21
Rork アプリの ATT 許可率を引き上げる実装パターン — iOS のトラッキング同意を広告収益につなげる
Rork で作った iOS アプリの App Tracking Transparency(ATT)許可率を高める実装パターンを、AdMob の広告収益を守る観点から具体的に解説します。purpose string の設計、呼び出しタイミング、プリパーミッション画面、cohort 別の効果計測、UMP(GDPR)との呼び出し順序、非パーソナライズ広告のフォールバックまで扱います。
開発ツール2026-04-30
Rork で iOS アプリ申請を通すための App Tracking Transparency 実装
Rork で作った iOS アプリが ATT(App Tracking Transparency)周りで App Store 審査に落ちないようにする実装手順を、Info.plist の説明文・プロンプトを出す位置・拒否されたときの後続フローまで含めて整理しました。
開発ツール2026-06-16
課金復元より先に広告を初期化したら、有料ユーザに一瞬広告が出た — Rork(Expo)アプリの起動順序設計
同意取得・ATT・広告初期化・課金復元・リモート設定を、起動の数百ミリ秒のあいだにどの順番で走らせるか。順序を1つ間違えるだけで、有料ユーザに広告が一瞬出たり、EEA で同意なしの計測が走ったりします。Rork で生成した Expo アプリの起動処理を1つのオーケストレータに畳み、競合を設計で潰す手順を実装メモとしてまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →