個人でアプリを作って12年になりますが、「開発中は動いていたのに、本番リリース直前で詰まる」経験はいまだに毎回あります。Rork のようにAIがコードを生成してくれる環境では、さらに独特の落とし穴があります。生成されたコードが一見正しそうに見えても、本番環境特有の条件で破綻するケースがあるからです。
ここでは私が Rork で作ったアプリをストア配信するたびに遭遇した6つの具体的な詰まりポイントと、それぞれの回避策を共有します。公式ドキュメントにはまず載っていない、実際に踏み抜いた経験から得た知見です。
ポイント1: プレビュー環境と本番環境でのAPIキーの扱い
Rork のプレビュー環境では EXPO_PUBLIC_ 付きの環境変数が便利に使えます。しかし、AdMobのユニットIDやStripeの公開鍵などを EXPO_PUBLIC_ で管理していると、本番ビルド時に値が差し替わっていないまま公開されることがあります。
私は過去にAdMobのテスト用IDをそのまま本番に配信してしまい、気づいたのは翌日の朝、「収益がゼロのまま」というGoogle Analyticsの警告を見たときでした。
対策: 本番ビルド前に、必ず以下を確認します。
# ビルド前のチェック
grep -rn "ca-app-pub-3940256099942544" src/ # AdMobテストID
grep -rn "pk_test_" src/ # Stripeテスト鍵
grep -rn "localhost" src/ # 開発サーバーURLこの3つのgrepで引っかかる文字列は、本番ビルドに絶対に含めてはいけません。CI パイプラインに組み込んで自動検出できるようにすると事故を防げます。
ポイント2: Permissionダイアログの文言がローカライズされていない
Rork が生成するコードは、iOS の Info.plist に相当する権限説明文を英語のまま置いていることがよくあります。日本のストアに配信する場合、審査で落ちることはありませんが、ユーザー体験としては致命的です。
さらに厄介なのは、iOS では権限説明文が空文字列や空白のみだとアプリクラッシュの原因になる点です。プレビュー環境では問題なくても、本番ビルドで発覚します。
対策: 以下のような設定を app.json で明示的に用意します。
{
"expo": {
"ios": {
"infoPlist": {
"NSCameraUsageDescription": "写真を撮影して作品を記録するために使用します",
"NSPhotoLibraryUsageDescription": "作品の画像を選択するために使用します",
"NSLocationWhenInUseUsageDescription": "近くのアートスポットをご案内するために使用します"
}
}
}
}説明文は必ず「なぜ必要か」をユーザー視点で書きます。「写真を使用します」ではなく「作品を記録するために」のように、目的と利益を明示します。Apple の審査ではこの表現の質がチェックされます。
ポイント3: スプラッシュ画面が数秒固まる問題
Rork のデフォルトのスプラッシュ画面設定のまま本番ビルドすると、アプリ起動から最初の画面表示までの間、スプラッシュが長く表示される問題に遭遇します。これは多くの場合、useFonts や画像プリロードが同期的に処理されていることが原因です。
私は最初、「Rork が遅いのかな」と勘違いしていましたが、実際は自分の書いたローディング処理が悪かったのでした。
対策: 起動時の非同期処理を並列化し、スプラッシュを SplashScreen.preventAutoHideAsync() で明示的に制御します。
import * as SplashScreen from 'expo-splash-screen';
import { useEffect, useState } from 'react';
SplashScreen.preventAutoHideAsync();
export default function App() {
const [ready, setReady] = useState(false);
useEffect(() => {
async function prepare() {
try {
await Promise.all([
loadFonts(),
loadInitialData(),
preloadImages(),
]);
} finally {
setReady(true);
await SplashScreen.hideAsync();
}
}
prepare();
}, []);
if (\!ready) return null;
return <RootNavigator />;
}複数の初期化処理を Promise.all で並列化するだけで、起動時間が半分以下になることもあります。
ポイント4: iOS の Safe Area と Android の戻るボタン
Rork が生成するレイアウトは、シミュレータでは美しく表示されても、実機の iPhone で上下のSafe Areaに食い込んだり、Android の戻るボタン処理が抜けていたりすることが頻繁にあります。
特に Android の戻るボタンは、Web 開発の感覚ではつい忘れがちです。モーダル画面を開いたまま戻るボタンを押すとアプリが終了してしまう、というバグは審査では通ってしまうのでユーザーから苦情が来るまで気づきません。
対策: Safe Area は SafeAreaView ではなく react-native-safe-area-context の useSafeAreaInsets を使い、戻るボタンは画面ごとに BackHandler.addEventListener を明示的に実装します。
useEffect(() => {
const sub = BackHandler.addEventListener('hardwareBackPress', () => {
if (modalVisible) {
setModalVisible(false);
return true;
}
return false;
});
return () => sub.remove();
}, [modalVisible]);面倒に感じますが、Android ユーザーの評価に直結する部分です。
ポイント5: AdMob の ATT(App Tracking Transparency)対応
iOS 14以降、AdMobを使うアプリは ATT のダイアログを適切に出さないと、広告の配信率が極端に下がります。Rorkが生成するコードにはATT対応が含まれていないことがあり、そのまま配信すると収益が想定の半分以下になることがあります。
私は最初に出したアプリで、AdMobのeCPMが公表値の1/3しかなく、2週間気づきませんでした。原因は ATT を一切出していなかったためです。
対策: expo-tracking-transparency を使い、初回起動時にATTダイアログを表示します。
import { requestTrackingPermissionsAsync } from 'expo-tracking-transparency';
async function initTracking() {
const { status } = await requestTrackingPermissionsAsync();
if (status === 'granted') {
// パーソナライズド広告が配信される
} else {
// 非パーソナライズド広告の設定に切り替え
}
}そして app.json の infoPlist に NSUserTrackingUsageDescription を追加することを忘れないでください。この設定がないと ATT ダイアログ自体が出ません。
ポイント6: ストア審査で「Guideline 4.3 Spam」として落ちる
これは Rork 特有ではなく個人開発全般の話ですが、AIで生成されたアプリは「似たようなアプリが世の中に既に存在する」と判定されやすく、Apple の審査で Guideline 4.3(Spam)として落とされるケースが増えています。
対策: 私は以下の3点を必ず実装するようにしています。
- アプリ独自の機能を最低1つ、他にはない形で実装する
- スクリーンショットに「このアプリでしか得られない体験」が一目で分かる要素を入れる
- App Store の説明文に、作者の独自の視点や作成意図を3行以上書く
テンプレートから生成しただけのアプリは、審査員に即座に見抜かれます。AIで高速に作ることと、独自性を持たせることは両立しなければいけません。
リリース後に備える設計
最後に、リリース「後」の話を少しだけ。アプリが公開されたら、必ず以下の仕組みを事前に入れておきます。
クラッシュレポートの自動収集
Sentry などを使ってクラッシュを自動収集する仕組みを入れます。ユーザーはアプリが落ちても報告してくれません。エラーログなしで原因を探すのは不可能に近いです。
緊急メンテナンス告知の仕組み
アプリ内に Firebase Remote Config などを使った「お知らせバナー」を仕込んでおき、緊急時にサーバーからアプリに通知できるようにします。バグが発覚してから審査を通してアップデートを配信するまでには最短でも1日かかります。その間、ユーザーに状況を伝える手段は絶対に必要です。
最新バージョン強制アップデートの仕組み
特にStripe課金やAPIキーを使っているアプリは、古いバージョンを使い続けられると運用上の問題が発生します。バージョン番号をサーバーで管理し、一定以下のバージョンではアプリを起動時にアップデート画面を出す、という設計を最初から入れておきます。
次にやってみてほしいこと
もしあなたが今、Rork で作ったアプリをリリース直前の段階で持っているなら、この記事を閉じる前に1つだけ試してほしいことがあります。プロジェクトのルートで grep -rn "ca-app-pub-3940256099942544\|pk_test_\|localhost" src/ を実行してみてください。何も出なければ、この記事のポイント1はクリアです。何かが出たら、本番配信前に必ず差し替えてください。
AIがコードを書いてくれる時代になっても、本番にアプリを出す責任は開発者にあります。その責任を負うための小さなチェックリストを、自分の中に1つずつ増やしていきましょう。