# ITMS-90338: correggere “Non-public API usage” con Expo

> ITMS-90338: il tuo build cita un metodo, una funzione o una classe che Apple considera privati. Trova la dipendenza colpevole, aggiornala e rifai il build.

Pagina originale: [ITMS-90338: correggere “Non-public API usage” con Expo](https://devoria.fr/it/blog/itms-90338-non-public-api-usage-expo)
Autore: [Mehdi Azizi](https://devoria.fr/it/mehdi-azizi)
Pubblicazione: 2026-11-04
Ultima revisione dei contenuti: 2026-11-04


ITMS-90338 è un controllo automatico di Apple sul tuo build. Ti dice che il tuo binario fa riferimento ad API che Apple considera private: metodi Objective-C, funzioni o classi. **La soluzione: individuare il file citato nell'email, risalire alla dipendenza che contiene il nome, aggiornarla o rinominare il tuo metodo, poi rifare il build.**

Con Expo, il nome arriva quasi sempre da una libreria nativa, raramente dal tuo JavaScript. Il 15 ottobre 2026 ho lanciato i comandi di analisi di questa guida su un build per simulatore di Solys, la mia sveglia con minigiochi (SDK Expo 57), e sull'SDK iOS di Xcode 26.2. Ecco come leggere l'email, trovare il colpevole e riconoscere un falso positivo.

## Cosa significa l'errore ITMS-90338?

Il messaggio si intitola "ITMS-90338: Non-public API usage". Esiste in tre varianti, a seconda di cosa Apple ha trovato nel tuo binario:

- **"references non-public selectors"**: nomi di metodi Objective-C, come `boldFontOfSize:`;
- **"references non-public symbols"**: funzioni, scritte con un trattino basso davanti, come `__dyld_get_shared_cache_range`;
- **"contains or inherits from non-public classes"**: un nome di classe.

Ogni variante riporta un percorso, poi l'elenco dei nomi. Il testo aggiunge due consigli: rinominare i tuoi metodi se il loro nome coincide con un'API privata, e rimuovere la libreria statica che li contiene. Un'email di settembre 2026 propone anche `xcrun swift-demangle` per rendere leggibili i nomi Swift codificati.

La regola dietro questo controllo è la [guideline 2.5.1](https://developer.apple.com/app-store/review/guidelines/#2.5.1): un'app può usare solo API pubbliche. Il [contratto di licenza dell'Apple Developer Program](https://developer.apple.com/support/terms/apple-developer-program-license-agreement/) lo ribadisce all'articolo 3.3.1: nessuna chiamata a un'API privata. La revisione umana di questa regola è spiegata nella guida alla [guideline 2.5.1](https://devoria.fr/it/blog/guideline-2-5-1-api-pubbliche-sdk-expo). Qui ci occupiamo dell'errore di invio.

Come per gli altri errori ITMS, nessuno ha aperto la tua app: Apple ha analizzato il binario durante l'elaborazione del build. **Apple non pubblica alcun elenco delle API private: i nomi nella tua email sono l'unico elenco affidabile.**

## Leggere il percorso: un SDK o il tuo eseguibile?

Il percorso scritto prima dell'elenco ti dice in quale file cercare.

- **`Frameworks/Nome.framework/Nome`**: il nome si trova in un framework dinamico incluso, quindi in un SDK preciso. Nel build di Solys questa cartella contiene 14 framework: Hermes, React Native, moduli Expo forniti già compilati e la libreria di immagini SDWebImage.
- **Solo il nome della tua app**: il nome si trova nell'eseguibile principale. Con Expo, questo riunisce il tuo codice nativo e i pod compilati dai sorgenti, collegati staticamente per impostazione predefinita. Qualsiasi libreria nativa può quindi essere la causa.

## L'albero: dal nome citato alla correzione

Scorri le righe in ordine. La prima che corrisponde al tuo caso ti dà la pista.

| Cosa leggi                  | Dove cercare           | Correzione              |
| --------------------------- | ---------------------- | ----------------------- |
| Nome in `$s` o `_$s`        | `xcrun swift-demangle` | Ripartire dall'albero   |
| Introvabile nella tua IPA   | `grep` sul bundle      | Rinviare un build       |
| Nome pubblico su iOS        | Header dell'SDK iOS    | Rinviare un build       |
| `API_UNAVAILABLE(ios)`      | Header dell'SDK iOS    | Aggiornare il pacchetto |
| In `Frameworks/X`           | L'SDK che fornisce X   | Aggiornare l'SDK        |
| Selettore, la tua app       | Pod e moduli nativi    | Prefissare o aggiornare |
| Simbolo `_nome`, la tua app | `nm -m`, poi i pod     | Sostituire la chiamata  |
| Classe, la tua app          | Le tue classi e i pod  | Prefissare la classe    |

Le righe "Rinviare un build" segnalano un falso positivo, trattato più sotto.

## Passo 1: aprire il build inviato

Lavora sul file effettivamente inviato ad Apple, non sul tuo progetto. Scarica l'IPA dalla pagina del build su expo.dev, poi lancia sul tuo Mac:

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

La seconda riga elenca i framework inclusi. La terza cerca un collegamento diretto a un framework privato di Apple: non deve mostrare nulla. Su Solys, `otool -L` non trova alcun framework privato, né nell'eseguibile né nei 14 framework.

## Passo 2: trovare il file che contiene il nome

Parti largo. Cerca il nome citato, senza i trattini bassi iniziali, in tutto il bundle:

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

Il comando elenca i file che contengono quel testo. Poi restringi in base al tipo di nome (selettore, simbolo, classe), sul file trovato, indicato come `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 un selettore usa `strings`, non `nm`.** Sul framework React di Solys, `nm` mostra i metodi con la loro classe, come `-[RCTRootView initWithBridge:moduleName:initialProperties:]`, mai il selettore `initWithBridge:` da solo. `strings` invece lo trova.

Per un simbolo, leggi la fine della riga. Su Solys, `nm -m` mostra per esempio `(undefined) external __dyld_get_image_name (from libSystem)`: il binario chiama la funzione senza contenerla, ed è libSystem a fornirla. Quella funzione è pubblica, come mostra il passo successivo.

## Passo 3: verificare se il nome è davvero privato

Gli header dell'SDK iOS descrivono le API pubbliche. Una ricerca di pochi secondi ti dice se il nome citato è presente, e su quali piattaforme. Per un simbolo, togli prima il trattino basso aggiunto in compilazione: `_SCDynamicStoreCreate` diventa `SCDynamicStoreCreate`.

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

Il 15 ottobre 2026, con l'SDK iOS 26.2, ecco il risultato per quattro nomi presi da vere email ITMS-90338:

| Nome citato                   | Nell'SDK iOS         | Lettura              |
| ----------------------------- | -------------------- | -------------------- |
| \_dyld_get_shared_cache_range | Assente              | API privata          |
| boldFontOfSize:               | Assente              | Privata o collisione |
| SCDynamicStoreCreate          | API_UNAVAILABLE(ios) | Solo macOS           |
| isMainFrame                   | Proprietà di WebKit  | Pubblica             |

**Un nome presente e disponibile su iOS indica un falso positivo. Un nome assente indica un'API privata o una collisione.** Due limiti: un'API pubblica solo in Swift non compare in questi header, e per un accessor come `setDelegate:` cerca la proprietà `delegate`.

## Passo 4: risalire al pacchetto

Se il percorso cita un framework, il suo nome porta al pod, poi al pacchetto npm:

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

`npm explain` ti dice quale dipendenza l'ha installato, se non l'hai mai aggiunto tu.

Se il percorso cita l'eseguibile, cerca il nome nei sorgenti nativi, poi nei pod:

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

Adatta l'elenco delle cartelle: `modules` è quella dei moduli Expo locali. La cartella `ios/Pods` compare dopo `npx expo prebuild -p ios`, che su Mac installa anche i pod. Attenzione: può ricreare la cartella `ios`. Se l'hai modificata a mano, lavora su una copia.

**La ricerca in `ios/Pods` conta quanto quella nei sorgenti.** Su Solys, questa cartella contiene 15 framework forniti già compilati, in formato `.xcframework`: moduli Expo, Hermes, React Native e SDK di terze parti. Una ricerca limitata ai file sorgente non vedrebbe il loro codice.

## Le cause tipiche

**Un SDK di terze parti, troppo vecchio o troppo recente.** A settembre 2026 uno sviluppatore Flutter ha [segnalato un rifiuto dovuto a un SDK di sicurezza](https://github.com/talsec/Free-RASP-Flutter/issues/236): la versione 8.2.3 chiamava `__dyld_get_shared_cache_range`. La versione 8.2.2 passava, e l'editore ha consigliato di tornarci in attesa di una correzione.

**Un modulo nativo che chiama un'API non documentata.** Il tuo modulo, un plugin o una piccola libreria può usare un metodo interno di UIKit trovato su un forum. Il controllo lo individua dal nome.

**Una collisione di nomi.** Il tuo metodo o la tua classe ha per caso il nome di un'API privata di Apple. Il messaggio lo prevede e ti chiede di rinominarlo. Apple consiglia di [prefissare i metodi aggiunti alle sue classi](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html), per esempio con tre lettere minuscole seguite da un trattino basso.

**Un'API pubblica su macOS, non su iOS.** Nel 2024 un'app .NET MAUI è stata [rifiutata per `_SCDynamicStoreCreate`](https://github.com/dotnet/runtime/issues/103949). Apple documenta questa funzione, ma [solo per macOS](https://developer.apple.com/documentation/systemconfiguration/1437828-scdynamicstorecreate), e l'header dell'SDK iOS la marca `API_UNAVAILABLE(ios, tvos, watchos)`.

## Correggere senza aggirare

1. **Aggiorna la dipendenza.** Per i pacchetti seguiti da Expo, `npx expo install --check` segnala quelli che non corrispondono al tuo SDK, secondo la [documentazione dell'Expo CLI](https://docs.expo.dev/more/expo-cli/). Per gli altri, leggi il changelog.
2. **Torna alla versione precedente** se è quella nuova ad aver introdotto il nome, e avvisa l'editore.
3. **Sostituisci o rimuovi** una libreria abbandonata.
4. **Rinomina** il tuo metodo o la tua classe, con un prefisso.

**Non mascherare il nome.** Spezzare una stringa o chiamare il metodo per vie traverse nasconde l'uso senza correggerlo. Resta vietato dalla regola 2.5.1 e dal contratto, e a quel punto è il tuo account a essere esposto.

Poi rifai un build con un nuovo [numero di build](https://devoria.fr/it/blog/build-number-app-store-connect-versione-numero-build). Un aggiornamento da remoto non basta: il nome è nel codice nativo compilato. Prima dell'invio, ripeti il passo 2 sulla nuova IPA. Il nome non deve più comparire.

## Quando è un falso positivo

Il controllo di Apple a volte sbaglia. Il 6 giugno 2022 alcune app React Native e Flutter hanno ricevuto ITMS-90338 per nomi pubblici di WebKit, come `setNavigationDelegate:` o `isMainFrame`, anche su aggiornamenti quasi invariati. Il giorno dopo un ingegnere di Apple ha scritto che il problema era stato risolto lato validazione e ha invitato a rinviare i build. La discussione è conservata nel [repository GitHub di Expo](https://github.com/expo/expo/issues/17768).

Tre indizi messi insieme indicano un falso positivo:

- il build somiglia all'ultimo build accettato;
- il passo 2 non trova il nome, oppure il passo 3 lo mostra pubblico su iOS;
- altri sviluppatori segnalano lo stesso errore lo stesso giorno sui forum di Apple.

In questo caso controlla lo [stato dei servizi Apple](https://developer.apple.com/system-status/), poi rinvia un build con un nuovo numero. Se l'errore persiste, scrivi al [supporto sviluppatori di Apple](https://developer.apple.com/contact/) allegando l'email completa. Una collisione di nomi, invece, per Apple non è un falso positivo: rinomina.

## Prima del tuo prossimo invio

Anche gli altri errori ITMS hanno la loro guida: [ITMS-90725](https://devoria.fr/it/blog/itms-90725-sdk-version-issue-expo) per la versione di Xcode, [ITMS-91053](https://devoria.fr/it/blog/itms-91053-missing-api-declaration-expo) per il privacy manifest e [ITMS-90034](https://devoria.fr/it/blog/itms-90034-missing-invalid-signature) per la firma. Una volta accettato il build, installalo con [TestFlight](https://devoria.fr/it/blog/testflight-testare-app-iphone-prima-invio) prima della revisione.

I rifiuti di un reviewer richiedono più distacco: il procedimento è nella pagina [app rifiutata da Apple](https://devoria.fr/it/expertise/app-rifiutata-da-apple), e i motivi più frequenti sono in [perché Apple rifiuta un'app](https://devoria.fr/it/blog/app-rifiutata-da-apple-perche-casi-reali).

Per controllare il tuo progetto prima dell'invio, il [Review Kit](https://devoria.fr/it/kits/app-review) riunisce la mia checklist e delle skill di Claude Code che rileggono il tuo progetto. Se il tuo invio è bloccato e vuoi uno sguardo umano, [prenota un'ora con me](https://devoria.fr/it/coaching): riprendiamo il tuo progetto in videochiamata, sul tuo schermo. E se stai preparando la tua prima pubblicazione, [fai la diagnosi gratuita](https://devoria.fr/it/diagnostic): rimette in ordine i passaggi e ti invia la mia checklist anti-rifiuto Apple.

## Domande frequenti

### ITMS-90338 è un rifiuto dell'App Review?

No. È un controllo automatico fatto durante l'elaborazione del build, prima di qualsiasi revisione umana. Il build rifiutato non arriva né su TestFlight né in revisione. Un'API privata individuata più tardi da un reviewer ricade invece nella guideline 2.5.1.

### Si può correggere ITMS-90338 con un aggiornamento da remoto?

No. I nomi citati sono nel codice nativo compilato del binario. Un aggiornamento da remoto cambia solo il JavaScript e le risorse. Correggi la dipendenza, rifai il build, poi rinvialo.

### Perché Apple cita metodi che non ho mai scritto?

Perché arrivano da una libreria inclusa, oppure da una collisione: una dipendenza dichiara un metodo con il nome di un'API privata. Apple legge i nomi presenti nel binario, non il tuo codice sorgente. Risali al pacchetto con `grep` e `npm explain`.

### Come capire se un'API è pubblica su iOS?

Cerca il suo nome negli header dell'SDK iOS, il cui percorso ti viene dato da `xcrun --sdk iphoneos --show-sdk-path`, poi leggi la sua disponibilità. Lo indica anche la pagina di documentazione di Apple. Una funzione documentata solo per macOS non è pubblica su iPhone.

### Bisogna cambiare il numero di build dopo la correzione?

Sì. Lascia che sia EAS a incrementarlo: un numero già inviato per la stessa versione può essere rifiutato con ITMS-90189.


## Il tuo progetto

[Review Kit](https://devoria.fr/it/kits/app-review): La mia checklist per la review dell'App Store, i rifiuti di Apple che ho risolto, quella di Google Play e tre skill per Claude Code. 49 €.

[iOS & Android Kit](https://devoria.fr/it/kits/ios): Crea la tua app iPhone e Android con Claude Code: il codice di partenza delle mie app, il percorso fino agli store e tutti i kit. 99 €.

[Diagnosi: cosa ti separa dalla tua app per iPhone](https://devoria.fr/it/diagnostic): 7 domande, 60 secondi: scopri cosa ti blocca davvero nel pubblicare la tua app per iPhone, il tuo percorso in 4 tappe e il kit giusto per te.
