ITMS-90338: fixa ”Non-public API usage” i Expo
Av Mehdi Azizi
Publicerad , 9 min
ITMS-90338: ditt build refererar till en metod, funktion eller klass som Apple anser vara privat. Hitta beroendet, uppdatera det och bygg om.
ITMS-90338 är en automatisk kontroll som Apple gör på ditt build. Den säger att din binär refererar till API:er som Apple betraktar som privata: Objective-C-metoder, funktioner eller klasser. Lösningen: hitta filen som nämns i mejlet, spåra den till beroendet som innehåller namnet, uppdatera det eller byt namn på din metod, och bygg sedan om.
Med Expo kommer namnet nästan alltid från ett native-bibliotek, sällan från din JavaScript. Den 15 oktober 2026 körde jag analyskommandona i den här guiden på ett simulatorbuild av Solys, min väckarklocka med minispel (Expo SDK 57), och på iOS-SDK:n i Xcode 26.2. Så här läser du mejlet, hittar den skyldiga och känner igen ett falsklarm.
Vad betyder felet ITMS-90338?
Meddelandet har rubriken ”ITMS-90338: Non-public API usage”. Det finns i tre varianter, beroende på vad Apple hittade i din binär:
- ”references non-public selectors”: namn på Objective-C-metoder, som
boldFontOfSize:; - ”references non-public symbols”: funktioner, skrivna med understreck först, som
__dyld_get_shared_cache_range; - ”contains or inherits from non-public classes”: ett klassnamn.
Varje variant anger en sökväg och sedan listan med namn. Texten ger också två råd: byt namn på dina metoder om namnet matchar ett privat API, och ta bort det statiska bibliotek som innehåller dem. Ett mejl från september 2026 föreslår dessutom xcrun swift-demangle för att göra kodade Swift-namn läsbara.
Regeln bakom kontrollen är guideline 2.5.1: en app får bara använda publika API:er. Licensavtalet för Apple Developer Program säger samma sak i punkt 3.3.1: inga anrop till privata API:er. Den manuella granskningen av regeln beskrivs i guiden om guideline 2.5.1. Här handlar det om uppladdningsfelet.
Precis som med andra ITMS-fel har ingen öppnat din app: Apple analyserade binären när buildet bearbetades. Apple publicerar ingen lista över privata API:er: namnen i ditt mejl är den enda pålitliga listan.
Läs sökvägen: en SDK eller din körbara fil?
Sökvägen före listan visar vilken fil du ska leta i.
Frameworks/Nom.framework/Nom: namnet finns i ett inbäddat dynamiskt framework, alltså i en specifik SDK. I Solys build innehåller mappen 14 frameworks: Hermes, React Native, förkompilerade Expo-moduler och bildbiblioteket SDWebImage.- Bara appens namn: namnet finns i den huvudsakliga körbara filen. Med Expo samlar den din native-kod och de pods som kompileras från källkod, statiskt länkade som standard. Vilket native-bibliotek som helst kan då vara orsaken.
Beslutsträdet: från namnet till lösningen
Gå igenom raderna i ordning. Den första som matchar ditt fall visar vägen.
| Det du ser | Var du letar | Lösning |
|---|---|---|
Namn med $s eller _$s | xcrun swift-demangle | Gå igenom trädet igen |
| Finns inte i din IPA | grep på bundlen | Skicka ett nytt build |
| Publikt namn på iOS | iOS-SDK:ns headers | Skicka ett nytt build |
API_UNAVAILABLE(ios) | iOS-SDK:ns headers | Uppdatera paketet |
I Frameworks/X | SDK:n som levererar X | Uppdatera SDK:n |
| Selektor, din app | Pods och native-moduler | Lägg till prefix/uppdatera |
Symbol _namn, din app | nm -m, sedan pods | Ersätt anropet |
| Klass, din app | Dina klasser och pods | Lägg prefix på klassen |
Raderna ”Skicka ett nytt build” pekar på ett falsklarm, som tas upp längre ner.
Steg 1: öppna det uppladdade buildet
Arbeta med filen du faktiskt skickade till Apple, inte med ditt projekt. Ladda ner IPA-filen från buildets sida på expo.dev och kör sedan på din Mac:
unzip -q MyApp.ipa -d ipa
ls ipa/Payload/MyApp.app/Frameworks
otool -L ipa/Payload/MyApp.app/MyApp | grep PrivateFrameworks
Andra raden listar de inbäddade frameworken. Tredje raden letar efter en direkt länk till ett privat Apple-framework: den ska inte visa något. På Solys hittar otool -L inget privat framework, varken i den körbara filen eller i de 14 frameworken.
Steg 2: hitta filen som innehåller namnet
Börja brett. Sök efter namnet, utan de inledande understrecken, i hela bundlen:
grep -rl "dyld_get_shared_cache_range" ipa/Payload/MyApp.app
Kommandot listar filerna som innehåller texten. Precisera sedan efter typ av namn (selektor, symbol, klass) i den hittade filen, här kallad Binaire:
strings -a Binaire | grep -x "boldFontOfSize:"
nm -m Binaire | grep "_dyld_get_shared_cache_range"
nm -m Binaire | grep '_OBJC_CLASS_$_NomDeClasse'
För en selektor, använd strings, inte nm. På Solys React-framework visar nm metoder tillsammans med sin klass, som -[RCTRootView initWithBridge:moduleName:initialProperties:], aldrig selektorn initWithBridge: ensam. strings hittar den.
För en symbol, läs slutet av raden. På Solys visar nm -m till exempel (undefined) external __dyld_get_image_name (from libSystem): binären anropar funktionen utan att innehålla den, och libSystem levererar den. Just den funktionen är publik, vilket nästa steg visar.
Steg 3: kontrollera om namnet verkligen är privat
iOS-SDK:ns headers beskriver de publika API:erna. En sökning på några sekunder visar om namnet finns där, och på vilka plattformar. För en symbol tar du först bort understrecket som läggs till vid kompileringen: _SCDynamicStoreCreate blir SCDynamicStoreCreate.
SDK=$(xcrun --sdk iphoneos --show-sdk-path)
grep -rn "SCDynamicStoreCreate" "$SDK/usr/include" "$SDK"/System/Library/Frameworks/*/Headers
Den 15 oktober 2026, med iOS SDK 26.2, gav fyra namn från riktiga ITMS-90338-mejl detta:
| Nämnt namn | I iOS-SDK:n | Tolkning |
|---|---|---|
| _dyld_get_shared_cache_range | Saknas | Privat API |
| boldFontOfSize: | Saknas | Privat eller kollision |
| SCDynamicStoreCreate | API_UNAVAILABLE(ios) | Bara macOS |
| isMainFrame | WebKit-egenskap | Publik |
Ett namn som finns och är tillgängligt på iOS pekar på ett falsklarm. Ett namn som saknas pekar på ett privat API eller en kollision. Två begränsningar: ett API som bara är publikt i Swift syns inte i dessa headers, och för en accessor som setDelegate: söker du efter egenskapen delegate.
Steg 4: spåra till paketet
Om sökvägen nämner ett framework leder dess namn till podden och sedan till npm-paketet:
grep -n "NomDuFramework" ios/Podfile.lock
npm explain nom-du-paquet
npm explain visar vilket beroende som installerade paketet, om du aldrig lagt till det själv.
Om sökvägen nämner den körbara filen, sök efter namnet i native-källkoden och sedan i pods:
grep -rl --include='*.m' --include='*.mm' --include='*.swift' --include='*.h' \
"boldFontOfSize" node_modules modules
grep -rl "boldFontOfSize" ios/Pods
Anpassa listan med mappar: modules är mappen för lokala Expo-moduler. Mappen ios/Pods dyker upp efter npx expo prebuild -p ios, som också installerar pods på Mac. Se upp: kommandot kan återskapa mappen ios. Om du har ändrat den för hand, jobba på en kopia.
Sökningen i ios/Pods är lika viktig som sökningen i källkoden. På Solys innehåller mappen 15 förkompilerade frameworks i formatet .xcframework: Expo-moduler, Hermes, React Native och tredjeparts-SDK:er. En sökning som bara tittar på källfiler missar deras kod.
Vanliga orsaker
En tredjeparts-SDK, för gammal eller för ny. I september 2026 rapporterade en Flutter-utvecklare ett avslag orsakat av en säkerhets-SDK: version 8.2.3 anropade __dyld_get_shared_cache_range. Version 8.2.2 gick igenom, och utgivaren rådde att gå tillbaka till den i väntan på en rättning.
En native-modul som anropar ett odokumenterat API. Din egen modul, ett plugin eller ett litet bibliotek kan använda en intern UIKit-metod som någon hittat på ett forum. Kontrollen hittar den via namnet.
En namnkollision. Din metod eller klass råkar heta samma sak som ett privat Apple-API. Meddelandet tar höjd för det och ber dig byta namn. Apple rekommenderar att du lägger prefix på metoder du lägger till i deras klasser, till exempel tre gemener följda av ett understreck.
Ett API som är publikt på macOS men inte på iOS. 2024 blev en .NET MAUI-app avvisad för _SCDynamicStoreCreate. Apple dokumenterar visserligen funktionen, men bara för macOS, och headern i iOS-SDK:n markerar den API_UNAVAILABLE(ios, tvos, watchos).
Fixa utan att fuska
- Uppdatera beroendet. För paket som Expo följer signalerar
npx expo install --checkde som inte matchar din SDK, enligt Expo CLI-dokumentationen. För övriga, läs versionsloggen. - Gå tillbaka till föregående version om det var den nya som införde namnet, och meddela utgivaren.
- Ersätt eller ta bort ett övergivet bibliotek.
- Byt namn på din metod eller klass, med ett prefix.
Dölj inte namnet. Att dela upp en sträng eller anropa metoden via en omväg döljer användningen utan att lösa den. Det är fortfarande förbjudet enligt regel 2.5.1 och avtalet, och då är det ditt konto som står på spel.
Bygg sedan om med ett nytt buildnummer. En fjärruppdatering räcker inte: namnet sitter i den kompilerade native-koden. Innan du laddar upp, kör steg 2 igen på den nya IPA-filen. Namnet ska inte längre synas.
När det är ett falsklarm
Apples kontroll har ibland fel. Den 6 juni 2022 fick React Native- och Flutter-appar ITMS-90338 för publika WebKit-namn, som setNavigationDelegate: eller isMainFrame, även på nästan oförändrade uppdateringar. Dagen efter skrev en Apple-ingenjör att problemet var löst i valideringen och bad utvecklarna skicka sina builds igen. Tråden finns kvar i Expos GitHub-repo.
Tre tecken tillsammans pekar på ett falsklarm:
- buildet liknar det senaste godkända buildet;
- steg 2 hittar inte namnet, eller steg 3 visar att det är publikt på iOS;
- andra utvecklare rapporterar samma fel samma dag på Apples forum.
I så fall, kolla Apples tjänststatus och skicka sedan ett build med nytt nummer. Om felet kvarstår, skriv till Apples utvecklarsupport med hela mejlet. En namnkollision är däremot inget falsklarm i Apples ögon: byt namn.
Innan nästa uppladdning
De andra ITMS-felen har också sina guider: ITMS-90725 för Xcode-versionen, ITMS-91053 för integritetsmanifestet och ITMS-90034 för signaturen. När buildet är godkänt, installera det via TestFlight före granskningen.
Avslag från en granskare kräver mer eftertanke: arbetssättet finns på sidan app avvisad av Apple, och de vanligaste orsakerna i varför Apple avvisar en app.
För att kontrollera ditt underlag före uppladdning samlar Review Kit min checklista och Claude Code-skills som går igenom ditt projekt. Om din uppladdning fastnar och du vill ha en människa som tittar, boka en timme med mig: vi går igenom ditt projekt i ett videosamtal, på din skärm. Och om du förbereder din första release, gör din gratis diagnostik: den sätter stegen i rätt ordning och skickar dig min checklista mot avslag från Apple.
Vanliga frågor
Är ITMS-90338 ett avslag från App Review?
Nej. Det är en automatisk kontroll som görs när buildet bearbetas, före all manuell granskning. Det avvisade buildet hamnar varken i TestFlight eller i granskning. Ett privat API som en granskare hittar senare faller däremot under guideline 2.5.1.
Kan man fixa ITMS-90338 med en fjärruppdatering?
Nej. Namnen sitter i binärens kompilerade native-kod. En fjärruppdatering ändrar bara JavaScript och resurser. Rätta beroendet, bygg om och skicka det igen.
Varför nämner Apple metoder som jag aldrig har skrivit?
För att de kommer från ett inbäddat bibliotek, eller från en kollision: ett beroende deklarerar en metod med samma namn som ett privat API. Apple läser namnen i binären, inte din källkod. Spåra till paketet med grep och npm explain.
Hur vet man om ett API är publikt på iOS?
Sök efter namnet i iOS-SDK:ns headers, vars sökväg du får med xcrun --sdk iphoneos --show-sdk-path, och läs dess tillgänglighet. Apples dokumentationssida visar det också. En funktion som bara är dokumenterad för macOS är inte publik på iPhone.
Måste man ändra buildnumret efter rättningen?
Ja. Låt EAS räkna upp det: ett nummer som redan skickats för samma version kan avvisas med ITMS-90189.

