Rork Max の「2クリックで App Store へ」という説明を、最初は半信半疑で読んでいました。実際に触ってみると、コード署名もプロビジョニングプロファイルも意識しないうちにビルドが上がっていきます。ところが App Store Connect を開くと、上がってきたビルドの横に黄色い警告が付いていました。「Missing Compliance」です。
暗号化に関する輸出コンプライアンスの申告が済んでいない、という意味の警告になります。ここに答えないとそのビルドは TestFlight にも配信できませんし、審査にも出せません。厄介なのは、放置するとビルドを上げるたびに毎回聞かれる 点です。2クリックで公開できるはずが、提出のたびにブラウザで同じ質問に答える作業が挟まります。
私は AdMob と課金を載せた壁紙アプリを個人開発で運用していて、この質問には長らく「その場でボタンを押す」対応をしていました。設定を1つ入れれば二度と出ないと知ったのは、恥ずかしながらかなり後のことです。
この警告が聞いているのは「独自の暗号を足したか」
最初につまずくのは、質問文の読み方です。「あなたのアプリは暗号化を使用していますか」と聞かれると、HTTPS で通信している以上は YES だと考えたくなります。私も最初はそう答えて、そのまま書類の提出を求められる画面まで進んでしまいました。
Apple のドキュメントを読むと、判断の軸はそこにありません。Complying with Encryption Export Regulations には、OS に組み込まれた暗号化 — たとえば URLSession 経由の HTTPS 接続 — は書類提出の要件から免除される、と書かれています。免除されないのは、独自の暗号を自分で足した場合です。
つまりこの質問は「暗号を使っているか」ではなく、「Apple が用意した以外の暗号を、あなたが持ち込んだか 」を聞いています。ここが分かると判断はかなり単純になります。
免除に当たるかを3点で切り分ける
自分のアプリを次の3点で見ていくと、ほとんどの場合は数分で結論が出ます。
アプリがやっていること 該当する分類 申告
URLSession / fetch による HTTPS 通信のみOS 標準の暗号(免除) false
Keychain / SecureStore への保存、Data Protection 標準 API 経由(免除) false
暗号化を一切使っていない 非該当 false
独自アルゴリズムの実装、自作の E2E プロトコル 非免除 true
アプリ内で実装した VPN トンネル、非標準暗号の金庫機能 非免除 true
Rork で生成される多くのアプリ — API を叩いてデータを表示し、認証トークンを SecureStore に置き、AdMob や課金 SDK を組み込む構成 — は、上の表の上3行に収まります。Rork Max がネイティブ Swift 側で HealthKit や Core ML に踏み込んだ場合も、暗号という観点では同じです。標準 API を呼んでいる限り、持ち込んだ暗号にはあたりません。
判断に迷ったときの私の基準は「自分で暗号のコードを書いたかどうか」です。書いていないなら false で問題ありません。
Rork(React Native / Expo)側で恒久的に消す
Rork 本体は Expo(React Native)が土台になっています。この場合は app.json(または app.config.js)に1行入れるだけで済みます。
{
"expo" : {
"name" : "My Wallpaper App" ,
"slug" : "my-wallpaper-app" ,
"ios" : {
"bundleIdentifier" : "net.example.mywallpaper" ,
"config" : {
// false = 非免除の暗号を含まない(HTTPS / Keychain のみ)
// ここが ipa 内の Info.plist の ITSAppUsesNonExemptEncryption になります
"usesNonExemptEncryption" : false
}
}
}
}
ios.config.usesNonExemptEncryption は、Expo の設定ドキュメント にある通り、スタンドアロン ipa の Info.plist に ITSAppUsesNonExemptEncryption を書き込むためのフィールドです。値を入れたら、ネイティブ側に反映させてからビルドし直します。
# 1. ネイティブプロジェクトへ設定を反映(ios/ ディレクトリを再生成)
npx expo prebuild --platform ios --clean
# 2. 反映されたか、生成された Info.plist を直接確認する
/usr/libexec/PlistBuddy -c "Print :ITSAppUsesNonExemptEncryption" \
ios/MyWallpaperApp/Info.plist
# 期待する出力: false
# 3. ビルドして提出
eas build --platform ios --profile production
eas submit --platform ios --latest
手順2を飛ばさないことをお勧めします。次の節で触れますが、設定したつもりで反映されていないケースがいちばん時間を溶かします。
Rork Max(ネイティブ Swift)側で恒久的に消す
Rork Max は React Native ではなくネイティブ Swift を生成するため、app.json は存在しません。Info.plist に直接キーを置きます。両者の違いそのものについては Rork と Rork Max の違いを整理した記事 が参考になります。
<!-- Info.plist -->
<? xml version = "1.0" encoding = "UTF-8" ?>
<! DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
< plist version = "1.0" >
< dict >
< key >CFBundleShortVersionString</ key >
< string >2.1.0</ string >
<!-- 非免除の暗号を含まない場合。App Store Connect の質問が出なくなります -->
< key >ITSAppUsesNonExemptEncryption</ key >
< false />
</ dict >
</ plist >
Rork Max のプロジェクト画面から Info.plist を編集できない場合は、エディタ側にプロンプトで指示するのが早い場面もあります。「Info.plist に ITSAppUsesNonExemptEncryption を false で追加してください」と伝えると、該当キーだけを足した差分が返ってきます。生成されたコードを後から自分で保守できる形にしておく、という観点でも、この程度の粒度の指示は覚えておいて損がありません。
なお、この設定はビルドのメタデータに影響するだけで、アプリの挙動は1バイトも変わりません。安心して入れてください。
設定したのにまだ聞かれるとき
ここからが本題に近い部分です。設定を入れたのに警告が消えない、という相談をよく見かけます。原因はだいたい次の3つに絞られます。
① prebuild を通していない、または古いキャッシュでビルドされた
app.json を書き換えただけでは、既に生成済みの ios/ 配下の Info.plist は更新されません。npx expo prebuild --clean を通すか、EAS 側でキャッシュを無効化して再ビルドします。
# キャッシュを使わずにクリーンビルド
eas build --platform ios --profile production --clear-cache
② ios.config と ios.infoPlist を取り違えている
Expo には Info.plist へ任意のキーを流し込む ios.infoPlist というフィールドもあり、混同しやすい箇所です。どちらでも結果として同じキーが書かれますが、両方に別々の値を書くと打ち消し合います。片方に寄せてください。
{
"expo" : {
"ios" : {
// ❌ 両方に書かない。config を使うなら infoPlist 側からは消す
"config" : { "usesNonExemptEncryption" : false },
"infoPlist" : { "ITSAppUsesNonExemptEncryption" : false }
}
}
}
③ ビルドツール側で設定が読まれない既知のケースを踏んでいる
ios.config.usesNonExemptEncryption が EAS のビルドで参照されず、対話的なプロンプトが出てしまう挙動が報告されています 。この落とし穴を回避する確実な方法は、設定の反映を信じずに、生成物である ipa の中を実際に見に行くことです。手順は後述します。
App Store Connect 側で既に「毎回聞かれる」状態が固定化してしまった場合は、対象アプリの「App 情報」→「輸出コンプライアンスに関する書類」から回答を一度リセットできます。ただし Info.plist に正しいキーが入っていれば、次のビルドからは質問自体が出ません。
true を選んだ瞬間に増える義務
ここは誤って true を選ばないための予防知識として、把握しておく価値があります。
独自暗号を持ち込んでいて true に該当する場合、Apple に書類を出すか、米国の Bureau of Industry and Security(BIS)へ年次の自己分類レポートを提出する義務が発生します。レポートの期限は毎年2月1日です。加えて、暗号の種類によっては BIS からの正式な分類(CCATS)や暗号登録(ERN)が必要になる場合があり、App Store Connect のヘルプ にその判断が開発者側の責任である旨が明記されています。
さらに、フランスの App Store で配信する場合はフランス政府向けの暗号申告書が別途必要になります。安全な保存・安全な通信・アンチウイルスに該当するアプリが主な対象です。
個人開発でここまでの手続きを踏む価値があるのは、独自の E2E 暗号がプロダクトの核になっている場合くらいだと私は考えています。Apple Developer Program の $99/年 とは別に、書類作業の時間が丸ごと乗ってくるからです。逆に言えば、標準 API で足りる設計にしておけば、この領域には一切足を踏み入れずに済みます。Keychain と HTTPS で要件が満たせるなら、そちらを選ぶ判断を私はお勧めします。
提出前に ipa の中身を1コマンドで確かめる
設定ファイルではなく成果物を見る、というのがこの問題の再発防止策になります。ビルドが完了したら、ipa を展開して Info.plist を直接読みます。
#!/usr/bin/env bash
# check-encryption-key.sh — 提出前に ipa の申告キーを検証する
set -euo pipefail
IPA_PATH = " ${1 :? usage : . / check-encryption-key . sh < path-to . ipa > } "
TMP_DIR = "$( mktemp -d )"
trap 'rm -rf "$TMP_DIR"' EXIT
unzip -q " $IPA_PATH " -d " $TMP_DIR "
PLIST = "$( find " $TMP_DIR /Payload" -maxdepth 2 -name Info.plist | head -1 )"
if [ -z " $PLIST " ]; then
echo "❌ Info.plist が見つかりません: $IPA_PATH "
exit 1
fi
# バイナリ plist の場合があるため XML に変換してから読む
plutil -convert xml1 " $PLIST " -o " $TMP_DIR /out.plist"
if grep -q "ITSAppUsesNonExemptEncryption" " $TMP_DIR /out.plist" ; then
VALUE = "$( /usr/libexec/PlistBuddy -c \
'Print :ITSAppUsesNonExemptEncryption' " $TMP_DIR /out.plist")"
echo "✅ 申告キーあり: ITSAppUsesNonExemptEncryption = ${ VALUE }"
else
echo "⚠️ 申告キーが埋まっていません。App Store Connect で毎回質問されます"
exit 1
fi
$ chmod +x check-encryption-key.sh
$ ./check-encryption-key.sh ~/Downloads/build-2.1.0.ipa
✅ 申告キーあり: ITSAppUsesNonExemptEncryption = false
Mac が手元にない構成で Rork Max のクラウドビルドを使っている場合は、ipa をダウンロードせずに TestFlight の処理完了を待ち、App Store Connect のビルド一覧に警告アイコンが出ないことをもって代替の確認とする運用でも実用上は足ります。Xcode を介さない公開で抜け落ちる事前確認については、提出前の自己点検を取り戻す記事 にほかの項目もまとめてあります。
提出フローに1回だけ組み込む
この設定は、アプリ1本につき生涯で1回入れれば終わります。それだけに、新規プロジェクトを作った直後 — バージョン番号やアイコンを整えるのと同じタイミング — に済ませてしまうのが結局いちばん安く付きます。
次にやることとして、いま手元で動いている Rork プロジェクトの app.json(Rork Max なら Info.plist)を開いて、usesNonExemptEncryption の行があるかを確認してみてください。無ければ1行足して prebuild を通すところまでで、今日の作業は終わりです。公開までの全体の流れは2クリック公開の仕組みを追った記事 に譲ります。
提出のたびに出る小さな警告ほど、後回しにされたまま積み上がっていくものはありません。私自身、こうした細部を1つずつ潰していくことでリリースの心理的な重さが軽くなった実感があります。お読みいただきありがとうございました。