# ITMS-90338: виправляємо «Non-public API usage» в Expo

> ITMS-90338: твій білд згадує метод, функцію чи клас, які Apple вважає приватними. Знайди залежність, онови її і збери білд заново.

Оригінальна сторінка: [ITMS-90338: виправляємо «Non-public API usage» в Expo](https://devoria.fr/uk/blog/itms-90338-non-public-api-usage-expo)
Автор: [Mehdi Azizi](https://devoria.fr/uk/mehdi-azizi)
Опубліковано: 2026-11-04
Останнє оновлення змісту: 2026-11-04


ITMS-90338 означає, що Apple автоматично перевірила твій білд. Помилка каже, що твій бінарник посилається на API, які Apple вважає приватними: методи Objective-C, функції або класи. **Як виправити: знайди файл, згаданий у листі, дійди до залежності, яка містить цю назву, онови її або перейменуй свій метод, а потім збери білд заново.**

В Expo ця назва майже завжди походить із нативної бібліотеки і рідко з твого JavaScript. 15 жовтня 2026 року я запустив команди аналізу з цього гайду на білді для симулятора Solys, мого будильника з мініграми (Expo SDK 57), і на iOS SDK з Xcode 26.2. Нижче розповідаю, як читати лист, як знайти винуватця і як розпізнати хибне спрацювання.

## Що означає помилка ITMS-90338?

Повідомлення має заголовок «ITMS-90338: Non-public API usage». Воно буває у трьох варіантах, залежно від того, що Apple знайшла у твоєму бінарнику:

- **«references non-public selectors»**: назви методів Objective-C, наприклад `boldFontOfSize:`;
- **«references non-public symbols»**: функції з підкресленням на початку, наприклад `__dyld_get_shared_cache_range`;
- **«contains or inherits from non-public classes»**: назва класу.

У кожному варіанті є шлях, а за ним список назв. У тексті є дві поради: перейменувати свої методи, якщо їхня назва збігається з приватним API, і прибрати статичну бібліотеку, яка їх містить. Лист від вересня 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/uk/blog/guideline-2-5-1-public-api-sdk-expo). Тут ми розбираємо помилку відправлення.

Як і з іншими помилками ITMS, твій застосунок ніхто не відкривав: Apple проаналізувала бінарник під час обробки білда. **Apple не публікує списку приватних API: назви з твого листа і є єдиним надійним списком.**

## Читаємо шлях: SDK чи твій виконуваний файл?

Шлях перед списком підказує, в якому файлі шукати.

- **`Frameworks/Name.framework/Name`**: назва міститься у вбудованому динамічному фреймворку, тобто в конкретному SDK. У білді Solys у цій папці 14 фреймворків: Hermes, React Native, уже скомпільовані модулі Expo та бібліотека зображень SDWebImage.
- **Лише назва твого застосунку**: назва міститься в головному виконуваному файлі. В Expo він об'єднує твій нативний код і поди, скомпільовані з вихідного коду, які за замовчуванням лінкуються статично. Тоді винною може бути будь-яка нативна бібліотека.

## Дерево рішень: від згаданої назви до виправлення

Йди рядками по порядку. Перший рядок, що підходить до твого випадку, і дає підказку.

| Що ти бачиш                     | Де шукати              | Виправлення               |
| ------------------------------- | ---------------------- | ------------------------- |
| Назва з `$s` або `_$s`          | `xcrun swift-demangle` | Пройти дерево ще раз      |
| Немає у твоєму IPA              | `grep` по бандлу       | Надіслати білд ще раз     |
| Публічна назва в iOS            | Заголовки iOS SDK      | Надіслати білд ще раз     |
| `API_UNAVAILABLE(ios)`          | Заголовки iOS SDK      | Оновити пакет             |
| У `Frameworks/X`                | SDK, що постачає X     | Оновити SDK               |
| Селектор, твій застосунок       | Поди й нативні модулі  | Додати префікс чи оновити |
| Символ `_name`, твій застосунок | `nm -m`, потім поди    | Замінити виклик           |
| Клас, твій застосунок           | Твої класи й поди      | Додати префікс до класу   |

Рядки «Надіслати білд ще раз» вказують на хибне спрацювання, про нього нижче.

## Крок 1: відкрити надісланий білд

Працюй із файлом, який справді надіслано в Apple, а не з проєктом. Завантаж IPA зі сторінки білда на 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
```

Другий рядок показує вбудовані фреймворки. Третій шукає пряме посилання на приватний фреймворк Apple: він не повинен нічого вивести. У Solys `otool -L` не знаходить жодного приватного фреймворку ні у виконуваному файлі, ні в 14 фреймворках.

## Крок 2: знайти файл, що містить назву

Почни широко. Шукай згадану назву, без підкреслень на початку, по всьому бандлу:

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

Команда виводить файли, що містять цей текст. Далі уточнюй залежно від типу назви (селектор, символ, клас) у знайденому файлі, позначеному як `Binary`:

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

**Для селектора використовуй `strings`, а не `nm`.** На фреймворку React у Solys `nm` показує методи разом із класом, наприклад `-[RCTRootView initWithBridge:moduleName:initialProperties:]`, і ніколи окремо селектор `initWithBridge:`. А `strings` його знаходить.

Для символу дивись на кінець рядка. У Solys `nm -m` показує, наприклад, `(undefined) external __dyld_get_image_name (from libSystem)`: бінарник викликає функцію, але не містить її, а постачає її libSystem. Саме ця функція публічна, як покаже наступний крок.

## Крок 3: перевірити, чи назва справді приватна

Заголовки iOS SDK описують публічні API. Пошук на кілька секунд покаже, чи є там згадана назва і для яких платформ. Для символу спершу прибери підкреслення, додане під час компіляції: `_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 дали чотири назви з реальних листів ITMS-90338:

| Згадана назва                 | В iOS SDK            | Висновок              |
| ----------------------------- | -------------------- | --------------------- |
| \_dyld_get_shared_cache_range | Відсутня             | Приватний API         |
| boldFontOfSize:               | Відсутня             | Приватний або колізія |
| SCDynamicStoreCreate          | API_UNAVAILABLE(ios) | Лише macOS            |
| isMainFrame                   | Властивість WebKit   | Публічний             |

**Якщо назва є і доступна в iOS, це вказує на хибне спрацювання. Якщо її немає, це вказує на приватний API або колізію.** Два обмеження: API, публічний лише у Swift, у цих заголовках не з'являється, а для аксесора на кшталт `setDelegate:` шукай властивість `delegate`.

## Крок 4: дійти до пакета

Якщо шлях згадує фреймворк, його назва веде до пода, а потім до npm-пакета:

```bash
grep -n "FrameworkName" ios/Podfile.lock
npm explain package-name
```

`npm explain` покаже, яка залежність його встановила, якщо ти сам його ніколи не додавав.

Якщо шлях згадує виконуваний файл, шукай назву в нативних вихідних файлах, а потім у подах:

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

Підлаштуй список папок: `modules` містить локальні модулі Expo. Папка `ios/Pods` з'являється після `npx expo prebuild -p ios`, яка на Mac ще й встановлює поди. Увага: ця команда може перестворити папку `ios`. Якщо ти змінював її вручну, працюй із копією.

**Пошук в `ios/Pods` важить не менше, ніж пошук у вихідному коді.** У Solys ця папка містить 15 уже скомпільованих фреймворків у форматі `.xcframework`: модулі Expo, Hermes, React Native і сторонні SDK. Пошук лише по вихідних файлах пропустить їхній код.

## Типові причини

**Сторонній SDK, надто старий або надто новий.** У вересні 2026 року Flutter-розробник [повідомив про відмову через SDK безпеки](https://github.com/talsec/Free-RASP-Flutter/issues/236): його версія 8.2.3 викликала `__dyld_get_shared_cache_range`. Версія 8.2.2 проходила, і розробник SDK порадив повернутися до неї, поки не вийде виправлення.

**Нативний модуль, що викликає недокументований API.** Твій власний модуль, плагін чи невелика бібліотека може використовувати внутрішній метод UIKit, знайдений на форумі. Перевірка знаходить його за назвою.

**Колізія назв.** Твій метод чи клас випадково має ту саму назву, що й приватний 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), а заголовок 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. **Перейменуй** свій метод чи клас, додавши префікс.

**Не маскуй назву.** Розбити рядок на частини чи викликати метод обхідним шляхом означає сховати використання, а не виправити його. Воно все одно заборонене правилом 2.5.1 і угодою, і тоді під ударом опиняється твій акаунт.

Потім збери білд знову з новим [номером білда](https://devoria.fr/uk/blog/app-store-connect-build-number-version). Віддаленого оновлення не вистачить: назва сидить у скомпільованому нативному коді. Перед відправленням повтори крок 2 на новому IPA. Назви там бути не повинно.

## Коли це хибне спрацювання

Перевірка Apple іноді помиляється. 6 червня 2022 року застосунки на React Native і Flutter отримали ITMS-90338 за публічні назви WebKit, як-от `setNavigationDelegate:` або `isMainFrame`, навіть на майже незмінених оновленнях. Наступного дня інженер Apple написав, що проблему на боці валідації вирішено, і запропонував надіслати білди ще раз. Обговорення збереглося в [GitHub-репозиторії Expo](https://github.com/expo/expo/issues/17768).

Три ознаки разом вказують на хибне спрацювання:

- білд схожий на останній прийнятий;
- крок 2 не знаходить назви, або крок 3 показує, що вона публічна в iOS;
- інші розробники того ж дня пишуть про ту саму помилку на форумах Apple.

У такому разі перевір [стан сервісів Apple](https://developer.apple.com/system-status/), а потім надішли білд із новим номером. Якщо помилка не зникає, напиши в [підтримку розробників Apple](https://developer.apple.com/contact/) і додай повний лист. А от колізія назв для Apple хибним спрацюванням не є: перейменовуй.

## Перед наступним відправленням

Для інших помилок ITMS теж є гайди: [ITMS-90725](https://devoria.fr/uk/blog/itms-90725-sdk-version-issue-expo) про версію Xcode, [ITMS-91053](https://devoria.fr/uk/blog/itms-91053-missing-api-declaration-expo) про маніфест конфіденційності та [ITMS-90034](https://devoria.fr/uk/blog/itms-90034-missing-invalid-signature-fix) про підпис. Коли білд приймуть, встанови його через [TestFlight](https://devoria.fr/uk/blog/testflight-testuvannya-zastosunku-iphone) перед перевіркою.

Відмови від рев'юера потребують більш виваженого підходу: як діяти, описано на сторінці [застосунок відхилено Apple](https://devoria.fr/uk/expertise/app-rejected-by-apple), а найчастіші причини зібрано в статті [чому Apple відхиляє застосунок](https://devoria.fr/uk/blog/vidmova-apple-app-store-prychyny).

Щоб перевірити все перед відправленням, [Review Kit](https://devoria.fr/uk/kits/app-review) містить мій чекліст і skills для Claude Code, які переглядають твій проєкт. Якщо відправлення застрягло і тобі потрібен погляд живої людини, [забронюй годину зі мною](https://devoria.fr/uk/coaching): розберемо твій проєкт по відео, на твоєму екрані. А якщо готуєш свій перший реліз, [пройди безкоштовну діагностику](https://devoria.fr/uk/diagnostic): вона розставить кроки по порядку і надішле тобі мій чекліст проти відмов Apple.

## Поширені запитання

### ITMS-90338 це відмова App Review?

Ні. Це автоматична перевірка під час обробки білда, ще до будь-якої ручної перевірки. Відхилений білд не потрапляє ні в TestFlight, ні на рев'ю. А от приватний API, який пізніше помітить рев'юер, підпадає під guideline 2.5.1.

### Чи можна виправити ITMS-90338 віддаленим оновленням?

Ні. Згадані назви містяться в скомпільованому нативному коді бінарника. Віддалене оновлення змінює лише JavaScript і ресурси. Виправ залежність, збери білд заново і надішли його.

### Чому Apple згадує методи, яких я ніколи не писав?

Бо вони походять із вбудованої бібліотеки або з колізії: залежність оголошує метод, назва якого збігається з приватним API. Apple читає назви в бінарнику, а не твій вихідний код. Дійди до пакета за допомогою `grep` і `npm explain`.

### Як дізнатися, чи API публічний в iOS?

Шукай його назву в заголовках iOS SDK, шлях до яких дає `xcrun --sdk iphoneos --show-sdk-path`, і дивись на доступність. Сторінка документації Apple теж це вказує. Функція, задокументована лише для macOS, не є публічною на iPhone.

### Чи треба змінювати номер білда після виправлення?

Так. Нехай EAS його збільшить: номер, уже надісланий для тієї самої версії, можуть відхилити з помилкою ITMS-90189.


## Твій проєкт

[Review Kit](https://devoria.fr/uk/kits/app-review): Мій чекліст для перевірки в App Store, виправлені відмови Apple, чекліст для Google Play і три скіли для Claude Code. 2 499 ₴.

[iOS & Android Kit](https://devoria.fr/uk/kits/ios): Створи свій застосунок для iPhone і Android з Claude Code: стартовий код моїх застосунків, шлях до публікації в сторах і всі кіти. 4 999 ₴.

[Діагностика: що відділяє тебе від твого застосунку для iPhone](https://devoria.fr/uk/diagnostic): 7 запитань, 60 секунд: ти дізнаєшся, що насправді заважає тобі опублікувати застосунок для iPhone, отримаєш свій шлях у 4 кроки і кіт, який тобі підходить.
