# ITMS-90338「Non-public API usage」をExpoで直す方法

> ITMS-90338は、Appleが非公開と判断するAPI名がビルドに含まれるエラーです。原因の依存関係を特定して更新し、再ビルドします。

元のページ：[ITMS-90338「Non-public API usage」をExpoで直す方法](https://devoria.fr/ja/blog/itms-90338-non-public-api-usage-expo)
著者：[Mehdi Azizi](https://devoria.fr/ja/mehdi-azizi)
公開日：2026-11-04
内容の最終更新：2026-11-04


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](https://developer.apple.com/app-store/review/guidelines/#2.5.1)で、アプリは公開APIしか使えません。[Apple Developer Programのライセンス契約](https://developer.apple.com/support/terms/apple-developer-program-license-agreement/)も第3.3.1条で、プライベートAPIの呼び出しを禁止しています。このルールに関する人による審査は[ガイドライン2.5.1](https://devoria.fr/ja/blog/guideline-2-5-1-public-api-sdk-expo)のガイドで詳しく解説しています。この記事ではアップロード時のエラーを扱います。

他の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で次を実行します。

```bash
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：名前を含むファイルを見つける

まずは広く探します。指摘された名前を、先頭のアンダースコアを除いてバンドル全体から検索します。

```bash
grep -rl "dyld_get_shared_cache_range" ipa/Payload/MyApp.app
```

このコマンドは、その文字列を含むファイルを一覧表示します。次に、見つかったファイル（ここでは `Binaire` と表記）に対して、名前の種類（セレクタ、シンボル、クラス）に応じて絞り込みます。

```bash
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` になります。

```bash
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パッケージへたどれます。

```bash
grep -n "FrameworkName" ios/Podfile.lock
npm explain package-name
```

自分で追加した覚えがない場合、`npm explain` がどの依存関係によってインストールされたかを教えてくれます。

パスが実行ファイルを指している場合は、ネイティブのソースから、次にPodから名前を探します。

```bash
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が原因のリジェクトを報告](https://github.com/talsec/Free-RASP-Flutter/issues/236)しました。そのバージョン8.2.3が `__dyld_get_shared_cache_range` を呼び出していたのです。8.2.2は通っていて、提供元は修正が出るまでそちらに戻すよう勧めました。

**ドキュメント化されていないAPIを呼ぶネイティブモジュール。** 自作のモジュールやプラグイン、小さなライブラリが、フォーラムで見つけたUIKitの内部メソッドを使っていることがあります。チェックはその名前で検出します。

**名前の衝突。** 自分のメソッドやクラスが、たまたまAppleのプライベートAPIと同じ名前になっているケースです。メッセージもこれを想定していて、名前の変更を求めています。Appleは、[自社クラスに追加するメソッドにはプレフィックスを付ける](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html)ことを推奨しています。たとえば小文字3文字とアンダースコアです。

**macOSでは公開だがiOSでは非公開のAPI。** 2024年、ある.NET MAUIアプリが[`_SCDynamicStoreCreate` でリジェクトされました](https://github.com/dotnet/runtime/issues/103949)。Appleはこの関数をドキュメント化していますが、[macOS専用](https://developer.apple.com/documentation/systemconfiguration/1437828-scdynamicstorecreate)で、iOS SDKのヘッダーでは `API_UNAVAILABLE(ios, tvos, watchos)` と指定されています。

## 回避ではなく修正する

1. **依存関係を更新します。** Expoが管理するパッケージなら、[Expo CLIのドキュメント](https://docs.expo.dev/more/expo-cli/)にあるとおり、`npx expo install --check` がSDKと合わないものを教えてくれます。それ以外はリリースノートを確認します。
2. **前のバージョンに戻します。** 新しいバージョンでその名前が入った場合です。提供元にも知らせましょう。
3. **置き換えるか削除します。** メンテナンスされていないライブラリの場合です。
4. **名前を変更します。** 自分のメソッドやクラスに、プレフィックスを付けます。

**名前を隠さないでください。** 文字列を分割したり、回り道でメソッドを呼んだりしても、使用を隠すだけで修正にはなりません。ルール2.5.1と契約で禁止されたままで、リスクを負うのはあなたのアカウントです。

その後、新しい[ビルド番号](https://devoria.fr/ja/blog/app-store-connect-build-number-version)で再ビルドします。リモートアップデートでは不十分です。名前はコンパイル済みのネイティブコードの中にあるからです。送信前に、新しいIPAでステップ2をもう一度実行してください。名前が出てこなければOKです。

## 誤検知の場合

Appleのチェックが間違うこともあります。2022年6月6日、React NativeやFlutterのアプリが、`setNavigationDelegate:` や `isMainFrame` といったWebKitの公開名でITMS-90338を受け取りました。ほとんど変更のないアップデートでも起きています。翌日、Appleのエンジニアが検証側で問題は解決したと書き、ビルドの再送信を促しました。このスレッドは[ExpoのGitHubリポジトリ](https://github.com/expo/expo/issues/17768)に残っています。

次の3つがそろえば、誤検知の可能性が高いです。

- ビルドが前回受理されたビルドとほぼ同じ
- ステップ2で名前が見つからない、またはステップ3でiOSの公開APIだとわかる
- 同じ日にAppleのフォーラムで他の開発者も同じエラーを報告している

その場合は[Appleのシステムステータス](https://developer.apple.com/system-status/)を確認し、新しい番号でビルドを再送信します。それでもエラーが続くなら、メール全文を添えて[Appleのデベロッパサポート](https://developer.apple.com/contact/)に連絡してください。なお、名前の衝突はAppleから見れば誤検知ではありません。名前を変更しましょう。

## 次の送信の前に

他のITMSエラーにもガイドがあります。Xcodeのバージョンは[ITMS-90725](https://devoria.fr/ja/blog/itms-90725-sdk-version-issue-expo)、プライバシーマニフェストは[ITMS-91053](https://devoria.fr/ja/blog/itms-91053-missing-api-declaration-expo)、署名は[ITMS-90034](https://devoria.fr/ja/blog/itms-90034-missing-invalid-signature-fix)です。ビルドが受理されたら、審査の前に[TestFlight](https://devoria.fr/ja/blog/testflight-iphone-app-beta-testing)でインストールしてみてください。

審査員によるリジェクトには、もう少し落ち着いた対応が必要です。手順は[Appleにアプリをリジェクトされたとき](https://devoria.fr/ja/expertise/app-rejected-by-apple)のページに、よくある理由は[Appleがアプリをリジェクトする理由](https://devoria.fr/ja/blog/apple-app-rejection-reasons-fixes)にまとめています。

送信前にプロジェクトを確認したいなら、[Review Kit](https://devoria.fr/ja/kits/app-review)に僕のチェックリストと、プロジェクトをチェックするClaude Codeのスキルをまとめています。送信で行き詰まって人の目で見てほしいなら、[僕との1時間セッションを予約](https://devoria.fr/ja/coaching)してください。ビデオ通話で、あなたの画面を見ながらプロジェクトを一緒に確認します。初めてのリリースを準備中なら、[無料の診断を受けてください](https://devoria.fr/ja/diagnostic)。やるべきステップを順番に整理し、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で弾かれることがあります。


## あなたのプロジェクト

[Review Kit](https://devoria.fr/ja/kits/app-review)：私のApp Store審査チェックリスト、修正済みのAppleのリジェクト事例、Google Play用チェックリスト、Claude Codeのスキル3つ。￥8,500。

[iOS & Android Kit](https://devoria.fr/ja/kits/ios)：Claude CodeでiPhoneとAndroidのアプリを作りましょう。私のアプリのスターターコード、ストア公開までの手順、すべてのキットが揃っています。￥17,500。

[診断：あなたとiPhoneアプリの間にあるもの](https://devoria.fr/ja/diagnostic)：7つの質問、60秒。iPhoneアプリの公開を本当に妨げているもの、4ステップのロードマップ、あなたに合ったキットがわかります。
