EXPOでATT許諾(iOS)が表示されない場合の対処法

2024/07/20に公開

はじめに

iOSにおいてApp Tracking Transparency(ATT)許諾プロンプトが表示されない問題について、解決方法を解説します。

問題

1. ios.infoPlist.NSUserTrackingUsageDescriptionに記載していない

ATT許諾プロンプトを表示するためには、app.json等でios.infoPlist.NSUserTrackingUsageDescriptionを記載する必要があります。
これがないとプロンプトが表示されません。

2. アプリがアクティブ状態でないとATT許諾プロンプトが表示されない

ATT許諾プロンプトは、アプリがアクティブな状態でなければ表示されません。したがって、アプリの初期化時にこのプロンプトを表示する場合、アプリがアクティブであることを確認する必要があります。

解決方法

1. NSUserTrackingUsageDescriptionの記載

まず、必要なパッケージをインストールします。

npx expo install expo-tracking-transparency

次に、app.json or app.config.js or app.config.ts のいずれかにNSUserTrackingUsageDescriptionを記載します。
expo-tracking-transparencyプラグインの設定も必要です(たぶん)

app.config.ts
export default (): ExpoConfig => ({
  // ...other
  ios: {
    infoPlist: {
      NSUserTrackingUsageDescription:
        "広告表示にのみ使用されます。許可しなくても機能制限等はございません。",
    },
  },
  plugins: [
    "expo-router",
    [
      "expo-tracking-transparency",
      {
        userTrackingPermission:
          "広告表示にのみ使用されます。許可しなくても機能制限等はございません。",
      },
    ],
  ],
});

2. アクティブ状態の確認と初期化

以下のカスタムフックを使用して、アプリがアクティブな状態であることを確認してからATT許諾プロンプトを表示します。

useAppInitialization
export const useAppInitialization = () => {
  // AppStateの現在の状態を保持するためのref
  const appState = useRef(AppState.currentState);
  // アプリがアクティブかどうかを管理するstate
  const [isActiveApp, setIsActiveApp] = useState<boolean>(
    appState.current === "active",
  );

  // アプリの状態が変更されたときに実行
  useEffect(() => {
    // AppStateの変更を監視するリスナーを追加
    const subscription = AppState.addEventListener("change", (nextAppState) => {
      appState.current = nextAppState; // 現在の状態を更新
      setIsActiveApp(appState.current === "active"); // アプリがアクティブかどうかを更新
    });

    return () => subscription.remove();
  }, []);

  // アプリが初期化されたかどうかを管理するstate
  const [isInitializedApp, setIsInitializedApp] = useState<boolean>(false);

  // アプリの初期化処理
  useEffect(() => {
    const initializeApp = async () => {
      try {
        // ATT許諾プロンプトを表示して許可を求める
        await requestTrackingPermissionsAsync();
        // 初期化が完了したことを示すためにstateを更新
        setIsInitializedApp(true);
      } catch (e) {
        console.warn(e);
      }
    };

    // アプリがアクティブな場合に初期化処理を実行
    if (isActiveApp) {
      initializeApp();
    }
  }, [isActiveApp]);

  return {
    isInitializedApp,
  };
};

以下のコードをapp/_layout.tsxに記載して、useAppInitializationフックを利用し、アプリの初期化が完了するまでコンポーネントのレンダリングを遅延させます。

app/_layout.tsx
export default function RootLayout() {
  const { isInitializedApp } = useAppInitialization();

  if (!isInitializedApp) {
    return null;
  }

  return (
    <ThemeProvider>
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
        <Stack.Screen name="+not-found" />
      </Stack>
    </ThemeProvider>
  );
}

おわりに

以上の手順を踏むことで、ATT許諾プロンプトが正しく表示されるようになります。

参考

Discussion