ITMS-90338 修正:Expo 的「Non-public API usage」
作者:Mehdi Azizi
發布於 ,9 分鐘
ITMS-90338:你的 build 引用了 Apple 視為私有的方法、函式或類別。找出相關依賴,更新後重新 build。
ITMS-90338 是 Apple 對你的 build 做的自動檢查。它表示你的二進位檔引用了 Apple 視為私有的 API:Objective-C 方法、函式或類別。修正方法:找出 e-mail 裡提到的檔案,追到含有該名稱的依賴,更新它或重新命名你的方法,然後重新 build。
用 Expo 的話,這個名稱幾乎都來自某個原生函式庫,很少來自你的 JavaScript。2026 年 10 月 15 日,我在 Solys(我的小遊戲鬧鐘,Expo SDK 57)的模擬器 build 上,以及 Xcode 26.2 的 iOS SDK 上,跑過這篇指南裡的分析指令。下面告訴你怎麼讀 e-mail、找出元凶,以及辨認誤判。
ITMS-90338 錯誤是什麼意思?
訊息標題是「ITMS-90338: Non-public API usage」。依 Apple 在你的二進位檔裡找到的東西,有三種版本:
- 「references non-public selectors」:Objective-C 方法名稱,例如
boldFontOfSize:; - 「references non-public symbols」:函式,前面帶底線,例如
__dyld_get_shared_cache_range; - 「contains or inherits from non-public classes」:類別名稱。
每種版本都會給一個路徑,接著是名稱清單。內文還附上兩個建議:如果你的方法名稱和私有 API 相同就重新命名,以及移除含有它們的靜態函式庫。2026 年 9 月的一封 e-mail 也建議用 xcrun swift-demangle 把編碼過的 Swift 名稱轉成看得懂的形式。
這項檢查背後的規則是 guideline 2.5.1:app 只能使用公開 API。Apple Developer Program 授權合約第 3.3.1 條也重申:不得呼叫私有 API。這條規則的人工審查,在 guideline 2.5.1 指南裡有詳細說明。這裡處理的是上傳錯誤。
和其他 ITMS 錯誤一樣,沒有人打開過你的 app:Apple 是在處理 build 時分析二進位檔。Apple 沒有公布任何私有 API 清單:你 e-mail 裡的名稱就是唯一可靠的清單。
讀懂路徑:是 SDK 還是你的執行檔?
清單前面寫的路徑會告訴你該去哪個檔案找。
Frameworks/Nom.framework/Nom:名稱在某個內嵌的動態 framework 裡,也就是某個特定 SDK。在 Solys 的 build 中,這個資料夾有 14 個 framework:Hermes、React Native、已預先編譯的 Expo 模組,以及圖片函式庫 SDWebImage。- 只有你的 app 名稱:名稱在主執行檔裡。用 Expo 的話,主執行檔包含你的原生程式碼和從原始碼編譯的 pod,預設是靜態連結。所以任何原生函式庫都可能是元凶。
決策樹:從被點名的名稱到修正
依序看每一列。第一個符合你情況的列就是線索。
| 你看到的 | 去哪裡找 | 修正方式 |
|---|---|---|
$s 或 _$s 開頭的名稱 | xcrun swift-demangle | 重新走一次決策樹 |
| 在你的 IPA 裡找不到 | 對 bundle 執行 grep | 重新上傳 build |
| iOS 上的公開名稱 | iOS SDK 標頭檔 | 重新上傳 build |
API_UNAVAILABLE(ios) | iOS SDK 標頭檔 | 更新套件 |
在 Frameworks/X 裡 | 提供 X 的 SDK | 更新 SDK |
| 選擇器,在你的 app 裡 | Pod 和原生模組 | 加前綴或更新 |
符號 _nom,在你的 app 裡 | nm -m,再查 pod | 替換呼叫 |
| 類別,在你的 app 裡 | 你的類別和 pod | 為類別加前綴 |
「重新上傳 build」那幾列代表誤判,下文會說明。
步驟 1:打開已上傳的 build
針對實際上傳給 Apple 的檔案操作,而不是你的專案。從 expo.dev 的 build 頁面下載 IPA,然後在你的 Mac 上執行:
unzip -q MonApp.ipa -d ipa
ls ipa/Payload/MonApp.app/Frameworks
otool -L ipa/Payload/MonApp.app/MonApp | grep PrivateFrameworks
第二行列出內嵌的 framework。第三行尋找直接連結到 Apple 私有 framework 的地方:它應該什麼都不顯示。在 Solys 上,otool -L 沒找到任何私有 framework,執行檔和 14 個 framework 裡都沒有。
步驟 2:找出含有該名稱的檔案
先大範圍搜尋。在整個 bundle 裡找被點名的名稱,拿掉開頭的底線:
grep -rl "dyld_get_shared_cache_range" ipa/Payload/MonApp.app
這個指令會列出含有這段文字的檔案。接著依名稱類型(選擇器、符號、類別),在找到的檔案(記為 Binaire)上縮小範圍:
strings -a Binaire | grep -x "boldFontOfSize:"
nm -m Binaire | grep "_dyld_get_shared_cache_range"
nm -m Binaire | grep '_OBJC_CLASS_$_NomDeClasse'
選擇器要用 strings,不要用 nm。 在 Solys 的 React framework 上,nm 顯示的是帶類別的方法,例如 -[RCTRootView initWithBridge:moduleName:initialProperties:],從來不會單獨顯示選擇器 initWithBridge:。strings 找得到。
符號的話,看那一行的結尾。在 Solys 上,nm -m 例如會顯示 (undefined) external __dyld_get_image_name (from libSystem):二進位檔呼叫這個函式但不包含它,由 libSystem 提供。這個函式是公開的,下一步會說明。
步驟 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 26.2 SDK,取自真實 ITMS-90338 e-mail 的四個名稱結果如下:
| 被點名的名稱 | 在 iOS SDK 中 | 解讀 |
|---|---|---|
| _dyld_get_shared_cache_range | 不存在 | 私有 API |
| boldFontOfSize: | 不存在 | 私有或名稱衝突 |
| SCDynamicStoreCreate | API_UNAVAILABLE(ios) | 僅限 macOS |
| isMainFrame | WebKit 的屬性 | 公開 |
名稱存在且在 iOS 上可用,代表是誤判。名稱不存在,代表是私有 API 或名稱衝突。 有兩個限制:只在 Swift 中公開的 API 不會出現在這些標頭檔裡;像 setDelegate: 這樣的存取方法,要搜尋屬性 delegate。
步驟 4:追到套件
如果路徑點名的是 framework,從它的名稱可以找到 pod,再找到 npm 套件:
grep -n "NomDuFramework" ios/Podfile.lock
npm explain nom-du-paquet
如果你從沒自己加過這個套件,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 上,這個資料夾有 15 個已預先編譯的 framework,格式為 .xcframework: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 建議為加到它的類別上的方法加前綴,例如三個小寫字母加一個底線。
在 macOS 上公開、在 iOS 上不公開的 API。 2024 年,一個 .NET MAUI app 因 _SCDynamicStoreCreate 被拒。Apple 確實有記載這個函式,但只限 macOS,而 iOS SDK 的標頭檔把它標為 API_UNAVAILABLE(ios, tvos, watchos)。
正面修正,不要繞過
- 更新依賴。 對於 Expo 有追蹤的套件,根據 Expo CLI 文件,
npx expo install --check會指出和你的 SDK 不相符的套件。其他套件就去看版本更新紀錄。 - 退回上一版,如果是新版本帶進了這個名稱,並通知開發商。
- 替換或移除已停止維護的函式庫。
- 重新命名你的方法或類別,加上前綴。
不要隱藏名稱。 把字串拆開或用迂迴方式呼叫方法,只是把使用藏起來,沒有修正。這仍然違反 2.5.1 規則和合約,到時候承擔風險的是你的帳號。
接著,用新的 build 編號重新 build。遠端更新不夠:名稱在編譯過的原生程式碼裡。上傳前,在新的 IPA 上再跑一次步驟 2。名稱應該不會再出現。
遇到誤判時
Apple 的檢查有時會出錯。2022 年 6 月 6 日,一些 React Native 和 Flutter app 因為 WebKit 的公開名稱(例如 setNavigationDelegate: 或 isMainFrame)收到 ITMS-90338,連幾乎沒改動的更新也是。隔天,一位 Apple 工程師表示問題已在驗證端解決,並請大家重新上傳 build。這個討論串保存在 Expo 的 GitHub 儲存庫。
三個跡象同時出現,代表是誤判:
- 這個 build 和上一個被接受的 build 很相似;
- 步驟 2 找不到這個名稱,或步驟 3 顯示它在 iOS 上是公開的;
- 同一天有其他開發者在 Apple 論壇上回報相同錯誤。
這種情況下,先看 Apple 服務狀態,再用新編號重新上傳 build。如果錯誤持續,附上完整 e-mail 寫信給 Apple 開發者支援。至於名稱衝突,在 Apple 眼中不算誤判:請重新命名。
下次上傳之前
其他 ITMS 錯誤也有各自的指南:ITMS-90725 是 Xcode 版本,ITMS-91053 是隱私權清單,ITMS-90034 是簽章。build 被接受後,在審查前先用 TestFlight 安裝它。
審查人員的拒絕需要更多思考:處理流程在 app 被 Apple 拒絕頁面,最常見的原因在Apple 為什麼拒絕 app。
想在上傳前檢查你的資料,Review Kit 整合了我的檢查清單和會檢閱你專案的 Claude Code skills。如果上傳卡住、想找個人幫你看看,跟我預約一小時:我們透過視訊,在你的螢幕上一起檢查你的專案。如果你正在準備第一次上架,做個免費診斷:它會幫你把步驟排好順序,並寄給你我的 Apple 防退件檢查清單。
常見問題
ITMS-90338 是 App Review 的拒絕嗎?
不是。這是處理 build 時的自動檢查,在任何人工審查之前。被拒的 build 不會進入 TestFlight,也不會進入審查。之後審查人員發現的私有 API,則屬於 guideline 2.5.1 的範圍。
可以用遠端更新修正 ITMS-90338 嗎?
不行。被點名的名稱在二進位檔編譯過的原生程式碼裡。遠端更新只會改變 JavaScript 和資源。修正依賴,重新 build,再重新上傳。
為什麼 Apple 點名我從沒寫過的方法?
因為它們來自內嵌的函式庫,或是名稱衝突:某個依賴宣告了一個和私有 API 同名的方法。Apple 讀的是二進位檔裡的名稱,不是你的原始碼。用 grep 和 npm explain 追到套件。
怎麼知道某個 API 在 iOS 上是不是公開的?
在 iOS SDK 的標頭檔裡搜尋它的名稱(路徑可用 xcrun --sdk iphoneos --show-sdk-path 取得),再看它的可用性。Apple 的文件頁面也有標示。只記載於 macOS 的函式,在 iPhone 上不算公開。
修正後需要更改 build 編號嗎?
需要。讓 EAS 自動遞增:同一版本已上傳過的編號,可能會被 ITMS-90189 拒絕。

