# ITMS-90338: betulkan “Non-public API usage” dengan Expo

> ITMS-90338: build anda menyebut kaedah, fungsi atau kelas yang Apple anggap peribadi. Cari dependency puncanya, kemas kininya, kemudian buat build semula.

Halaman asal: [ITMS-90338: betulkan “Non-public API usage” dengan Expo](https://devoria.fr/ms/blog/itms-90338-non-public-api-usage-expo)
Pengarang: [Mehdi Azizi](https://devoria.fr/ms/mehdi-azizi)
Diterbitkan: 2026-11-04
Semakan kandungan terakhir: 2026-11-04


ITMS-90338 ialah semakan automatik Apple pada build anda. Ia bermaksud binari anda merujuk API yang Apple anggap peribadi (private): kaedah Objective-C, fungsi atau kelas. **Cara membetulkannya: kenal pasti fail yang disebut dalam e-mel, jejak hingga ke dependency yang mengandungi nama itu, kemas kini dependency tersebut atau namakan semula kaedah anda, kemudian buat build semula.**

Dengan Expo, nama itu hampir selalu datang daripada library native, jarang sekali daripada JavaScript anda. Pada 15 Oktober 2026, saya menjalankan arahan analisis dalam panduan ini pada build simulator Solys, aplikasi jam penggera saya yang ada permainan mini (Expo SDK 57), dan pada iOS SDK Xcode 26.2. Berikut cara membaca e-mel itu, mencari puncanya dan mengenal pasti positif palsu.

## Apakah maksud ralat ITMS-90338?

Mesejnya bertajuk "ITMS-90338: Non-public API usage". Ia ada tiga variasi, bergantung pada apa yang Apple temui dalam binari anda:

- **"references non-public selectors"**: nama kaedah Objective-C, seperti `boldFontOfSize:`;
- **"references non-public symbols"**: fungsi, ditulis dengan garis bawah di hadapan, seperti `__dyld_get_shared_cache_range`;
- **"contains or inherits from non-public classes"**: nama kelas.

Setiap variasi memberikan satu laluan (path), diikuti senarai nama. Teksnya menambah dua nasihat: namakan semula kaedah anda jika namanya sama dengan API peribadi, dan buang library statik yang mengandunginya. Satu e-mel bulan September 2026 turut mencadangkan `xcrun swift-demangle` untuk menjadikan nama Swift yang dikodkan boleh dibaca.

Peraturan di sebalik semakan ini ialah [guideline 2.5.1](https://developer.apple.com/app-store/review/guidelines/#2.5.1): aplikasi hanya boleh menggunakan API awam. [Perjanjian lesen Apple Developer Program](https://developer.apple.com/support/terms/apple-developer-program-license-agreement/) mengulanginya dalam fasal 3.3.1: tiada panggilan ke API peribadi. Semakan manusia bagi peraturan ini diterangkan dalam panduan [guideline 2.5.1](https://devoria.fr/ms/blog/guideline-2-5-1-api-awam-sdk-expo). Di sini, kita urus ralat semasa penghantaran.

Seperti ralat ITMS yang lain, tiada sesiapa membuka aplikasi anda: Apple menganalisis binari semasa memproses build. **Apple tidak menerbitkan sebarang senarai API peribadi: nama dalam e-mel anda ialah satu-satunya senarai yang boleh dipercayai.**

## Membaca laluan: SDK atau executable anda?

Laluan yang ditulis sebelum senarai memberitahu anda fail mana yang perlu diperiksa.

- **`Frameworks/Name.framework/Name`**: nama itu berada dalam framework dinamik yang dibenamkan, jadi dalam SDK tertentu. Pada build Solys, folder ini mengandungi 14 framework: Hermes, React Native, modul Expo yang dihantar siap dikompil dan library imej SDWebImage.
- **Nama aplikasi anda sahaja**: nama itu berada dalam executable utama. Dengan Expo, ia menggabungkan kod native anda dan pod yang dikompil daripada sumbernya, dipautkan secara statik secara lalai. Mana-mana library native boleh jadi puncanya.

## Pokok keputusan: daripada nama yang disebut kepada pembetulan

Ikut baris mengikut urutan. Baris pertama yang sepadan dengan kes anda memberi petunjuknya.

| Apa yang anda baca            | Tempat mencari         | Pembetulan                    |
| ----------------------------- | ---------------------- | ----------------------------- |
| Nama `$s` atau `_$s`          | `xcrun swift-demangle` | Ulang pokok keputusan         |
| Tiada dalam IPA anda          | `grep` pada bundle     | Hantar build semula           |
| Nama awam di iOS              | Header iOS SDK         | Hantar build semula           |
| `API_UNAVAILABLE(ios)`        | Header iOS SDK         | Kemas kini pakej              |
| Dalam `Frameworks/X`          | SDK yang menyediakan X | Kemas kini SDK                |
| Selector, aplikasi anda       | Pod dan modul native   | Tambah awalan atau kemas kini |
| Simbol `_nama`, aplikasi anda | `nm -m`, kemudian pod  | Gantikan panggilan            |
| Kelas, aplikasi anda          | Kelas dan pod anda     | Tambah awalan pada kelas      |

Baris "Hantar build semula" menandakan positif palsu, yang dibincangkan di bawah.

## Langkah 1: buka build yang dihantar

Gunakan fail yang benar-benar dihantar kepada Apple, bukan projek anda. Muat turun IPA dari halaman build di expo.dev, kemudian jalankan di Mac anda:

```bash
unzip -q MyApp.ipa -d ipa
ls ipa/Payload/MyApp.app/Frameworks
otool -L ipa/Payload/MyApp.app/MyApp | grep PrivateFrameworks
```

Baris kedua menyenaraikan framework yang dibenamkan. Baris ketiga mencari pautan terus ke framework peribadi Apple: ia tidak sepatutnya memaparkan apa-apa. Pada Solys, `otool -L` tidak menemui sebarang framework peribadi, sama ada dalam executable atau dalam 14 framework tersebut.

## Langkah 2: cari fail yang mengandungi nama itu

Mula secara luas. Cari nama yang disebut, tanpa garis bawah di hadapan, dalam seluruh bundle:

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

Arahan ini menyenaraikan fail yang mengandungi teks tersebut. Kemudian perincikan mengikut jenis nama (selector, simbol, kelas), pada fail yang ditemui, ditulis sebagai `Binary`:

```bash
strings -a Binary | grep -x "boldFontOfSize:"
nm -m Binary | grep "_dyld_get_shared_cache_range"
nm -m Binary | grep '_OBJC_CLASS_$_ClassName'
```

**Untuk selector, gunakan `strings`, bukan `nm`.** Pada framework React Solys, `nm` memaparkan kaedah bersama kelasnya, seperti `-[RCTRootView initWithBridge:moduleName:initialProperties:]`, tidak pernah selector `initWithBridge:` sahaja. `strings` dapat menemuinya.

Untuk simbol, baca hujung baris. Pada Solys, `nm -m` memaparkan contohnya `(undefined) external __dyld_get_image_name (from libSystem)`: binari memanggil fungsi itu tanpa mengandunginya, dan libSystem yang menyediakannya. Fungsi ini awam, seperti yang ditunjukkan dalam langkah seterusnya.

## Langkah 3: semak sama ada nama itu benar-benar peribadi

Header iOS SDK menerangkan API awam. Carian beberapa saat memberitahu sama ada nama yang disebut ada di situ, dan pada platform mana. Untuk simbol, buang dahulu garis bawah yang ditambah semasa kompilasi: `_SCDynamicStoreCreate` menjadi `SCDynamicStoreCreate`.

```bash
SDK=$(xcrun --sdk iphoneos --show-sdk-path)
grep -rn "SCDynamicStoreCreate" "$SDK/usr/include" "$SDK"/System/Library/Frameworks/*/Headers
```

Pada 15 Oktober 2026, dengan iOS SDK 26.2, inilah hasil bagi empat nama yang diambil daripada e-mel ITMS-90338 sebenar:

| Nama yang disebut             | Dalam iOS SDK        | Tafsiran                        |
| ----------------------------- | -------------------- | ------------------------------- |
| \_dyld_get_shared_cache_range | Tiada                | API peribadi                    |
| boldFontOfSize:               | Tiada                | Peribadi atau pertembungan nama |
| SCDynamicStoreCreate          | API_UNAVAILABLE(ios) | macOS sahaja                    |
| isMainFrame                   | Property WebKit      | Awam                            |

**Nama yang ada dan tersedia di iOS menunjukkan positif palsu. Nama yang tiada menunjukkan API peribadi atau pertembungan nama.** Dua had: API awam yang hanya untuk Swift tidak muncul dalam header ini, dan untuk accessor seperti `setDelegate:`, cari property `delegate`.

## Langkah 4: jejak hingga ke pakej

Jika laluan menyebut framework, namanya membawa kepada pod, kemudian kepada pakej npm:

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

`npm explain` memberitahu dependency mana yang memasangnya, jika anda tidak pernah menambahnya sendiri.

Jika laluan menyebut executable, cari nama itu dalam sumber native, kemudian dalam pod:

```bash
grep -rl --include='*.m' --include='*.mm' --include='*.swift' --include='*.h' \
  "boldFontOfSize" node_modules modules
grep -rl "boldFontOfSize" ios/Pods
```

Sesuaikan senarai folder: `modules` ialah folder untuk modul Expo tempatan. Folder `ios/Pods` muncul selepas `npx expo prebuild -p ios`, yang turut memasang pod di Mac. Awas: arahan ini boleh mencipta semula folder `ios`. Jika anda telah mengubahnya secara manual, gunakan salinan.

**Carian dalam `ios/Pods` sama penting dengan carian dalam sumber.** Pada Solys, folder ini mengandungi 15 framework yang dihantar siap dikompil, dalam format `.xcframework`: modul Expo, Hermes, React Native dan SDK pihak ketiga. Carian yang terhad kepada fail sumber akan terlepas kod mereka.

## Punca biasa

**SDK pihak ketiga, terlalu lama atau terlalu baharu.** Pada September 2026, seorang pembangun Flutter [melaporkan penolakan disebabkan SDK keselamatan](https://github.com/talsec/Free-RASP-Flutter/issues/236): versi 8.2.3 memanggil `__dyld_get_shared_cache_range`. Versi 8.2.2 lulus, dan penerbitnya menasihatkan untuk kembali ke versi itu sementara menunggu pembetulan.

**Modul native yang memanggil API tidak berdokumen.** Modul anda sendiri, plugin atau library kecil mungkin menggunakan kaedah dalaman UIKit yang ditemui di forum. Semakan ini mengesannya melalui namanya.

**Pertembungan nama.** Kaedah atau kelas anda secara kebetulan mempunyai nama yang sama dengan API peribadi Apple. Mesej itu sudah menjangkakannya dan meminta anda menamakannya semula. Apple mengesyorkan [awalan pada kaedah yang ditambah ke kelasnya](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html), contohnya tiga huruf kecil diikuti garis bawah.

**API awam di macOS, bukan di iOS.** Pada 2024, sebuah aplikasi .NET MAUI [ditolak kerana `_SCDynamicStoreCreate`](https://github.com/dotnet/runtime/issues/103949). Apple memang mendokumenkan fungsi ini, tetapi [untuk macOS sahaja](https://developer.apple.com/documentation/systemconfiguration/1437828-scdynamicstorecreate), dan header iOS SDK menandakannya `API_UNAVAILABLE(ios, tvos, watchos)`.

## Betulkan tanpa memintas

1. **Kemas kini dependency.** Untuk pakej yang dijejak oleh Expo, `npx expo install --check` menandakan pakej yang tidak sepadan dengan SDK anda, menurut [dokumentasi Expo CLI](https://docs.expo.dev/more/expo-cli/). Untuk yang lain, baca changelog.
2. **Kembali ke versi sebelumnya** jika versi baharu yang memperkenalkan nama itu, dan maklumkan kepada penerbitnya.
3. **Gantikan atau buang** library yang sudah ditinggalkan.
4. **Namakan semula** kaedah atau kelas anda, dengan awalan.

**Jangan sembunyikan nama itu.** Memecahkan string atau memanggil kaedah secara berlingkar hanya menyembunyikan penggunaannya tanpa membetulkannya. Ia tetap dilarang oleh peraturan 2.5.1 dan oleh perjanjian, dan akaun anda yang akan terdedah.

Kemudian, buat build semula dengan [nombor build](https://devoria.fr/ms/blog/build-number-app-store-connect-versi-nombor-build) baharu. Kemas kini jarak jauh (OTA) tidak mencukupi: nama itu berada dalam kod native yang dikompil. Sebelum menghantar, jalankan semula langkah 2 pada IPA baharu. Nama itu tidak sepatutnya muncul lagi.

## Apabila ia positif palsu

Semakan Apple kadangkala silap. Pada 6 Jun 2022, aplikasi React Native dan Flutter menerima ITMS-90338 untuk nama awam WebKit, seperti `setNavigationDelegate:` atau `isMainFrame`, termasuk pada kemas kini yang hampir tidak berubah. Keesokan harinya, seorang jurutera Apple menulis bahawa masalah itu telah diselesaikan di pihak pengesahan dan meminta build dihantar semula. Thread itu disimpan dalam [repositori GitHub Expo](https://github.com/expo/expo/issues/17768).

Tiga petunjuk ini, jika semuanya ada, menunjukkan positif palsu:

- build itu serupa dengan build terakhir yang diterima;
- langkah 2 tidak menemui nama itu, atau langkah 3 menunjukkan ia awam di iOS;
- pembangun lain melaporkan ralat yang sama pada hari yang sama di forum Apple.

Dalam kes ini, semak [status perkhidmatan Apple](https://developer.apple.com/system-status/), kemudian hantar build semula dengan nombor baharu. Jika ralat berterusan, hubungi [sokongan pembangun Apple](https://developer.apple.com/contact/) dengan e-mel penuh. Pertembungan nama pula bukan positif palsu di mata Apple: namakan semula.

## Sebelum penghantaran anda yang seterusnya

Ralat ITMS lain juga ada panduan masing-masing: [ITMS-90725](https://devoria.fr/ms/blog/itms-90725-sdk-version-issue-expo) untuk versi Xcode, [ITMS-91053](https://devoria.fr/ms/blog/itms-91053-missing-api-declaration-expo) untuk privacy manifest dan [ITMS-90034](https://devoria.fr/ms/blog/itms-90034-missing-invalid-signature) untuk tandatangan. Setelah build diterima, pasang ia dengan [TestFlight](https://devoria.fr/ms/blog/testflight-uji-aplikasi-iphone-sebelum-hantar) sebelum semakan.

Penolakan oleh reviewer memerlukan lebih banyak pertimbangan: caranya ada di halaman [aplikasi ditolak oleh Apple](https://devoria.fr/ms/expertise/aplikasi-ditolak-apple), dan sebab yang paling kerap ada dalam [mengapa Apple menolak aplikasi](https://devoria.fr/ms/blog/aplikasi-ditolak-apple-5-kes-sebenar).

Untuk menyemak fail anda sebelum menghantar, [Review Kit](https://devoria.fr/ms/kits/app-review) menghimpunkan senarai semak saya dan skill Claude Code yang menyemak projek anda. Jika penghantaran anda tersekat dan anda mahukan pandangan manusia, [tempah satu jam dengan saya](https://devoria.fr/ms/coaching): kita semak projek anda melalui panggilan video, di skrin anda. Dan jika anda sedang menyediakan pelancaran pertama, [buat diagnostik percuma anda](https://devoria.fr/ms/diagnostic): ia menyusun langkah mengikut urutan dan menghantar senarai semak anti-penolakan Apple saya kepada anda.

## Soalan lazim

### Adakah ITMS-90338 penolakan oleh App Review?

Tidak. Ia semakan automatik yang dibuat semasa build diproses, sebelum sebarang semakan manusia. Build yang ditolak tidak sampai ke TestFlight mahupun ke semakan. API peribadi yang dikesan kemudian oleh reviewer pula tertakluk pada guideline 2.5.1.

### Bolehkah ITMS-90338 dibetulkan dengan kemas kini jarak jauh?

Tidak. Nama yang disebut berada dalam kod native binari yang dikompil. Kemas kini jarak jauh hanya mengubah JavaScript dan aset. Betulkan dependency, buat build semula, kemudian hantar semula.

### Mengapa Apple menyebut kaedah yang tidak pernah saya tulis?

Kerana ia datang daripada library yang dibenamkan, atau daripada pertembungan nama: satu dependency mengisytiharkan kaedah yang namanya sama dengan API peribadi. Apple membaca nama yang ada dalam binari, bukan kod sumber anda. Jejak hingga ke pakej dengan `grep` dan `npm explain`.

### Bagaimana mengetahui sama ada API itu awam di iOS?

Cari namanya dalam header iOS SDK, yang laluannya diberikan oleh `xcrun --sdk iphoneos --show-sdk-path`, kemudian baca ketersediaannya. Halaman dokumentasi Apple juga menyatakannya. Fungsi yang didokumenkan untuk macOS sahaja bukan API awam di iPhone.

### Perlukah nombor build ditukar selepas pembetulan?

Ya. Biarkan EAS menaikkannya: nombor yang sudah dihantar untuk versi yang sama boleh ditolak dengan ITMS-90189.


## Projek anda

[Review Kit](https://devoria.fr/ms/kits/app-review): Senarai semak review App Store saya, penolakan Apple yang telah saya betulkan, senarai semak Google Play dan tiga skill Claude Code. RM 219.

[iOS & Android Kit](https://devoria.fr/ms/kits/ios): Bina app iPhone dan Android anda dengan Claude Code: kod permulaan app saya, laluan hingga ke store dan semua kit. RM 449.

[Diagnostik: apa yang memisahkan anda daripada app iPhone anda](https://devoria.fr/ms/diagnostic): 7 soalan, 60 saat: anda tahu apa yang benar-benar menghalang anda menerbitkan app iPhone, laluan 4 langkah anda dan kit yang sesuai dengan anda.
