ITMS-90338: fiks «Non-public API usage» med Expo
Av Mehdi Azizi
Publisert , 9 min
ITMS-90338: builden din nevner en metode, funksjon eller klasse Apple anser som privat. Finn avhengigheten, oppdater den og lag en ny build.
ITMS-90338 er en automatisk kontroll Apple kjører på builden din. Den sier at binærfilen din refererer til API-er som Apple regner som private: Objective-C-metoder, funksjoner eller klasser. Løsningen: finn filen som nevnes i e-posten, spor deg tilbake til avhengigheten som inneholder navnet, oppdater den eller gi metoden din nytt navn, og lag så en ny build.
Med Expo kommer navnet nesten alltid fra et native bibliotek, sjelden fra JavaScript-koden din. 15. oktober 2026 kjørte jeg analysekommandoene i denne guiden på en simulator-build av Solys, vekkerklokken min med minispill (Expo SDK 57), og på iOS-SDK-en i Xcode 26.2. Her ser du hvordan du leser e-posten, finner synderen og kjenner igjen en falsk positiv.
Hva betyr feilen ITMS-90338?
Meldingen har tittelen «ITMS-90338: Non-public API usage». Den finnes i tre varianter, avhengig av hva Apple fant i binærfilen din:
- «references non-public selectors»: navn på Objective-C-metoder, som
boldFontOfSize:; - «references non-public symbols»: funksjoner, skrevet med understrek foran, som
__dyld_get_shared_cache_range; - «contains or inherits from non-public classes»: et klassenavn.
Hver variant oppgir en sti og deretter listen over navn. Teksten gir to råd i tillegg: gi metodene dine nytt navn hvis navnet tilsvarer et privat API, og fjern det statiske biblioteket som inneholder dem. En e-post fra september 2026 foreslår også xcrun swift-demangle for å gjøre kodede Swift-navn lesbare.
Regelen bak kontrollen er guideline 2.5.1: en app kan bare bruke offentlige API-er. Lisensavtalen for Apple Developer Program gjentar det i punkt 3.3.1: ingen kall til private API-er. Den manuelle gjennomgangen av denne regelen er beskrevet i guiden om guideline 2.5.1. Her tar vi for oss opplastingsfeilen.
Som med de andre ITMS-feilene har ingen åpnet appen din: Apple analyserte binærfilen mens builden ble behandlet. Apple publiserer ingen liste over private API-er: navnene i e-posten din er den eneste pålitelige listen.
Les stien: en SDK eller den kjørbare filen din?
Stien som står før listen, forteller deg hvilken fil du skal lete i.
Frameworks/Nom.framework/Nom: navnet ligger i et innebygd dynamisk framework, altså i en bestemt SDK. I Solys-builden inneholder denne mappen 14 frameworks: Hermes, React Native, Expo-moduler levert ferdig kompilert og bildebiblioteket SDWebImage.- Bare navnet på appen din: navnet ligger i den kjørbare hovedfilen. Med Expo samler den din native kode og podene som er kompilert fra kildekode, statisk lenket som standard. Da kan hvilket som helst native bibliotek være årsaken.
Beslutningstreet: fra navnet til løsningen
Gå gjennom radene i rekkefølge. Den første som passer med ditt tilfelle, gir sporet.
| Det du ser | Hvor du leter | Løsning |
|---|---|---|
Navn med $s eller _$s | xcrun swift-demangle | Start treet på nytt |
| Finnes ikke i IPA-en | grep på bundelen | Send en ny build |
| Offentlig navn på iOS | Headerne i iOS-SDK-en | Send en ny build |
API_UNAVAILABLE(ios) | Headerne i iOS-SDK-en | Oppdater pakken |
I Frameworks/X | SDK-en som leverer X | Oppdater SDK-en |
| Selektor, appen din | Pods og native moduler | Legg til prefiks eller oppdater |
Symbol _navn, appen din | nm -m, deretter podene | Erstatt kallet |
| Klasse, appen din | Klassene og podene dine | Gi klassen et prefiks |
Radene «Send en ny build» peker mot en falsk positiv, som behandles lenger ned.
Steg 1: åpne builden du sendte inn
Jobb med filen som faktisk ble sendt til Apple, ikke med prosjektet ditt. Last ned IPA-en fra build-siden på expo.dev, og kjør dette på Macen din:
unzip -q MonApp.ipa -d ipa
ls ipa/Payload/MonApp.app/Frameworks
otool -L ipa/Payload/MonApp.app/MonApp | grep PrivateFrameworks
Den andre linjen lister de innebygde frameworkene. Den tredje leter etter en direkte lenke til et privat Apple-framework: den skal ikke vise noe. På Solys finner otool -L ingen private frameworks, verken i den kjørbare filen eller i de 14 frameworkene.
Steg 2: finn filen som inneholder navnet
Start bredt. Søk etter navnet, uten understrekene i starten, i hele bundelen:
grep -rl "dyld_get_shared_cache_range" ipa/Payload/MonApp.app
Kommandoen lister filene som inneholder teksten. Snevre deretter inn etter type navn (selektor, symbol, klasse) i filen du fant, her kalt Binaire:
strings -a Binaire | grep -x "boldFontOfSize:"
nm -m Binaire | grep "_dyld_get_shared_cache_range"
nm -m Binaire | grep '_OBJC_CLASS_$_NomDeClasse'
For en selektor bruker du strings, ikke nm. På React-frameworket i Solys viser nm metoder sammen med klassen, som -[RCTRootView initWithBridge:moduleName:initialProperties:], aldri selektoren initWithBridge: alene. strings finner den.
For et symbol leser du slutten av linjen. På Solys viser nm -m for eksempel (undefined) external __dyld_get_image_name (from libSystem): binærfilen kaller funksjonen uten å inneholde den, og libSystem leverer den. Akkurat den funksjonen er offentlig, som neste steg viser.
Steg 3: sjekk om navnet faktisk er privat
Headerne i iOS-SDK-en beskriver de offentlige API-ene. Et søk på noen sekunder forteller om navnet finnes der, og på hvilke plattformer. For et symbol fjerner du først understreken som legges til ved kompilering: _SCDynamicStoreCreate blir SCDynamicStoreCreate.
SDK=$(xcrun --sdk iphoneos --show-sdk-path)
grep -rn "SCDynamicStoreCreate" "$SDK/usr/include" "$SDK"/System/Library/Frameworks/*/Headers
- oktober 2026, med iOS SDK 26.2, ga fire navn hentet fra ekte ITMS-90338-e-poster dette:
| Navn i e-posten | I iOS-SDK-en | Tolkning |
|---|---|---|
| _dyld_get_shared_cache_range | Mangler | Privat API |
| boldFontOfSize: | Mangler | Privat eller kollisjon |
| SCDynamicStoreCreate | API_UNAVAILABLE(ios) | Bare macOS |
| isMainFrame | WebKit-egenskap | Offentlig |
Et navn som finnes og er tilgjengelig på iOS, peker mot en falsk positiv. Et navn som mangler, peker mot et privat API eller en kollisjon. To begrensninger: et offentlig API som bare finnes i Swift, dukker ikke opp i disse headerne, og for en accessor som setDelegate: søker du etter egenskapen delegate.
Steg 4: spor deg tilbake til pakken
Hvis stien nevner et framework, leder navnet deg til poden og deretter til npm-pakken:
grep -n "NomDuFramework" ios/Podfile.lock
npm explain nom-du-paquet
npm explain viser hvilken avhengighet som installerte den, hvis du aldri har lagt den til selv.
Hvis stien nevner den kjørbare filen, søker du etter navnet i de native kildene og deretter i podene:
grep -rl --include='*.m' --include='*.mm' --include='*.swift' --include='*.h' \
"boldFontOfSize" node_modules modules
grep -rl "boldFontOfSize" ios/Pods
Tilpass mappelisten: modules er mappen for lokale Expo-moduler. Mappen ios/Pods dukker opp etter npx expo prebuild -p ios, som også installerer podene på Mac. Pass på: kommandoen kan gjenskape ios-mappen. Har du endret den for hånd, jobber du på en kopi.
Søket i ios/Pods er like viktig som søket i kildene. På Solys inneholder denne mappen 15 frameworks levert ferdig kompilert, i .xcframework-format: Expo-moduler, Hermes, React Native og tredjeparts-SDK-er. Et søk som bare ser på kildefiler, ville gått glipp av koden deres.
Typiske årsaker
En tredjeparts-SDK som er for gammel eller for ny. I september 2026 rapporterte en Flutter-utvikler en avvisning på grunn av en sikkerhets-SDK: versjon 8.2.3 kalte __dyld_get_shared_cache_range. Versjon 8.2.2 gikk gjennom, og utgiveren anbefalte å gå tilbake til den i påvente av en rettelse.
En native modul som kaller et udokumentert API. Din egen modul, en plugin eller et lite bibliotek kan bruke en intern UIKit-metode funnet på et forum. Kontrollen fanger den opp på navnet.
En navnekollisjon. Metoden eller klassen din har tilfeldigvis samme navn som et privat Apple-API. Meldingen tar høyde for dette og ber deg gi den nytt navn. Apple anbefaler å gi et prefiks til metoder du legger til i klassene deres, for eksempel tre små bokstaver etterfulgt av en understrek.
Et API som er offentlig på macOS, men ikke på iOS. I 2024 ble en .NET MAUI-app avvist for _SCDynamicStoreCreate. Apple dokumenterer denne funksjonen, men bare for macOS, og headeren i iOS-SDK-en merker den API_UNAVAILABLE(ios, tvos, watchos).
Fiks det uten å omgå det
- Oppdater avhengigheten. For pakkene Expo følger med på, viser
npx expo install --checkhvilke som ikke passer med SDK-en din, ifølge dokumentasjonen for Expo CLI. For de andre leser du endringsloggen. - Gå tilbake til forrige versjon hvis det var den nye som innførte navnet, og si fra til utgiveren.
- Erstatt eller fjern et forlatt bibliotek.
- Gi nytt navn til metoden eller klassen din, med et prefiks.
Ikke skjul navnet. Å dele opp en streng eller kalle metoden via en omvei skjuler bruken uten å fikse den. Den er fortsatt forbudt etter regel 2.5.1 og avtalen, og da er det kontoen din som står i fare.
Lag deretter en ny build med et nytt buildnummer. En fjernoppdatering holder ikke: navnet ligger i den kompilerte native koden. Før du sender inn, kjører du steg 2 på nytt på den nye IPA-en. Navnet skal ikke dukke opp lenger.
Når det er en falsk positiv
Apples kontroll tar av og til feil. 6. juni 2022 fikk React Native- og Flutter-apper ITMS-90338 for offentlige WebKit-navn, som setNavigationDelegate: eller isMainFrame, også på oppdateringer som nesten ikke var endret. Dagen etter skrev en Apple-ingeniør at problemet var løst på valideringssiden, og ba folk sende buildene på nytt. Tråden er bevart i Expos GitHub-repo.
Tre tegn til sammen peker mot en falsk positiv:
- builden ligner på den siste godkjente builden;
- steg 2 finner ikke navnet, eller steg 3 viser at det er offentlig på iOS;
- andre utviklere melder om samme feil samme dag på Apples forum.
I så fall sjekker du statusen for Apples tjenester og sender en ny build med nytt nummer. Hvis feilen vedvarer, skriver du til Apples utviklerstøtte med hele e-posten. En navnekollisjon er derimot ikke en falsk positiv i Apples øyne: gi nytt navn.
Før neste innsending
De andre ITMS-feilene har også sin egen guide: ITMS-90725 for Xcode-versjonen, ITMS-91053 for personvernmanifestet og ITMS-90034 for signaturen. Når builden er godkjent, installerer du den med TestFlight før gjennomgangen.
Avvisninger fra en reviewer krever mer ettertanke: fremgangsmåten står på siden om app avvist av Apple, og de vanligste årsakene finner du i hvorfor Apple avviser en app.
For å sjekke prosjektet ditt før innsending samler Review Kit sjekklisten min og Claude Code-skills som går gjennom prosjektet ditt. Står innsendingen fast og du vil ha et menneskelig blikk på det, book en time med meg: vi går gjennom prosjektet ditt på video, på skjermen din. Og forbereder du din første lansering, ta den gratis diagnosen: den setter stegene i riktig rekkefølge og sender deg sjekklisten min mot avvisninger fra Apple.
Ofte stilte spørsmål
Er ITMS-90338 en avvisning fra App Review?
Nei. Det er en automatisk kontroll som gjøres mens builden behandles, før noen manuell gjennomgang. Den avviste builden kommer verken til TestFlight eller til gjennomgang. Et privat API som en reviewer oppdager senere, hører derimot inn under guideline 2.5.1.
Kan man fikse ITMS-90338 med en fjernoppdatering?
Nei. Navnene som nevnes, ligger i den kompilerte native koden i binærfilen. En fjernoppdatering endrer bare JavaScript og ressurser. Fiks avhengigheten, lag en ny build og send den inn på nytt.
Hvorfor nevner Apple metoder jeg aldri har skrevet?
Fordi de kommer fra et innebygd bibliotek, eller fra en kollisjon: en avhengighet deklarerer en metode med samme navn som et privat API. Apple leser navnene som finnes i binærfilen, ikke kildekoden din. Spor deg tilbake til pakken med grep og npm explain.
Hvordan vet jeg om et API er offentlig på iOS?
Søk etter navnet i headerne i iOS-SDK-en, der xcrun --sdk iphoneos --show-sdk-path gir stien, og les tilgjengeligheten. Apples dokumentasjonsside viser det også. En funksjon som bare er dokumentert for macOS, er ikke offentlig på iPhone.
Må jeg endre buildnummeret etter rettelsen?
Ja. La EAS øke det: et nummer som allerede er sendt inn for samme versjon, kan bli avvist med ITMS-90189.

