ITMS-90338「Non-public API usage」をExpoで直す方法
著者:Mehdi Azizi
公開・9 分
ITMS-90338は、Appleが非公開と判断するAPI名がビルドに含まれるエラーです。原因の依存関係を特定して更新し、再ビルドします。
ITMS-90338は、ビルドに対してAppleが行う自動チェックです。Appleが非公開(プライベート)とみなすAPI、つまりObjective-Cのメソッド、関数、クラスをバイナリが参照している、という意味です。直し方は、メールに書かれたファイルを特定し、その名前を含む依存関係までたどり、それを更新するか自分のメソッド名を変更して、再ビルドすることです。
Expoの場合、問題の名前はほぼ必ずネイティブライブラリから来ていて、JavaScriptが原因になることはまれです。2026年10月15日、僕はこのガイドの解析コマンドを、ミニゲーム付き目覚ましアプリSolys(Expo SDK 57)のシミュレータ用ビルドと、Xcode 26.2のiOS SDKで実行しました。ここでは、メールの読み方、原因の見つけ方、誤検知の見分け方を説明します。
ITMS-90338エラーの意味は?
メッセージの件名は「ITMS-90338: Non-public API usage」です。Appleがバイナリ内で何を見つけたかによって、3つのパターンがあります。
- 「references non-public selectors」:
boldFontOfSize:のようなObjective-Cのメソッド名 - 「references non-public symbols」:
__dyld_get_shared_cache_rangeのように先頭にアンダースコアが付いた関数 - 「contains or inherits from non-public classes」:クラス名
どのパターンでも、パスが1つ書かれ、その後に名前の一覧が続きます。本文には2つのアドバイスもあります。メソッド名がプライベートAPIと一致する場合は名前を変えること、そしてそれを含むスタティックライブラリを取り除くことです。2026年9月のメールでは、エンコードされたSwiftの名前を読めるようにする xcrun swift-demangle も案内されています。
このチェックの根拠はガイドライン2.5.1で、アプリは公開APIしか使えません。Apple Developer Programのライセンス契約も第3.3.1条で、プライベートAPIの呼び出しを禁止しています。このルールに関する人による審査はガイドライン2.5.1のガイドで詳しく解説しています。この記事ではアップロード時のエラーを扱います。
他のITMSエラーと同じく、誰もあなたのアプリを開いてはいません。ビルドの処理中にAppleがバイナリを解析したのです。AppleはプライベートAPIの一覧を公開していません。信頼できる一覧は、メールに書かれた名前だけです。
パスを読む:SDKか、自分の実行ファイルか
一覧の前に書かれたパスが、どのファイルを調べるべきかを教えてくれます。
Frameworks/Name.framework/Name:名前は埋め込まれた動的フレームワーク、つまり特定のSDKの中にあります。Solysのビルドでは、このフォルダに14個のフレームワークがあります。Hermes、React Native、コンパイル済みで配布されるExpoモジュール、画像ライブラリのSDWebImageです。- アプリ名だけ:名前はメインの実行ファイルの中にあります。Expoでは、ここに自分のネイティブコードと、ソースからコンパイルされたPodがまとめられ、デフォルトでは静的リンクされます。つまり、どのネイティブライブラリでも原因になり得ます。
フローチャート:指摘された名前から修正まで
上から順に見てください。自分のケースに最初に当てはまった行が手がかりです。
| 書かれている内容 | 調べる場所 | 修正 |
|---|---|---|
$s や _$s で始まる名前 | xcrun swift-demangle | 表を最初からやり直す |
| IPA内に見つからない | バンドルに grep | ビルドを再送信 |
| iOSで公開されている名前 | iOS SDKのヘッダー | ビルドを再送信 |
API_UNAVAILABLE(ios) | iOS SDKのヘッダー | パッケージを更新 |
Frameworks/X の中 | Xを提供するSDK | SDKを更新 |
| セレクタ、アプリ本体 | Podとネイティブモジュール | プレフィックス付与か更新 |
シンボル _name、アプリ本体 | nm -m、次にPod | 呼び出しを置き換え |
| クラス、アプリ本体 | 自分のクラスとPod | クラスにプレフィックス |
「ビルドを再送信」の行は誤検知を示します。後ほど説明します。
ステップ1:送信したビルドを開く
プロジェクトではなく、実際にAppleへ送信したファイルで作業します。expo.devのビルドページからIPAをダウンロードし、Macで次を実行します。
unzip -q MyApp.ipa -d ipa
ls ipa/Payload/MyApp.app/Frameworks
otool -L ipa/Payload/MyApp.app/MyApp | grep PrivateFrameworks
2行目は埋め込まれたフレームワークを一覧表示します。3行目はAppleのプライベートフレームワークへの直接リンクを探します。何も表示されないのが正常です。Solysでは、otool -L は実行ファイルにも14個のフレームワークにも、プライベートフレームワークを見つけませんでした。
ステップ2:名前を含むファイルを見つける
まずは広く探します。指摘された名前を、先頭のアンダースコアを除いてバンドル全体から検索します。
grep -rl "dyld_get_shared_cache_range" ipa/Payload/MyApp.app
このコマンドは、その文字列を含むファイルを一覧表示します。次に、見つかったファイル(ここでは Binaire と表記)に対して、名前の種類(セレクタ、シンボル、クラス)に応じて絞り込みます。
strings -a Binaire | grep -x "boldFontOfSize:"
nm -m Binaire | grep "_dyld_get_shared_cache_range"
nm -m Binaire | grep '_OBJC_CLASS_$_ClassName'
セレクタには nm ではなく strings を使ってください。 SolysのReactフレームワークでは、nm は -[RCTRootView initWithBridge:moduleName:initialProperties:] のようにクラス付きのメソッドを表示し、initWithBridge: というセレクタ単体は表示しません。strings なら見つかります。
シンボルの場合は、行の末尾を読みます。Solysでは、たとえば nm -m が (undefined) external __dyld_get_image_name (from libSystem) と表示します。バイナリはその関数を含まずに呼び出していて、libSystemが提供しているということです。この関数は、次のステップでわかるとおり公開APIです。
ステップ3:名前が本当にプライベートか確認する
iOS SDKのヘッダーには公開APIが記述されています。数秒の検索で、指摘された名前がそこにあるか、どのプラットフォームで使えるかがわかります。シンボルの場合は、まずコンパイル時に付くアンダースコアを外します。_SCDynamicStoreCreate は SCDynamicStoreCreate になります。
SDK=$(xcrun --sdk iphoneos --show-sdk-path)
grep -rn "SCDynamicStoreCreate" "$SDK/usr/include" "$SDK"/System/Library/Frameworks/*/Headers
2026年10月15日、iOS SDK 26.2で、実際のITMS-90338メールから取った4つの名前を調べた結果です。
| 指摘された名前 | iOS SDK内 | 解釈 |
|---|---|---|
| _dyld_get_shared_cache_range | なし | プライベートAPI |
| boldFontOfSize: | なし | プライベートか名前の衝突 |
| SCDynamicStoreCreate | API_UNAVAILABLE(ios) | macOS専用 |
| isMainFrame | WebKitのプロパティ | 公開 |
名前があり、iOSで利用可能なら誤検知の可能性が高いです。名前がなければ、プライベートAPIか名前の衝突です。 注意点が2つあります。Swift専用の公開APIはこれらのヘッダーに出てきません。また、setDelegate: のようなアクセサは、プロパティ delegate で検索してください。
ステップ4:パッケージまでたどる
パスがフレームワークを指している場合、その名前からPodへ、さらにnpmパッケージへたどれます。
grep -n "FrameworkName" ios/Podfile.lock
npm explain package-name
自分で追加した覚えがない場合、npm explain がどの依存関係によってインストールされたかを教えてくれます。
パスが実行ファイルを指している場合は、ネイティブのソースから、次にPodから名前を探します。
grep -rl --include='*.m' --include='*.mm' --include='*.swift' --include='*.h' \
"boldFontOfSize" node_modules modules
grep -rl "boldFontOfSize" ios/Pods
フォルダの一覧は環境に合わせてください。modules はローカルのExpoモジュール用のフォルダです。ios/Pods フォルダは npx expo prebuild -p ios の後に現れます。このコマンドはMacではPodのインストールも行います。注意:ios フォルダが作り直されることがあります。手で編集している場合は、コピーで作業してください。
ios/Pods での検索は、ソースの検索と同じくらい重要です。 Solysでは、このフォルダにコンパイル済みで配布される .xcframework 形式のフレームワークが15個あります。Expoモジュール、Hermes、React Native、サードパーティSDKです。ソースファイルだけを検索すると、これらのコードを見落とします。
よくある原因
古すぎる、または新しすぎるサードパーティSDK。 2026年9月、あるFlutter開発者がセキュリティSDKが原因のリジェクトを報告しました。そのバージョン8.2.3が __dyld_get_shared_cache_range を呼び出していたのです。8.2.2は通っていて、提供元は修正が出るまでそちらに戻すよう勧めました。
ドキュメント化されていないAPIを呼ぶネイティブモジュール。 自作のモジュールやプラグイン、小さなライブラリが、フォーラムで見つけたUIKitの内部メソッドを使っていることがあります。チェックはその名前で検出します。
名前の衝突。 自分のメソッドやクラスが、たまたまAppleのプライベートAPIと同じ名前になっているケースです。メッセージもこれを想定していて、名前の変更を求めています。Appleは、自社クラスに追加するメソッドにはプレフィックスを付けることを推奨しています。たとえば小文字3文字とアンダースコアです。
macOSでは公開だがiOSでは非公開のAPI。 2024年、ある.NET MAUIアプリが_SCDynamicStoreCreate でリジェクトされました。Appleはこの関数をドキュメント化していますが、macOS専用で、iOS SDKのヘッダーでは API_UNAVAILABLE(ios, tvos, watchos) と指定されています。
回避ではなく修正する
- 依存関係を更新します。 Expoが管理するパッケージなら、Expo CLIのドキュメントにあるとおり、
npx expo install --checkがSDKと合わないものを教えてくれます。それ以外はリリースノートを確認します。 - 前のバージョンに戻します。 新しいバージョンでその名前が入った場合です。提供元にも知らせましょう。
- 置き換えるか削除します。 メンテナンスされていないライブラリの場合です。
- 名前を変更します。 自分のメソッドやクラスに、プレフィックスを付けます。
名前を隠さないでください。 文字列を分割したり、回り道でメソッドを呼んだりしても、使用を隠すだけで修正にはなりません。ルール2.5.1と契約で禁止されたままで、リスクを負うのはあなたのアカウントです。
その後、新しいビルド番号で再ビルドします。リモートアップデートでは不十分です。名前はコンパイル済みのネイティブコードの中にあるからです。送信前に、新しいIPAでステップ2をもう一度実行してください。名前が出てこなければOKです。
誤検知の場合
Appleのチェックが間違うこともあります。2022年6月6日、React NativeやFlutterのアプリが、setNavigationDelegate: や isMainFrame といったWebKitの公開名でITMS-90338を受け取りました。ほとんど変更のないアップデートでも起きています。翌日、Appleのエンジニアが検証側で問題は解決したと書き、ビルドの再送信を促しました。このスレッドはExpoのGitHubリポジトリに残っています。
次の3つがそろえば、誤検知の可能性が高いです。
- ビルドが前回受理されたビルドとほぼ同じ
- ステップ2で名前が見つからない、またはステップ3でiOSの公開APIだとわかる
- 同じ日にAppleのフォーラムで他の開発者も同じエラーを報告している
その場合はAppleのシステムステータスを確認し、新しい番号でビルドを再送信します。それでもエラーが続くなら、メール全文を添えてAppleのデベロッパサポートに連絡してください。なお、名前の衝突はAppleから見れば誤検知ではありません。名前を変更しましょう。
次の送信の前に
他のITMSエラーにもガイドがあります。XcodeのバージョンはITMS-90725、プライバシーマニフェストはITMS-91053、署名はITMS-90034です。ビルドが受理されたら、審査の前にTestFlightでインストールしてみてください。
審査員によるリジェクトには、もう少し落ち着いた対応が必要です。手順はAppleにアプリをリジェクトされたときのページに、よくある理由はAppleがアプリをリジェクトする理由にまとめています。
送信前にプロジェクトを確認したいなら、Review Kitに僕のチェックリストと、プロジェクトをチェックするClaude Codeのスキルをまとめています。送信で行き詰まって人の目で見てほしいなら、僕との1時間セッションを予約してください。ビデオ通話で、あなたの画面を見ながらプロジェクトを一緒に確認します。初めてのリリースを準備中なら、無料の診断を受けてください。やるべきステップを順番に整理し、Appleのリジェクト対策チェックリストをお送りします。
よくある質問
ITMS-90338はApp Reviewによるリジェクトですか?
いいえ。ビルドの処理中、人による審査の前に行われる自動チェックです。弾かれたビルドはTestFlightにも審査にも届きません。後から審査員が見つけたプライベートAPIは、ガイドライン2.5.1の扱いになります。
ITMS-90338はリモートアップデートで直せますか?
いいえ。指摘された名前は、バイナリのコンパイル済みネイティブコードの中にあります。リモートアップデートで変わるのはJavaScriptとリソースだけです。依存関係を修正し、再ビルドしてから送信し直してください。
書いた覚えのないメソッドをAppleが指摘するのはなぜですか?
埋め込まれたライブラリから来ているか、名前の衝突だからです。依存関係がプライベートAPIと同じ名前のメソッドを宣言していることがあります。Appleが読むのはバイナリ内の名前で、ソースコードではありません。grep と npm explain でパッケージまでたどってください。
APIがiOSで公開されているかはどう確認しますか?
iOS SDKのヘッダーで名前を検索し、利用可能なプラットフォームを確認します。パスは xcrun --sdk iphoneos --show-sdk-path で取得できます。Appleのドキュメントページにも記載があります。macOS専用としてドキュメント化された関数は、iPhoneでは公開APIではありません。
修正後にビルド番号を変える必要はありますか?
はい。EASに自動で増やしてもらいましょう。同じバージョンで送信済みの番号は、ITMS-90189で弾かれることがあります。

