# ITMS-90338: corregir «Non-public API usage» amb Expo

> ITMS-90338: el teu build cita una API que Apple considera privada. Troba la dependència culpable, actualitza-la i torna a fer un build.

Pàgina original: [ITMS-90338: corregir «Non-public API usage» amb Expo](https://devoria.fr/ca/blog/itms-90338-non-public-api-usage-expo)
Autor: [Mehdi Azizi](https://devoria.fr/ca/mehdi-azizi)
Publicació: 2026-11-04
Darrera revisió del contingut: 2026-11-04


ITMS-90338 és un control automàtic d'Apple sobre el teu build. Diu que el teu binari fa referència a API que Apple considera privades: mètodes Objective-C, funcions o classes. **La correcció: localitzar el fitxer citat al correu, arribar fins a la dependència que conté el nom, actualitzar-la o canviar el nom del teu mètode, i després tornar a fer un build.**

Amb Expo, el nom gairebé sempre ve d'una biblioteca nativa, rarament del teu JavaScript. El 15 d'octubre de 2026 vaig executar les ordres d'anàlisi d'aquesta guia sobre un build de simulador de Solys, el meu despertador amb minijocs (SDK Expo 57), i sobre el SDK d'iOS de Xcode 26.2. Així es llegeix el correu, es troba el culpable i es reconeix un fals positiu.

## Què vol dir l'error ITMS-90338?

El missatge es titula «ITMS-90338: Non-public API usage». N'hi ha tres variants, segons el que Apple hagi trobat al teu binari:

- **«references non-public selectors»**: noms de mètodes Objective-C, com `boldFontOfSize:`;
- **«references non-public symbols»**: funcions, escrites amb un guió baix al davant, com `__dyld_get_shared_cache_range`;
- **«contains or inherits from non-public classes»**: un nom de classe.

Cada variant dona un camí i després la llista de noms. El text afegeix dos consells: canviar el nom dels teus mètodes si coincideix amb una API privada, i treure la biblioteca estàtica que els conté. Un correu de setembre de 2026 també proposa `xcrun swift-demangle` per fer llegibles els noms Swift codificats.

La regla darrere d'aquest control és la [guideline 2.5.1](https://developer.apple.com/app-store/review/guidelines/#2.5.1): una app només pot fer servir API públiques. El [contracte de llicència de l'Apple Developer Program](https://developer.apple.com/support/terms/apple-developer-program-license-agreement/) ho repeteix a l'article 3.3.1: cap crida a una API privada. La revisió humana d'aquesta regla es detalla a la guia de la [guideline 2.5.1](https://devoria.fr/ca/blog/guideline-2-5-1-api-publiques-sdk-expo). Aquí tractem l'error d'enviament.

Com els altres errors ITMS, ningú no ha obert la teva app: Apple ha analitzat el binari mentre processava el build. **Apple no publica cap llista d'API privades: els noms del teu correu són l'única llista fiable.**

## Llegir el camí: un SDK o el teu executable?

El camí escrit abans de la llista et diu en quin fitxer has de buscar.

- **`Frameworks/Nom.framework/Nom`**: el nom és dins d'un framework dinàmic inclòs, per tant en un SDK concret. Al build de Solys, aquesta carpeta conté 14 frameworks: Hermes, React Native, mòduls d'Expo ja compilats i la biblioteca d'imatges SDWebImage.
- **Només el nom de la teva app**: el nom és a l'executable principal. Amb Expo, aquest reuneix el teu codi natiu i els pods compilats des del codi font, enllaçats en estàtic per defecte. Qualsevol biblioteca nativa pot ser-ne la causa.

## L'arbre: del nom citat a la correcció

Agafa les files per ordre. La primera que encaixi amb el teu cas et dona la pista.

| El que llegeixes           | On buscar                 | Correcció                |
| -------------------------- | ------------------------- | ------------------------ |
| Nom en `$s` o `_$s`        | `xcrun swift-demangle`    | Tornar a l'arbre         |
| No apareix a la teva IPA   | `grep` sobre el bundle    | Tornar a enviar un build |
| Nom públic a iOS           | Capçaleres del SDK d'iOS  | Tornar a enviar un build |
| `API_UNAVAILABLE(ios)`     | Capçaleres del SDK d'iOS  | Actualitzar el paquet    |
| A `Frameworks/X`           | El SDK que proporciona X  | Actualitzar el SDK       |
| Selector, la teva app      | Pods i mòduls natius      | Prefixar o actualitzar   |
| Símbol `_nom`, la teva app | `nm -m`, després els pods | Substituir la crida      |
| Classe, la teva app        | Les teves classes i pods  | Prefixar la classe       |

Les files «Tornar a enviar un build» indiquen un fals positiu, que tracto més avall.

## Pas 1: obrir el build enviat

Treballa sobre el fitxer que realment has enviat a Apple, no sobre el projecte. Descarrega l'IPA des de la pàgina del build a expo.dev i executa al teu Mac:

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

La segona línia llista els frameworks inclosos. La tercera busca un enllaç directe a un framework privat d'Apple: no ha de mostrar res. A Solys, `otool -L` no troba cap framework privat, ni a l'executable ni als 14 frameworks.

## Pas 2: trobar el fitxer que conté el nom

Comença ampli. Busca el nom citat, sense els guions baixos inicials, a tot el bundle:

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

L'ordre llista els fitxers que contenen aquest text. Després afina segons el tipus de nom (selector, símbol, classe), sobre el fitxer trobat, anomenat `Binary`:

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

**Per a un selector, fes servir `strings`, no `nm`.** Al framework React de Solys, `nm` mostra mètodes amb la seva classe, com `-[RCTRootView initWithBridge:moduleName:initialProperties:]`, mai el selector `initWithBridge:` sol. `strings` sí que el troba.

Per a un símbol, llegeix el final de la línia. A Solys, `nm -m` mostra per exemple `(undefined) external __dyld_get_image_name (from libSystem)`: el binari crida la funció sense contenir-la, i libSystem la proporciona. Aquesta funció és pública, com mostra el pas següent.

## Pas 3: comprovar si el nom és realment privat

Les capçaleres del SDK d'iOS descriuen les API públiques. Una cerca de pocs segons et diu si el nom citat hi apareix, i en quines plataformes. Per a un símbol, treu primer el guió baix afegit en compilar: `_SCDynamicStoreCreate` passa a ser `SCDynamicStoreCreate`.

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

El 15 d'octubre de 2026, amb el SDK d'iOS 26.2, aquest és el resultat per a quatre noms trets de correus ITMS-90338 reals:

| Nom citat                     | Al SDK d'iOS         | Lectura             |
| ----------------------------- | -------------------- | ------------------- |
| \_dyld_get_shared_cache_range | Absent               | API privada         |
| boldFontOfSize:               | Absent               | Privada o col·lisió |
| SCDynamicStoreCreate          | API_UNAVAILABLE(ios) | Només macOS         |
| isMainFrame                   | Propietat de WebKit  | Pública             |

**Un nom present i disponible a iOS apunta a un fals positiu. Un nom absent apunta a una API privada o a una col·lisió.** Dos límits: una API pública només en Swift no apareix en aquestes capçaleres, i per a un accessor com `setDelegate:`, busca la propietat `delegate`.

## Pas 4: arribar fins al paquet

Si el camí cita un framework, el seu nom porta al pod i després al paquet npm:

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

`npm explain` et diu quina dependència l'ha instal·lat, si no l'has afegit mai tu.

Si el camí cita l'executable, busca el nom a les fonts natives i després als pods:

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

Adapta la llista de carpetes: `modules` és la dels mòduls d'Expo locals. La carpeta `ios/Pods` apareix després de `npx expo prebuild -p ios`, que també instal·la els pods al Mac. Compte: pot tornar a crear la carpeta `ios`. Si l'has modificat a mà, treballa sobre una còpia.

**La cerca a `ios/Pods` compta tant com la de les fonts.** A Solys, aquesta carpeta conté 15 frameworks ja compilats, en format `.xcframework`: mòduls d'Expo, Hermes, React Native i SDK de tercers. Una cerca limitada als fitxers font no veuria el seu codi.

## Les causes típiques

**Un SDK de tercers, massa antic o massa recent.** El setembre de 2026, un desenvolupador de Flutter va [informar d'un rebuig causat per un SDK de seguretat](https://github.com/talsec/Free-RASP-Flutter/issues/236): la versió 8.2.3 cridava `__dyld_get_shared_cache_range`. La 8.2.2 passava, i l'editor va recomanar tornar-hi mentre esperaven una correcció.

**Un mòdul natiu que crida una API no documentada.** El teu propi mòdul, un plugin o una biblioteca petita pot fer servir un mètode intern d'UIKit trobat en un fòrum. El control el detecta pel nom.

**Una col·lisió de nom.** El teu mètode o la teva classe té, per casualitat, el nom d'una API privada d'Apple. El missatge ho preveu i et demana que el canviïs. Apple recomana [prefixar els mètodes afegits a les seves classes](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html), per exemple amb tres lletres minúscules seguides d'un guió baix.

**Una API pública a macOS, però no a iOS.** El 2024, una app .NET MAUI va ser [rebutjada per `_SCDynamicStoreCreate`](https://github.com/dotnet/runtime/issues/103949). Apple documenta aquesta funció, però [només per a macOS](https://developer.apple.com/documentation/systemconfiguration/1437828-scdynamicstorecreate), i la capçalera del SDK d'iOS la marca `API_UNAVAILABLE(ios, tvos, watchos)`.

## Corregir sense fer trampes

1. **Actualitza la dependència.** Per als paquets que segueix Expo, `npx expo install --check` assenyala els que no coincideixen amb el teu SDK, segons la [documentació de l'Expo CLI](https://docs.expo.dev/more/expo-cli/). Per als altres, llegeix el registre de versions.
2. **Torna a la versió anterior** si és la nova la que ha introduït el nom, i avisa l'editor.
3. **Substitueix o elimina** una biblioteca abandonada.
4. **Canvia el nom** del teu mètode o de la teva classe, amb un prefix.

**No amaguis el nom.** Trossejar una cadena o cridar el mètode per una via indirecta amaga l'ús sense corregir-lo. Continua prohibit per la regla 2.5.1 i pel contracte, i llavors és el teu compte el que queda exposat.

Després, torna a fer un build amb un nou [número de build](https://devoria.fr/ca/blog/app-store-connect-build-number-versio-numero-build). Una actualització remota no n'hi ha prou: el nom és al codi natiu compilat. Abans d'enviar-lo, repeteix el pas 2 sobre la nova IPA. El nom ja no hi ha d'aparèixer.

## Quan és un fals positiu

El control d'Apple de vegades s'equivoca. El 6 de juny de 2022, apps de React Native i Flutter van rebre ITMS-90338 per noms públics de WebKit, com `setNavigationDelegate:` o `isMainFrame`, fins i tot en actualitzacions gairebé sense canvis. L'endemà, un enginyer d'Apple va escriure que el problema estava resolt a la validació i va convidar a tornar a enviar els builds. El fil es conserva al [repositori GitHub d'Expo](https://github.com/expo/expo/issues/17768).

Tres indicis junts apunten a un fals positiu:

- el build s'assembla a l'últim build acceptat;
- el pas 2 no troba el nom, o el pas 3 el mostra públic a iOS;
- altres desenvolupadors informen del mateix error el mateix dia als fòrums d'Apple.

En aquest cas, mira l'[estat dels serveis d'Apple](https://developer.apple.com/system-status/) i torna a enviar un build amb un número nou. Si l'error persisteix, escriu al [suport per a desenvolupadors d'Apple](https://developer.apple.com/contact/) amb el correu complet. Una col·lisió de nom, en canvi, no és un fals positiu als ulls d'Apple: canvia el nom.

## Abans del teu proper enviament

Els altres errors ITMS també tenen la seva guia: [ITMS-90725](https://devoria.fr/ca/blog/itms-90725-sdk-version-issue-expo) per a la versió de Xcode, [ITMS-91053](https://devoria.fr/ca/blog/itms-91053-missing-api-declaration-expo) per al manifest de privacitat i [ITMS-90034](https://devoria.fr/ca/blog/itms-90034-missing-invalid-signature) per a la signatura. Un cop acceptat el build, instal·la'l amb [TestFlight](https://devoria.fr/ca/blog/testflight-provar-app-iphone-abans-enviar) abans de la revisió.

Els rebutjos d'un revisor demanen més perspectiva: el procediment és a la pàgina [app rebutjada per Apple](https://devoria.fr/ca/expertise/aplicacio-rebutjada-per-apple), i els motius més freqüents són a [per què Apple rebutja una app](https://devoria.fr/ca/blog/rebuig-apple-per-que-rebutgen-app).

Per revisar el teu expedient abans d'enviar-lo, el [Review Kit](https://devoria.fr/ca/kits/app-review) reuneix la meva checklist i skills de Claude Code que revisen el teu projecte. Si el teu enviament es bloqueja i vols una mirada humana, [reserva una hora amb mi](https://devoria.fr/ca/coaching): repassem el teu projecte per videotrucada, a la teva pantalla. I si prepares el teu primer llançament, [fes el teu diagnòstic gratuït](https://devoria.fr/ca/diagnostic): posa els passos en ordre i t'envia la meva checklist antirebuig d'Apple.

## Preguntes freqüents

### ITMS-90338 és un rebuig de l'App Review?

No. És un control automàtic fet mentre es processa el build, abans de qualsevol revisió humana. El build rebutjat no arriba ni a TestFlight ni a revisió. Una API privada detectada més tard per un revisor depèn, en canvi, de la guideline 2.5.1.

### Es pot corregir ITMS-90338 amb una actualització remota?

No. Els noms citats són al codi natiu compilat del binari. Una actualització remota només canvia el JavaScript i els recursos. Corregeix la dependència, torna a fer un build i envia'l de nou.

### Per què Apple cita mètodes que no he escrit mai?

Perquè vénen d'una biblioteca inclosa, o d'una col·lisió: una dependència declara un mètode amb el nom d'una API privada. Apple llegeix els noms presents al binari, no el teu codi font. Arriba fins al paquet amb `grep` i `npm explain`.

### Com sé si una API és pública a iOS?

Busca'n el nom a les capçaleres del SDK d'iOS, el camí de les quals et dona `xcrun --sdk iphoneos --show-sdk-path`, i mira'n la disponibilitat. La pàgina de documentació d'Apple també ho indica. Una funció documentada només per a macOS no és pública a l'iPhone.

### Cal canviar el número de build després de la correcció?

Sí. Deixa que EAS l'incrementi: un número ja enviat per a la mateixa versió pot ser rebutjat amb ITMS-90189.


## El teu projecte

[Review Kit](https://devoria.fr/ca/kits/app-review): La meva checklist de revisió de l'App Store, els rebutjos d'Apple que he corregit, la de Google Play i tres skills de Claude Code. 49 €.

[iOS & Android Kit](https://devoria.fr/ca/kits/ios): Crea la teva app d'iPhone i Android amb Claude Code: el codi de partida de les meves apps, el camí fins a les botigues i tots els kits. 99 €.

[Diagnòstic: què et separa de la teva app d'iPhone](https://devoria.fr/ca/diagnostic): 7 preguntes, 60 segons: sabràs què et bloqueja de debò per publicar la teva app d'iPhone, el teu recorregut en 4 etapes i el kit que et toca.
