ITMS-90338: oprava „Non-public API usage“ v Expo
Autor: Mehdi Azizi
Publikováno , 9 min
ITMS-90338: tvůj build obsahuje metodu, funkci nebo třídu, kterou Apple považuje za privátní. Najdi viníka mezi závislostmi, aktualizuj ji a udělej nový build.
ITMS-90338 je automatická kontrola, kterou Apple provádí na tvém buildu. Říká, že tvůj binární soubor odkazuje na API, která Apple považuje za privátní: metody Objective-C, funkce nebo třídy. Oprava: najdi soubor uvedený v e-mailu, dohledej závislost, která daný název obsahuje, aktualizuj ji nebo přejmenuj svou metodu a pak udělej nový build.
U Expo pochází název skoro vždy z nativní knihovny, málokdy z tvého JavaScriptu. 15. října 2026 jsem příkazy z tohohle návodu spustil na simulátorovém buildu Solys, mého budíku s minihrami (Expo SDK 57), a na iOS SDK z Xcode 26.2. Ukážu ti, jak číst e-mail, najít viníka a poznat falešný poplach.
Co znamená chyba ITMS-90338?
Zpráva má název „ITMS-90338: Non-public API usage“. Existuje ve třech variantách podle toho, co Apple v tvém binárním souboru našel:
- „references non-public selectors“: názvy metod Objective-C, třeba
boldFontOfSize:; - „references non-public symbols“: funkce, psané s podtržítkem na začátku, třeba
__dyld_get_shared_cache_range; - „contains or inherits from non-public classes“: název třídy.
Každá varianta uvádí cestu a pak seznam názvů. Text přidává dvě rady: přejmenovat tvé metody, pokud se jejich název shoduje s privátním API, a odstranit statickou knihovnu, která je obsahuje. E-mail ze září 2026 navíc nabízí xcrun swift-demangle, aby se zakódované názvy ze Swiftu daly přečíst.
Pravidlo za touhle kontrolou je guideline 2.5.1: aplikace smí používat jen veřejná API. Licenční smlouva Apple Developer Program to opakuje v článku 3.3.1: žádné volání privátního API. Lidskou kontrolu tohoto pravidla podrobně rozebírá návod ke guideline 2.5.1. Tady řešíme chybu při nahrávání.
Stejně jako u ostatních chyb ITMS tvou aplikaci nikdo neotevřel: Apple analyzoval binární soubor během zpracování buildu. Apple žádný seznam privátních API nezveřejňuje: názvy z tvého e-mailu jsou jediný spolehlivý seznam.
Čtení cesty: SDK, nebo tvůj spustitelný soubor?
Cesta uvedená před seznamem ti řekne, ve kterém souboru hledat.
Frameworks/Nom.framework/Nom: název je ve vloženém dynamickém frameworku, tedy v konkrétním SDK. V buildu Solys obsahuje tahle složka 14 frameworků: Hermes, React Native, moduly Expo dodávané už zkompilované a knihovnu na obrázky SDWebImage.- Jen název tvé aplikace: název je v hlavním spustitelném souboru. U Expo ten spojuje tvůj nativní kód a pody zkompilované ze zdrojáků, ve výchozím stavu linkované staticky. Na vině tak může být jakákoli nativní knihovna.
Strom: od uvedeného názvu k opravě
Projdi řádky popořadě. První, který odpovídá tvému případu, ti dá stopu.
| Co čteš | Kde hledat | Oprava |
|---|---|---|
Název s $s nebo _$s | xcrun swift-demangle | Projít strom znovu |
| V IPA se nedá najít | grep na bundle | Poslat build znovu |
| Veřejný název na iOS | Hlavičky iOS SDK | Poslat build znovu |
API_UNAVAILABLE(ios) | Hlavičky iOS SDK | Aktualizovat balíček |
V Frameworks/X | SDK, které dodává X | Aktualizovat SDK |
| Selektor, tvoje aplikace | Pody a nativní moduly | Přidat prefix/aktualizovat |
Symbol _nom, tvoje aplikace | nm -m, pak pody | Nahradit volání |
| Třída, tvoje aplikace | Tvoje třídy a pody | Přidat prefix ke třídě |
Řádky „Poslat build znovu“ označují falešný poplach, o kterém je řeč níž.
Krok 1: otevři odeslaný build
Pracuj se souborem, který jsi Applu skutečně poslal, ne se svým projektem. Stáhni IPA ze stránky buildu na expo.dev a na Macu spusť:
unzip -q MyApp.ipa -d ipa
ls ipa/Payload/MyApp.app/Frameworks
otool -L ipa/Payload/MyApp.app/MyApp | grep PrivateFrameworks
Druhý řádek vypíše vložené frameworky. Třetí hledá přímý odkaz na privátní framework Applu: nesmí nic vypsat. U Solys otool -L nenajde žádný privátní framework, ani ve spustitelném souboru, ani ve 14 frameworcích.
Krok 2: najdi soubor, který název obsahuje
Začni zeširoka. Hledej uvedený název, bez úvodních podtržítek, v celém bundlu:
grep -rl "dyld_get_shared_cache_range" ipa/Payload/MyApp.app
Příkaz vypíše soubory, které tenhle text obsahují. Pak hledání zpřesni podle typu názvu (selektor, symbol, třída) na nalezeném souboru, označeném Binaire:
strings -a Binaire | grep -x "boldFontOfSize:"
nm -m Binaire | grep "_dyld_get_shared_cache_range"
nm -m Binaire | grep '_OBJC_CLASS_$_NomDeClasse'
Na selektor použij strings, ne nm. Na frameworku React v Solys nm vypisuje metody i s jejich třídou, třeba -[RCTRootView initWithBridge:moduleName:initialProperties:], nikdy samotný selektor initWithBridge:. strings ho najde.
U symbolu čti konec řádku. V Solys nm -m vypíše třeba (undefined) external __dyld_get_image_name (from libSystem): binární soubor funkci volá, ale neobsahuje ji, dodává ji libSystem. Tahle konkrétní funkce je veřejná, jak ukáže další krok.
Krok 3: ověř, jestli je název opravdu privátní
Hlavičky iOS SDK popisují veřejná API. Hledání na pár sekund ti řekne, jestli tam uvedený název je a na kterých platformách. U symbolu nejdřív odstraň podtržítko přidané při kompilaci: z _SCDynamicStoreCreate se stane SCDynamicStoreCreate.
SDK=$(xcrun --sdk iphoneos --show-sdk-path)
grep -rn "SCDynamicStoreCreate" "$SDK/usr/include" "$SDK"/System/Library/Frameworks/*/Headers
- října 2026 s iOS SDK 26.2 dopadly čtyři názvy ze skutečných e-mailů ITMS-90338 takhle:
| Uvedený název | V iOS SDK | Výklad |
|---|---|---|
| _dyld_get_shared_cache_range | Chybí | Privátní API |
| boldFontOfSize: | Chybí | Privátní nebo kolize |
| SCDynamicStoreCreate | API_UNAVAILABLE(ios) | Jen macOS |
| isMainFrame | Vlastnost WebKitu | Veřejné |
Název, který tam je a je dostupný na iOS, ukazuje na falešný poplach. Chybějící název ukazuje na privátní API nebo kolizi. Dvě omezení: veřejné API dostupné jen ve Swiftu se v těchto hlavičkách neobjeví a u přístupové metody jako setDelegate: hledej vlastnost delegate.
Krok 4: dohledej balíček
Pokud cesta uvádí framework, jeho název tě dovede k podu a pak k npm balíčku:
grep -n "NomDuFramework" ios/Podfile.lock
npm explain nom-du-paquet
npm explain ti řekne, která závislost ho nainstalovala, pokud sis ho nikdy nepřidal sám.
Pokud cesta uvádí spustitelný soubor, hledej název v nativních zdrojácích a pak v podech:
grep -rl --include='*.m' --include='*.mm' --include='*.swift' --include='*.h' \
"boldFontOfSize" node_modules modules
grep -rl "boldFontOfSize" ios/Pods
Seznam složek si uprav: modules je složka lokálních modulů Expo. Složka ios/Pods se objeví po npx expo prebuild -p ios, který na Macu zároveň nainstaluje pody. Pozor: může znovu vytvořit složku ios. Pokud jsi ji ručně upravoval, pracuj na kopii.
Hledání v ios/Pods je stejně důležité jako hledání ve zdrojácích. V Solys obsahuje tahle složka 15 frameworků dodaných už zkompilovaných ve formátu .xcframework: moduly Expo, Hermes, React Native a SDK třetích stran. Hledání omezené na zdrojové soubory by jejich kód minulo.
Typické příčiny
SDK třetí strany, příliš staré nebo příliš nové. V září 2026 jeden vývojář ve Flutteru nahlásil zamítnutí kvůli bezpečnostnímu SDK: jeho verze 8.2.3 volala __dyld_get_shared_cache_range. Verze 8.2.2 prošla a vydavatel doporučil vrátit se k ní, dokud nevyjde oprava.
Nativní modul, který volá nedokumentované API. Tvůj vlastní modul, plugin nebo malá knihovna může používat interní metodu UIKitu nalezenou na nějakém fóru. Kontrola ji pozná podle názvu.
Kolize názvů. Tvoje metoda nebo třída má náhodou stejný název jako privátní API Applu. Zpráva s tím počítá a žádá tě, abys ji přejmenoval. Apple doporučuje přidávat prefix k metodám, které přidáváš do jeho tříd, třeba tři malá písmena a podtržítko.
API veřejné na macOS, ale ne na iOS. V roce 2024 byla aplikace v .NET MAUI zamítnuta kvůli _SCDynamicStoreCreate. Apple tuhle funkci dokumentuje, ale jen pro macOS, a hlavička iOS SDK ji označuje jako API_UNAVAILABLE(ios, tvos, watchos).
Oprav to, neobcházej
- Aktualizuj závislost. U balíčků, které Expo sleduje, ti
npx expo install --checkukáže ty, které neodpovídají tvému SDK, podle dokumentace Expo CLI. U ostatních si přečti changelog. - Vrať se k předchozí verzi, pokud název přinesla ta nová, a dej vědět vydavateli.
- Nahraď nebo odstraň opuštěnou knihovnu.
- Přejmenuj svou metodu nebo třídu a přidej prefix.
Název neskrývej. Rozdělení řetězce nebo volání metody oklikou použití jen schová, neopraví ho. Pořád to zakazuje pravidlo 2.5.1 i smlouva a v sázce je pak tvůj účet.
Pak udělej nový build s novým číslem buildu. Aktualizace na dálku nestačí: název je ve zkompilovaném nativním kódu. Před odesláním spusť krok 2 znovu na nové IPA. Název se už nesmí objevit.
Když jde o falešný poplach
Kontrola Applu se občas splete. 6. června 2022 dostaly aplikace v React Native a Flutteru ITMS-90338 kvůli veřejným názvům z WebKitu, jako setNavigationDelegate: nebo isMainFrame, a to i u skoro nezměněných aktualizací. Další den napsal inženýr Applu, že problém je na straně validace vyřešený, a vyzval k opětovnému odeslání buildů. Vlákno je zachované v GitHub repozitáři Expo.
Na falešný poplach ukazují tři indicie dohromady:
- build vypadá jako poslední přijatý build;
- krok 2 název nenajde nebo krok 3 ukáže, že je na iOS veřejný;
- ostatní vývojáři hlásí stejný den stejnou chybu na fórech Applu.
V tom případě se podívej na stav služeb Applu a pošli build znovu s novým číslem. Pokud chyba přetrvává, napiš vývojářské podpoře Applu a přilož celý e-mail. Kolize názvů ale v očích Applu falešný poplach není: přejmenuj.
Před dalším odesláním
Ostatní chyby ITMS mají taky svůj návod: ITMS-90725 pro verzi Xcode, ITMS-91053 pro privacy manifest a ITMS-90034 pro podpis. Jakmile je build přijatý, nainstaluj si ho přes TestFlight ještě před review.
Zamítnutí od revieweru chtějí víc nadhledu: postup najdeš na stránce aplikace zamítnutá Applem a nejčastější důvody v článku proč Apple zamítá aplikace.
Pokud si chceš svůj projekt před odesláním zkontrolovat, Review Kit obsahuje můj checklist a skills pro Claude Code, které tvůj projekt projdou. Pokud se ti odesílání zaseklo a chceš, aby se na to podíval člověk, zarezervuj si se mnou hodinu: projdeme tvůj projekt přes videohovor, na tvé obrazovce. A pokud chystáš své první vydání, udělej si bezplatnou diagnostiku: seřadí ti kroky a pošle ti můj checklist proti zamítnutí od Applu.
Časté dotazy
Je ITMS-90338 zamítnutí od App Review?
Ne. Je to automatická kontrola během zpracování buildu, ještě před jakoukoli lidskou kontrolou. Odmítnutý build se nedostane ani do TestFlightu, ani do review. Privátní API, které později najde reviewer, spadá pod guideline 2.5.1.
Dá se ITMS-90338 opravit aktualizací na dálku?
Ne. Uvedené názvy jsou ve zkompilovaném nativním kódu binárního souboru. Aktualizace na dálku mění jen JavaScript a zdroje. Oprav závislost, udělej nový build a pošli ho znovu.
Proč Apple uvádí metody, které jsem nikdy nenapsal?
Protože pocházejí z vložené knihovny nebo z kolize: nějaká závislost deklaruje metodu se stejným názvem jako privátní API. Apple čte názvy v binárním souboru, ne tvůj zdrojový kód. Dohledej balíček pomocí grep a npm explain.
Jak poznám, jestli je API na iOS veřejné?
Hledej jeho název v hlavičkách iOS SDK, jejichž cestu vrátí xcrun --sdk iphoneos --show-sdk-path, a přečti si jeho dostupnost. Uvádí ji i stránka v dokumentaci Applu. Funkce zdokumentovaná jen pro macOS na iPhonu veřejná není.
Musím po opravě změnit číslo buildu?
Ano. Nech ho EAS zvýšit: číslo, které už bylo pro stejnou verzi odeslané, může skončit odmítnutím ITMS-90189.

