ITMS-90338: kako popraviti “Non-public API usage” u Expou
Autor: Mehdi Azizi
Objavljeno , 9 min
ITMS-90338: tvoj build sadrži metodu, funkciju ili klasu koju Apple smatra privatnom. Pronađi ovisnost koja je uzrok, ažuriraj je i napravi novi build.
ITMS-90338 je Appleova automatska provjera tvog builda. Javlja da se tvoj binarni fajl poziva na API-je koje Apple smatra privatnima: Objective-C metode, funkcije ili klase. Rješenje: pronađi fajl naveden u e-mailu, prati trag do ovisnosti koja sadrži taj naziv, ažuriraj je ili preimenuj svoju metodu, pa napravi novi build.
S Expoom naziv gotovo uvijek dolazi iz neke nativne biblioteke, rijetko iz tvog JavaScripta. 15. listopada 2026. pokrenuo sam naredbe za analizu iz ovog vodiča na simulatorskom buildu Solysa, moje budilice s mini-igrama (Expo SDK 57), i na iOS SDK-u iz Xcodea 26.2. Evo kako čitati e-mail, pronaći krivca i prepoznati lažnu uzbunu.
Što znači greška ITMS-90338?
Poruka nosi naslov "ITMS-90338: Non-public API usage". Postoji u tri varijante, ovisno o tome što je Apple našao u tvom binarnom fajlu:
- "references non-public selectors": nazivi Objective-C metoda, poput
boldFontOfSize:; - "references non-public symbols": funkcije, napisane s donjom crtom ispred, poput
__dyld_get_shared_cache_range; - "contains or inherits from non-public classes": naziv klase.
Svaka varijanta daje putanju, a zatim popis naziva. Tekst dodaje dva savjeta: preimenuj svoje metode ako im se naziv poklapa s privatnim API-jem i ukloni statičku biblioteku koja ih sadrži. E-mail iz rujna 2026. predlaže i xcrun swift-demangle da kodirani Swift nazivi postanu čitljivi.
Pravilo iza ove provjere je guideline 2.5.1: aplikacija smije koristiti samo javne API-je. Licencni ugovor Apple Developer Programa to ponavlja u članku 3.3.1: nema pozivanja privatnih API-ja. Ljudska provjera tog pravila opisana je u vodiču za guideline 2.5.1. Ovdje se bavimo greškom pri slanju.
Kao i kod drugih ITMS grešaka, nitko nije otvorio tvoju aplikaciju: Apple je analizirao binarni fajl tijekom obrade builda. Apple ne objavljuje nikakav popis privatnih API-ja: nazivi iz tvog e-maila jedini su pouzdan popis.
Čitanje putanje: SDK ili tvoj izvršni fajl?
Putanja napisana prije popisa govori ti u kojem fajlu tražiti.
Frameworks/Nom.framework/Nom: naziv se nalazi u ugrađenom dinamičkom frameworku, dakle u određenom SDK-u. U buildu Solysa ta mapa sadrži 14 frameworka: Hermes, React Native, Expo module isporučene već kompajlirane i biblioteku za slike SDWebImage.- Samo naziv tvoje aplikacije: naziv se nalazi u glavnom izvršnom fajlu. S Expoom on objedinjuje tvoj nativni kod i podove kompajlirane iz izvornog koda, po defaultu statički linkane. Tada uzrok može biti bilo koja nativna biblioteka.
Stablo odluke: od navedenog naziva do rješenja
Idi redak po redak. Prvi koji odgovara tvom slučaju daje ti trag.
| Što vidiš | Gdje tražiti | Rješenje |
|---|---|---|
Naziv s $s ili _$s | xcrun swift-demangle | Ponovno kroz stablo |
| Nema ga u tvom IPA-u | grep po bundleu | Ponovno pošalji build |
| Javni naziv na iOS-u | Headeri iOS SDK-a | Ponovno pošalji build |
API_UNAVAILABLE(ios) | Headeri iOS SDK-a | Ažuriraj paket |
U Frameworks/X | SDK koji isporučuje X | Ažuriraj SDK |
| Selektor, tvoja aplikacija | Podovi i nativni moduli | Dodaj prefiks ili ažuriraj |
Simbol _nom, tvoja app | nm -m, zatim podovi | Zamijeni poziv |
| Klasa, tvoja aplikacija | Tvoje klase i podovi | Dodaj prefiks klasi |
Retci "Ponovno pošalji build" označavaju lažnu uzbunu, o kojoj pišem niže.
Korak 1: otvori poslani build
Radi na fajlu koji je stvarno poslan Appleu, ne na svom projektu. Preuzmi IPA sa stranice builda na expo.dev, pa na Macu pokreni:
unzip -q MonApp.ipa -d ipa
ls ipa/Payload/MonApp.app/Frameworks
otool -L ipa/Payload/MonApp.app/MonApp | grep PrivateFrameworks
Drugi redak ispisuje ugrađene frameworke. Treći traži izravnu vezu na neki Appleov privatni framework: ne smije ništa ispisati. Na Solysu otool -L ne nalazi nijedan privatni framework, ni u izvršnom fajlu ni u 14 frameworka.
Korak 2: pronađi fajl koji sadrži naziv
Kreni široko. Traži navedeni naziv, bez donjih crta na početku, u cijelom bundleu:
grep -rl "dyld_get_shared_cache_range" ipa/Payload/MonApp.app
Naredba ispisuje fajlove koji sadrže taj tekst. Zatim suzi pretragu prema vrsti naziva (selektor, simbol, klasa), na pronađenom fajlu, označenom kao Binaire:
strings -a Binaire | grep -x "boldFontOfSize:"
nm -m Binaire | grep "_dyld_get_shared_cache_range"
nm -m Binaire | grep '_OBJC_CLASS_$_NomDeClasse'
Za selektor koristi strings, ne nm. Na React frameworku Solysa nm prikazuje metode s njihovom klasom, poput -[RCTRootView initWithBridge:moduleName:initialProperties:], a nikad sam selektor initWithBridge:. strings ga pronalazi.
Za simbol pročitaj kraj retka. Na Solysu nm -m prikazuje npr. (undefined) external __dyld_get_image_name (from libSystem): binarni fajl poziva funkciju, ali je ne sadrži, a isporučuje je libSystem. Ta je funkcija javna, kako pokazuje sljedeći korak.
Korak 3: provjeri je li naziv stvarno privatan
Headeri iOS SDK-a opisuju javne API-je. Pretraga od nekoliko sekundi kaže nalazi li se navedeni naziv u njima i na kojim platformama. Kod simbola najprije ukloni donju crtu dodanu pri kompajliranju: _SCDynamicStoreCreate postaje SCDynamicStoreCreate.
SDK=$(xcrun --sdk iphoneos --show-sdk-path)
grep -rn "SCDynamicStoreCreate" "$SDK/usr/include" "$SDK"/System/Library/Frameworks/*/Headers
- listopada 2026., s iOS SDK-om 26.2, ovo daju četiri naziva iz stvarnih ITMS-90338 e-mailova:
| Navedeni naziv | U iOS SDK-u | Tumačenje |
|---|---|---|
| _dyld_get_shared_cache_range | Nema ga | Privatni API |
| boldFontOfSize: | Nema ga | Privatno ili kolizija |
| SCDynamicStoreCreate | API_UNAVAILABLE(ios) | Samo macOS |
| isMainFrame | WebKit svojstvo | Javno |
Naziv koji postoji i dostupan je na iOS-u upućuje na lažnu uzbunu. Naziv kojeg nema upućuje na privatni API ili koliziju. Dva ograničenja: API koji je javan samo u Swiftu ne pojavljuje se u tim headerima, a za accessor poput setDelegate: traži svojstvo delegate.
Korak 4: prati trag do paketa
Ako putanja navodi framework, njegov naziv vodi do poda, a zatim do npm paketa:
grep -n "NomDuFramework" ios/Podfile.lock
npm explain nom-du-paquet
npm explain kaže koja ga je ovisnost instalirala, ako ga nikad nisi sam dodao.
Ako putanja navodi izvršni fajl, traži naziv u nativnom izvornom kodu, pa u podovima:
grep -rl --include='*.m' --include='*.mm' --include='*.swift' --include='*.h' \
"boldFontOfSize" node_modules modules
grep -rl "boldFontOfSize" ios/Pods
Prilagodi popis mapa: modules je mapa lokalnih Expo modula. Mapa ios/Pods pojavljuje se nakon npx expo prebuild -p ios, koja na Macu instalira i podove. Pazi: ta naredba može ponovno stvoriti mapu ios. Ako si je ručno mijenjao, radi na kopiji.
Pretraga u ios/Pods jednako je važna kao pretraga izvornog koda. Na Solysu ta mapa sadrži 15 frameworka isporučenih već kompajliranih, u formatu .xcframework: Expo module, Hermes, React Native i SDK-ove trećih strana. Pretraga ograničena na izvorne fajlove promašila bi njihov kod.
Tipični uzroci
SDK treće strane, prestar ili prenov. U rujnu 2026. jedan je Flutter developer prijavio odbijanje zbog sigurnosnog SDK-a: njegova verzija 8.2.3 pozivala je __dyld_get_shared_cache_range. Verzija 8.2.2 je prolazila, a izdavač je savjetovao povratak na nju dok ne stigne ispravak.
Nativni modul koji poziva nedokumentirani API. Tvoj vlastiti modul, plugin ili neka mala biblioteka može koristiti internu UIKit metodu pronađenu na nekom forumu. Provjera je prepoznaje po nazivu.
Kolizija naziva. Tvoja metoda ili klasa slučajno nosi naziv Appleovog privatnog API-ja. Poruka to predviđa i traži da je preimenuješ. Apple preporučuje dodavanje prefiksa metodama koje dodaješ njegovim klasama, npr. tri mala slova i donju crtu.
API koji je javan na macOS-u, ali ne i na iOS-u. 2024. godine jedna je .NET MAUI aplikacija odbijena zbog _SCDynamicStoreCreate. Apple tu funkciju jest dokumentirao, ali samo za macOS, a header iOS SDK-a označava je s API_UNAVAILABLE(ios, tvos, watchos).
Popravi, ne zaobilazi
- Ažuriraj ovisnost. Za pakete koje prati Expo,
npx expo install --checkjavlja one koji ne odgovaraju tvom SDK-u, prema dokumentaciji Expo CLI-ja. Za ostale pročitaj changelog. - Vrati se na prethodnu verziju ako je nova uvela taj naziv, i obavijesti izdavača.
- Zamijeni ili ukloni napuštenu biblioteku.
- Preimenuj svoju metodu ili klasu, uz prefiks.
Nemoj skrivati naziv. Rezanje stringa ili pozivanje metode zaobilaznim putem skriva korištenje, ali ga ne ispravlja. I dalje je zabranjeno pravilom 2.5.1 i ugovorom, a tada je izložen tvoj račun.
Zatim napravi novi build s novim brojem builda. Ažuriranje na daljinu nije dovoljno: naziv je u kompajliranom nativnom kodu. Prije slanja ponovno pokreni korak 2 na novom IPA-u. Naziv se više ne smije pojaviti.
Kad je riječ o lažnoj uzbuni
Appleova provjera ponekad griješi. 6. lipnja 2022. React Native i Flutter aplikacije dobile su ITMS-90338 zbog javnih WebKit naziva, poput setNavigationDelegate: ili isMainFrame, čak i za gotovo nepromijenjena ažuriranja. Sutradan je jedan Appleov inženjer napisao da je problem riješen na strani validacije i pozvao na ponovno slanje buildova. Rasprava je sačuvana u Expoovom GitHub repozitoriju.
Tri znaka zajedno upućuju na lažnu uzbunu:
- build je sličan zadnjem prihvaćenom buildu;
- korak 2 ne pronalazi naziv ili ga korak 3 pokazuje kao javan na iOS-u;
- drugi developeri istog dana prijavljuju istu grešku na Appleovim forumima.
U tom slučaju pogledaj status Appleovih servisa, pa ponovno pošalji build s novim brojem. Ako greška ostane, piši Appleovoj podršci za developere i priloži cijeli e-mail. Kolizija naziva, s druge strane, za Apple nije lažna uzbuna: preimenuj.
Prije sljedećeg slanja
I ostale ITMS greške imaju svoj vodič: ITMS-90725 za verziju Xcodea, ITMS-91053 za privacy manifest i ITMS-90034 za potpis. Kad build bude prihvaćen, instaliraj ga preko TestFlighta prije reviewa.
Odbijanja od strane reviewera traže više odmaka: postupak je na stranici aplikacija koju je Apple odbio, a najčešći razlozi su u članku zašto Apple odbija aplikaciju.
Da provjeriš sve prije slanja, Review Kit objedinjuje moju checklistu i Claude Code skillove koji pregledavaju tvoj projekt. Ako ti je slanje zapelo i želiš da netko pogleda, rezerviraj sat vremena sa mnom: prolazimo kroz tvoj projekt preko videopoziva, na tvom ekranu. A ako pripremaš svoje prvo izdanje, napravi besplatnu dijagnostiku: posložit će korake redom i poslati ti moju checklistu protiv Appleovih odbijanja.
Česta pitanja
Je li ITMS-90338 odbijanje od App Reviewa?
Nije. To je automatska provjera tijekom obrade builda, prije bilo kakvog ljudskog pregleda. Odbijeni build ne stiže ni u TestFlight ni na review. Privatni API koji kasnije uoči reviewer spada pod guideline 2.5.1.
Može li se ITMS-90338 popraviti ažuriranjem na daljinu?
Ne može. Navedeni nazivi nalaze se u kompajliranom nativnom kodu binarnog fajla. Ažuriranje na daljinu mijenja samo JavaScript i resurse. Popravi ovisnost, napravi novi build i ponovno ga pošalji.
Zašto Apple navodi metode koje nikad nisam napisao?
Zato što dolaze iz ugrađene biblioteke ili iz kolizije: neka ovisnost deklarira metodu koja nosi naziv privatnog API-ja. Apple čita nazive prisutne u binarnom fajlu, ne tvoj izvorni kod. Prati trag do paketa s grep i npm explain.
Kako znati je li API javan na iOS-u?
Potraži njegov naziv u headerima iOS SDK-a, čiju putanju daje xcrun --sdk iphoneos --show-sdk-path, pa pročitaj njegovu dostupnost. To piše i na Appleovoj stranici dokumentacije. Funkcija dokumentirana samo za macOS nije javna na iPhoneu.
Treba li nakon popravka promijeniti broj builda?
Treba. Pusti EAS da ga poveća: broj koji je već poslan za istu verziju može biti odbijen s greškom ITMS-90189.

