# ITMS-90338: διόρθωση «Non-public API usage» στο Expo

> ITMS-90338: το build σου αναφέρει μέθοδο, συνάρτηση ή κλάση που η Apple θεωρεί ιδιωτική. Βρες την εξάρτηση, ενημέρωσέ τη και κάνε νέο build.

Αρχική σελίδα: [ITMS-90338: διόρθωση «Non-public API usage» στο Expo](https://devoria.fr/el/blog/itms-90338-non-public-api-usage-expo)
Συγγραφέας: [Mehdi Azizi](https://devoria.fr/el/mehdi-azizi)
Δημοσίευση: 2026-11-04
Τελευταία αναθεώρηση περιεχομένου: 2026-11-04


Το ITMS-90338 είναι ένας αυτόματος έλεγχος της Apple στο build σου. Λέει ότι το binary σου αναφέρεται σε API που η Apple θεωρεί ιδιωτικά: μεθόδους Objective-C, συναρτήσεις ή κλάσεις. **Η διόρθωση: εντοπίζεις το αρχείο που αναφέρει το e-mail, φτάνεις μέχρι την εξάρτηση που περιέχει το όνομα, την ενημερώνεις ή μετονομάζεις τη μέθοδό σου, και μετά κάνεις νέο build.**

Με το Expo, το όνομα προέρχεται σχεδόν πάντα από μια native βιβλιοθήκη, σπάνια από τη JavaScript σου. Στις 15 Οκτωβρίου 2026 έτρεξα τις εντολές ανάλυσης αυτού του οδηγού σε ένα build simulator του Solys, του ξυπνητηριού μου με mini-games (Expo SDK 57), και στο iOS SDK του Xcode 26.2. Δες πώς να διαβάσεις το e-mail, να βρεις τον ένοχο και να αναγνωρίσεις ένα false positive.

## Τι σημαίνει το σφάλμα ITMS-90338;

Το μήνυμα έχει τίτλο «ITMS-90338: Non-public API usage». Υπάρχει σε τρεις παραλλαγές, ανάλογα με το τι βρήκε η Apple στο binary σου:

- **«references non-public selectors»**: ονόματα μεθόδων Objective-C, όπως `boldFontOfSize:`·
- **«references non-public symbols»**: συναρτήσεις, γραμμένες με κάτω παύλα μπροστά, όπως `__dyld_get_shared_cache_range`·
- **«contains or inherits from non-public classes»**: ένα όνομα κλάσης.

Κάθε παραλλαγή δίνει μια διαδρομή και μετά τη λίστα με τα ονόματα. Το κείμενο προσθέτει δύο συμβουλές: να μετονομάσεις τις μεθόδους σου αν το όνομά τους συμπίπτει με ιδιωτικό API, και να αφαιρέσεις τη στατική βιβλιοθήκη που τις περιέχει. Ένα e-mail του Σεπτεμβρίου 2026 προτείνει επίσης το `xcrun swift-demangle` για να γίνουν αναγνώσιμα τα κωδικοποιημένα ονόματα Swift.

Ο κανόνας πίσω από αυτόν τον έλεγχο είναι η [guideline 2.5.1](https://developer.apple.com/app-store/review/guidelines/#2.5.1): μια εφαρμογή μπορεί να χρησιμοποιεί μόνο δημόσια API. Η [σύμβαση άδειας του Apple Developer Program](https://developer.apple.com/support/terms/apple-developer-program-license-agreement/) το επαναλαμβάνει στο άρθρο 3.3.1: καμία κλήση σε ιδιωτικό API. Ο ανθρώπινος έλεγχος αυτού του κανόνα αναλύεται στον οδηγό για τη [guideline 2.5.1](https://devoria.fr/el/blog/guideline-2-5-1-public-api-sdk-expo). Εδώ ασχολούμαστε με το σφάλμα αποστολής.

Όπως και στα άλλα σφάλματα ITMS, κανείς δεν άνοιξε την εφαρμογή σου: η Apple ανέλυσε το binary κατά την επεξεργασία του build. **Η Apple δεν δημοσιεύει καμία λίστα ιδιωτικών API: τα ονόματα στο e-mail σου είναι η μόνη αξιόπιστη λίστα.**

## Διάβασε τη διαδρομή: ένα SDK ή το εκτελέσιμό σου;

Η διαδρομή πριν από τη λίστα σου λέει σε ποιο αρχείο να ψάξεις.

- **`Frameworks/Nom.framework/Nom`**: το όνομα βρίσκεται σε ένα ενσωματωμένο δυναμικό framework, άρα σε ένα συγκεκριμένο SDK. Στο build του Solys, αυτός ο φάκελος περιέχει 14 frameworks: Hermes, React Native, modules του Expo που έρχονται ήδη μεταγλωττισμένα και τη βιβλιοθήκη εικόνων SDWebImage.
- **Μόνο το όνομα της εφαρμογής σου**: το όνομα βρίσκεται στο κύριο εκτελέσιμο. Με το Expo, αυτό περιλαμβάνει τον native κώδικά σου και τα pods που μεταγλωττίζονται από τον πηγαίο κώδικα, συνδεδεμένα στατικά από προεπιλογή. Οποιαδήποτε native βιβλιοθήκη μπορεί τότε να ευθύνεται.

## Το δέντρο: από το όνομα στη διόρθωση

Πάρε τις γραμμές με τη σειρά. Η πρώτη που ταιριάζει στην περίπτωσή σου δίνει το στοιχείο.

| Τι διαβάζεις                  | Πού να ψάξεις              | Διόρθωση                      |
| ----------------------------- | -------------------------- | ----------------------------- |
| Όνομα σε `$s` ή `_$s`         | `xcrun swift-demangle`     | Ξανά από την αρχή του δέντρου |
| Δεν βρίσκεται στο IPA σου     | `grep` στο bundle          | Ξαναστείλε build              |
| Δημόσιο όνομα στο iOS         | Headers του iOS SDK        | Ξαναστείλε build              |
| `API_UNAVAILABLE(ios)`        | Headers του iOS SDK        | Ενημέρωσε το πακέτο           |
| Μέσα στο `Frameworks/X`       | Το SDK που παρέχει το X    | Ενημέρωσε το SDK              |
| Selector, η εφαρμογή σου      | Pods και native modules    | Πρόθεμα ή ενημέρωση           |
| Symbol `_nom`, η εφαρμογή σου | `nm -m`, μετά τα pods      | Αντικατάστησε την κλήση       |
| Κλάση, η εφαρμογή σου         | Οι κλάσεις σου και τα pods | Πρόθεμα στην κλάση            |

Οι γραμμές «Ξαναστείλε build» δείχνουν false positive, που το εξετάζουμε παρακάτω.

## Βήμα 1: άνοιξε το build που στάλθηκε

Δούλεψε πάνω στο αρχείο που πραγματικά στάλθηκε στην Apple, όχι στο project σου. Κατέβασε το IPA από τη σελίδα του build στο expo.dev και τρέξε στο Mac σου:

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

Η δεύτερη γραμμή εμφανίζει τα ενσωματωμένα frameworks. Η τρίτη ψάχνει άμεση σύνδεση με ιδιωτικό framework της Apple: δεν πρέπει να εμφανίσει τίποτα. Στο Solys, το `otool -L` δεν βρίσκει κανένα ιδιωτικό framework, ούτε στο εκτελέσιμο ούτε στα 14 frameworks.

## Βήμα 2: βρες το αρχείο που περιέχει το όνομα

Ξεκίνα πλατιά. Ψάξε το όνομα που αναφέρεται, χωρίς τις κάτω παύλες στην αρχή, σε όλο το bundle:

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

Η εντολή εμφανίζει τα αρχεία που περιέχουν αυτό το κείμενο. Μετά γίνε πιο συγκεκριμένος ανάλογα με τον τύπο του ονόματος (selector, symbol, κλάση), στο αρχείο που βρήκες, εδώ `Binaire`:

```bash
strings -a Binaire | grep -x "boldFontOfSize:"
nm -m Binaire | grep "_dyld_get_shared_cache_range"
nm -m Binaire | grep '_OBJC_CLASS_$_NomDeClasse'
```

**Για selector, χρησιμοποίησε `strings`, όχι `nm`.** Στο framework React του Solys, το `nm` εμφανίζει μεθόδους μαζί με την κλάση τους, όπως `-[RCTRootView initWithBridge:moduleName:initialProperties:]`, ποτέ μόνο του τον selector `initWithBridge:`. Το `strings` τον βρίσκει.

Για symbol, διάβασε το τέλος της γραμμής. Στο Solys, το `nm -m` εμφανίζει για παράδειγμα `(undefined) external __dyld_get_image_name (from libSystem)`: το binary καλεί τη συνάρτηση χωρίς να την περιέχει, και τη δίνει η libSystem. Η συγκεκριμένη συνάρτηση είναι δημόσια, όπως δείχνει το επόμενο βήμα.

## Βήμα 3: έλεγξε αν το όνομα είναι όντως ιδιωτικό

Τα headers του iOS SDK περιγράφουν τα δημόσια API. Μια αναζήτηση λίγων δευτερολέπτων σου λέει αν το όνομα υπάρχει εκεί και σε ποιες πλατφόρμες. Για symbol, αφαίρεσε πρώτα την κάτω παύλα που προστίθεται στη μεταγλώττιση: το `_SCDynamicStoreCreate` γίνεται `SCDynamicStoreCreate`.

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

Στις 15 Οκτωβρίου 2026, με το iOS SDK 26.2, να τι δίνουν τέσσερα ονόματα από πραγματικά e-mails ITMS-90338:

| Όνομα                         | Στο iOS SDK          | Ερμηνεία             |
| ----------------------------- | -------------------- | -------------------- |
| \_dyld_get_shared_cache_range | Απουσιάζει           | Ιδιωτικό API         |
| boldFontOfSize:               | Απουσιάζει           | Ιδιωτικό ή σύγκρουση |
| SCDynamicStoreCreate          | API_UNAVAILABLE(ios) | Μόνο macOS           |
| isMainFrame                   | Property του WebKit  | Δημόσιο              |

**Ένα όνομα που υπάρχει και είναι διαθέσιμο στο iOS δείχνει false positive. Ένα όνομα που απουσιάζει δείχνει ιδιωτικό API ή σύγκρουση ονομάτων.** Δύο περιορισμοί: ένα δημόσιο API μόνο για Swift δεν εμφανίζεται σε αυτά τα headers, και για έναν accessor όπως το `setDelegate:`, ψάξε την property `delegate`.

## Βήμα 4: φτάσε μέχρι το πακέτο

Αν η διαδρομή αναφέρει framework, το όνομά του σε οδηγεί στο pod και μετά στο πακέτο npm:

```bash
grep -n "NomDuFramework" ios/Podfile.lock
npm explain nom-du-paquet
```

Το `npm explain` σου λέει ποια εξάρτηση το εγκατέστησε, αν δεν το πρόσθεσες ποτέ εσύ.

Αν η διαδρομή αναφέρει το εκτελέσιμο, ψάξε το όνομα στον native πηγαίο κώδικα και μετά στα pods:

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

Προσάρμοσε τη λίστα φακέλων: το `modules` είναι ο φάκελος των τοπικών modules του Expo. Ο φάκελος `ios/Pods` εμφανίζεται μετά το `npx expo prebuild -p ios`, που σε Mac εγκαθιστά και τα pods. Προσοχή: η εντολή μπορεί να ξαναδημιουργήσει τον φάκελο `ios`. Αν τον έχεις αλλάξει με το χέρι, δούλεψε σε αντίγραφο.

**Η αναζήτηση στο `ios/Pods` μετράει όσο και αυτή στον πηγαίο κώδικα.** Στο Solys, αυτός ο φάκελος περιέχει 15 frameworks που έρχονται ήδη μεταγλωττισμένα, σε μορφή `.xcframework`: modules του Expo, Hermes, React Native και SDK τρίτων. Μια αναζήτηση μόνο στα αρχεία πηγαίου κώδικα θα έχανε τον κώδικά τους.

## Οι συνηθισμένες αιτίες

**Ένα SDK τρίτου, πολύ παλιό ή πολύ καινούργιο.** Τον Σεπτέμβριο 2026, ένας developer Flutter [ανέφερε απόρριψη λόγω ενός SDK ασφαλείας](https://github.com/talsec/Free-RASP-Flutter/issues/236): η έκδοση 8.2.3 καλούσε το `__dyld_get_shared_cache_range`. Η έκδοση 8.2.2 περνούσε, και ο εκδότης πρότεινε να γυρίσει σε αυτήν μέχρι να βγει διόρθωση.

**Ένα native module που καλεί μη τεκμηριωμένο API.** Το δικό σου module, ένα plugin ή μια μικρή βιβλιοθήκη μπορεί να χρησιμοποιεί μια εσωτερική μέθοδο του UIKit που βρέθηκε σε κάποιο forum. Ο έλεγχος την εντοπίζει από το όνομά της.

**Μια σύγκρουση ονομάτων.** Η μέθοδος ή η κλάση σου έχει, κατά σύμπτωση, το όνομα ενός ιδιωτικού API της Apple. Το μήνυμα το προβλέπει και σου ζητά να τη μετονομάσεις. Η Apple συνιστά να [βάζεις πρόθεμα στις μεθόδους που προσθέτεις στις κλάσεις της](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html), για παράδειγμα τρία πεζά γράμματα και μια κάτω παύλα.

**Ένα API δημόσιο στο macOS, όχι στο iOS.** Το 2024, μια εφαρμογή .NET MAUI [απορρίφθηκε για το `_SCDynamicStoreCreate`](https://github.com/dotnet/runtime/issues/103949). Η Apple τεκμηριώνει αυτή τη συνάρτηση, αλλά [μόνο για macOS](https://developer.apple.com/documentation/systemconfiguration/1437828-scdynamicstorecreate), και το header του iOS SDK τη σημειώνει `API_UNAVAILABLE(ios, tvos, watchos)`.

## Διόρθωσε χωρίς παρακάμψεις

1. **Ενημέρωσε την εξάρτηση.** Για τα πακέτα που παρακολουθεί το Expo, το `npx expo install --check` δείχνει όσα δεν ταιριάζουν με το SDK σου, σύμφωνα με την [τεκμηρίωση του Expo CLI](https://docs.expo.dev/more/expo-cli/). Για τα υπόλοιπα, διάβασε το changelog.
2. **Γύρνα στην προηγούμενη έκδοση** αν η καινούργια έφερε το όνομα, και ενημέρωσε τον εκδότη.
3. **Αντικατάστησε ή αφαίρεσε** μια εγκαταλειμμένη βιβλιοθήκη.
4. **Μετονόμασε** τη μέθοδο ή την κλάση σου, με πρόθεμα.

**Μην κρύβεις το όνομα.** Αν σπάσεις ένα string ή καλέσεις τη μέθοδο από παράκαμψη, κρύβεις τη χρήση χωρίς να τη διορθώνεις. Παραμένει απαγορευμένη από τον κανόνα 2.5.1 και από τη σύμβαση, και τότε εκτίθεται ο λογαριασμός σου.

Μετά, κάνε νέο build με νέο [build number](https://devoria.fr/el/blog/app-store-connect-build-number-version). Ένα απομακρυσμένο update δεν αρκεί: το όνομα βρίσκεται στον μεταγλωττισμένο native κώδικα. Πριν την αποστολή, ξανατρέξε το βήμα 2 στο νέο IPA. Το όνομα δεν πρέπει να εμφανίζεται πια.

## Όταν είναι false positive

Ο έλεγχος της Apple κάνει μερικές φορές λάθος. Στις 6 Ιουνίου 2022, εφαρμογές React Native και Flutter έλαβαν ITMS-90338 για δημόσια ονόματα του WebKit, όπως `setNavigationDelegate:` ή `isMainFrame`, ακόμη και σε σχεδόν αμετάβλητα updates. Την επόμενη μέρα, ένας μηχανικός της Apple έγραψε ότι το πρόβλημα λύθηκε από την πλευρά του validation και ζήτησε να ξανασταλούν τα builds. Το thread υπάρχει ακόμη στο [GitHub repository του Expo](https://github.com/expo/expo/issues/17768).

Τρεις ενδείξεις μαζί δείχνουν false positive:

- το build μοιάζει με το τελευταίο build που έγινε δεκτό·
- το βήμα 2 δεν βρίσκει το όνομα, ή το βήμα 3 το δείχνει δημόσιο στο iOS·
- άλλοι developers αναφέρουν το ίδιο σφάλμα την ίδια μέρα στα forums της Apple.

Σε αυτή την περίπτωση, κοίτα την [κατάσταση των υπηρεσιών της Apple](https://developer.apple.com/system-status/) και μετά ξαναστείλε build με νέο αριθμό. Αν το σφάλμα επιμένει, γράψε στο [developer support της Apple](https://developer.apple.com/contact/) με ολόκληρο το e-mail. Μια σύγκρουση ονομάτων όμως δεν είναι false positive για την Apple: μετονόμασε.

## Πριν την επόμενη αποστολή σου

Και τα άλλα σφάλματα ITMS έχουν τον οδηγό τους: [ITMS-90725](https://devoria.fr/el/blog/itms-90725-sdk-version-issue-expo) για την έκδοση του Xcode, [ITMS-91053](https://devoria.fr/el/blog/itms-91053-missing-api-declaration-expo) για το privacy manifest και [ITMS-90034](https://devoria.fr/el/blog/itms-90034-missing-invalid-signature) για την υπογραφή. Μόλις γίνει δεκτό το build, εγκατέστησέ το με το [TestFlight](https://devoria.fr/el/blog/testflight-dokimi-efarmogis-iphone) πριν το review.

Οι απορρίψεις από reviewer θέλουν περισσότερη ψυχραιμία: η διαδικασία είναι στη σελίδα [εφαρμογή που απέρριψε η Apple](https://devoria.fr/el/expertise/app-rejected-by-apple), και οι πιο συχνοί λόγοι στο [γιατί η Apple απορρίπτει μια εφαρμογή](https://devoria.fr/el/blog/aporripsi-app-apple-app-store-aities).

Για να ελέγξεις το project σου πριν την αποστολή, το [Review Kit](https://devoria.fr/el/kits/app-review) συγκεντρώνει τη checklist μου και skills του Claude Code που ελέγχουν το project σου. Αν η αποστολή σου κολλάει και θέλεις ανθρώπινη ματιά, [κλείσε μια ώρα μαζί μου](https://devoria.fr/el/coaching): δουλεύουμε το project σου σε βιντεοκλήση, στην οθόνη σου. Κι αν ετοιμάζεις την πρώτη σου κυκλοφορία, [κάνε το δωρεάν diagnostic](https://devoria.fr/el/diagnostic): βάζει τα βήματα σε σειρά και σου στέλνει τη checklist μου κατά των απορρίψεων της Apple.

## Συχνές ερωτήσεις

### Είναι το ITMS-90338 απόρριψη από το App Review;

Όχι. Είναι αυτόματος έλεγχος κατά την επεξεργασία του build, πριν από οποιονδήποτε ανθρώπινο έλεγχο. Το build που απορρίπτεται δεν φτάνει ούτε στο TestFlight ούτε σε review. Ένα ιδιωτικό API που εντοπίζει αργότερα ένας reviewer εμπίπτει στη guideline 2.5.1.

### Διορθώνεται το ITMS-90338 με απομακρυσμένο update;

Όχι. Τα ονόματα βρίσκονται στον μεταγλωττισμένο native κώδικα του binary. Ένα απομακρυσμένο update αλλάζει μόνο τη JavaScript και τα resources. Διόρθωσε την εξάρτηση, κάνε νέο build και ξαναστείλ' το.

### Γιατί η Apple αναφέρει μεθόδους που δεν έγραψα ποτέ;

Επειδή προέρχονται από μια ενσωματωμένη βιβλιοθήκη ή από σύγκρουση ονομάτων: μια εξάρτηση δηλώνει μέθοδο με το όνομα ενός ιδιωτικού API. Η Apple διαβάζει τα ονόματα μέσα στο binary, όχι τον πηγαίο κώδικά σου. Φτάσε μέχρι το πακέτο με `grep` και `npm explain`.

### Πώς ξέρω αν ένα API είναι δημόσιο στο iOS;

Ψάξε το όνομά του στα headers του iOS SDK, τη διαδρομή των οποίων δίνει το `xcrun --sdk iphoneos --show-sdk-path`, και διάβασε τη διαθεσιμότητά του. Το δείχνει και η σελίδα τεκμηρίωσης της Apple. Μια συνάρτηση τεκμηριωμένη μόνο για macOS δεν είναι δημόσια στο iPhone.

### Πρέπει να αλλάξω το build number μετά τη διόρθωση;

Ναι. Άφησε το EAS να το αυξήσει: ένας αριθμός που έχει ήδη σταλεί για την ίδια έκδοση μπορεί να απορριφθεί με ITMS-90189.


## Το project σου

[Review Kit](https://devoria.fr/el/kits/app-review): Η checklist μου για το review του App Store, οι απορρίψεις μου από την Apple διορθωμένες, η checklist του Google Play και τρία skills του Claude Code. 49 €.

[iOS & Android Kit](https://devoria.fr/el/kits/ios): Φτιάξε την εφαρμογή σου για iPhone και Android με το Claude Code: ο αρχικός κώδικας των εφαρμογών μου, η διαδρομή μέχρι τα stores και όλα τα kits. 99 €.

[Διάγνωση: τι σε χωρίζει από την iPhone εφαρμογή σου](https://devoria.fr/el/diagnostic): 7 ερωτήσεις, 60 δευτερόλεπτα: μαθαίνεις τι πραγματικά σε μπλοκάρει για να δημοσιεύσεις την iPhone εφαρμογή σου, τη διαδρομή σου σε 4 βήματα και το kit που σου ταιριάζει.
