# Guideline 2.3.1: скрытые функции и OTA-обновления

> Guideline 2.3.1: Apple отклоняет скрытые и спящие функции, а также всё, чего нет в заметках для ревью. Что можно менять удалённо, а что требует нового билда.

Оригинальная страница: [Guideline 2.3.1: скрытые функции и OTA-обновления](https://devoria.fr/ru/blog/guideline-2-3-1-hidden-features-ota-updates)
Автор: [Mehdi Azizi](https://devoria.fr/ru/mehdi-azizi)
Опубликовано: 2026-11-04
Последняя правка контента: 2026-11-04


Guideline 2.3.1 запрещает скрытые, спящие и недокументированные функции: всё, что делает твоё приложение, должно быть видно и пользователю, и ревьюеру. Каждую новинку нужно точно описать в заметках для ревью. **Удалённое обновление может исправить то, что Apple видела, но никогда не может добавить то, чего Apple не видела.**

Я публикую свои iOS-приложения через Expo, и в каждое с первого билда встроен EAS Update, чтобы выпускать исправления. Моё правило: никаких удалённых обновлений, пока версия на ревью. Этот гайд опирается на текст Apple, её лицензию, документацию Expo и опубликованные отказы.

## Что говорит guideline 2.3.1?

2.3.1 открывает раздел 2.3 «Accurate Metadata» в [App Review Guidelines](https://developer.apple.com/app-store/review/guidelines/#2.3.1), внутри секции 2 «Performance». Текст начинается так: «Don't include any hidden, dormant, or undocumented features in your app; your app's functionality should be clear to end users and App Review.» Проще говоря: ничего скрытого, спящего или недокументированного.

Дальше речь о каждой отправке: «All new features, functionality, and product changes must be described with specificity in the Notes for Review section of App Store Connect (generic descriptions will be rejected) and accessible for review.» **Каждую новинку нужно точно описать в заметках для ревью, и она должна быть доступна ревьюеру.** Так что «Исправления ошибок и улучшения» не хватит.

Тот же пункт касается вводящего в заблуждение маркетинга: обещать контент или сервис, которого нет, или показывать фальшивую цену, в App Store или где-то ещё.

Эта часть мне пригодилась на Nolera. Перечитывая страницу приложения перед релизом, я нашёл две непроверяемые фразы: «интерфейс только на французском», которая уже стала неправдой, и «самый популярный пакет» для приложения, которое ещё ни разу не выходило. Заметки для ревьюера тоже описывали устаревшее состояние продукта. Именно на такое и нацелена эта часть. А [2.3.10](https://devoria.fr/ru/blog/guideline-2-3-10-app-store-rejection-metadata) касается в основном страницы приложения.

## Что значит сообщение об отказе по 2.3.1?

Отказ называется «Guideline 2.3.1 - Performance», иногда с добавкой «Hidden Features». Вот два сообщения, опубликованных React Native-разработчиками:

- **Март 2024**, в [тикете CodePush на GitHub](https://github.com/microsoft/react-native-code-push/issues/2680): «The app contains hidden features.», без всяких уточнений. Разработчик просит подробностей и получает дополнение по 2.5.2: «Your app, extension, or linked framework appears to contain code designed explicitly with the capability to change the app's behavior or functionality after App Review approval.» Из тикета не ясно, прошло ли приложение потом.
- **Февраль 2023**, в [другом тикете](https://github.com/microsoft/react-native-code-push/issues/2434), Apple указывает на «CodePush-related remote code downloading/updating support» в бинарнике.

Как я это понимаю: **ревьюер не видит твоих обновлений, он видит код, который способен их получать.** Оба случая касаются CodePush. Свежих публичных отказов из-за EAS Update я не нашёл, но это ничего не доказывает.

## Удалённые обновления: что разрешают 2.5.2 и лицензия

[Правило 2.5.2](https://developer.apple.com/app-store/review/guidelines/#2.5.2), о котором я пишу и в гайде про [фоновые режимы](https://devoria.fr/ru/blog/guideline-2-5-4-background-mode-rejected), запрещает приложению «download, install, or execute code which introduces or changes features or functionality of the app».

Статья 3.3.1(B) [Apple Developer Program License Agreement](https://developer.apple.com/support/terms/apple-developer-program-license-agreement/#b331) запрещает загружаемый исполняемый код, но уточняет: «Interpreted code may be downloaded to an Application». При трёх условиях: этот код не меняет основное назначение приложения, не обходит ни подпись, ни песочницу, ни защиту системы и не создаёт магазин других приложений.

JavaScript в Expo-приложении это интерпретируемый код: по-моему, EAS Update укладывается в эти рамки. В его [документации](https://docs.expo.dev/eas-update/introduction/) напоминают, что обновления подчиняются правилам App Store и что «changes to your app's behavior need to be reviewed». Для критического бага Expo предлагает удалённое исправление, а затем новую отправку, в которую оно встроено.

**Моя граница: исправлять и подстраивать то, что уже есть, можно. Менять то, чем приложение является или что оно продаёт, нельзя.**

## Что можно менять удалённо, а что требует нового билда

Таблица, которой я пользуюсь, составлена по страницам Apple и Expo, на которые я здесь ссылаюсь. Строка «бесплатное становится платным» и две последние строки это моя интерпретация.

| Изменение                        | Путь                   | Почему                          |
| -------------------------------- | ---------------------- | ------------------------------- |
| Баг в JavaScript                 | Удалённое обновление   | Так задумано в Expo             |
| Текст, перевод, стили            | Удалённое обновление   | Так задумано в Expo             |
| Цена подписки                    | App Store Connect      | Код не меняется                 |
| Promotional Text                 | App Store Connect      | Без новой отправки              |
| Покупка уже одобренного типа     | Ревью только покупки   | Правило App Store Connect       |
| Первая покупка нового типа       | Новая версия           | Правило App Store Connect       |
| Новая функция                    | Новая версия           | 2.3.1 и 2.5.2                   |
| Бесплатное становится платным    | Новая версия           | Пересмотреть страницу и заметки |
| Разрешение, Info.plist           | Новый билд             | Файл внутри бинарника           |
| Нативный модуль, Expo SDK        | Новый билд             | Меняется нативный код           |
| Переключатель, который отключает | Удалённая конфигурация | Допустимо, если временно        |
| Переключатель, который открывает | Запрещено              | Функция не проходила ревью      |

Promotional Text меняется «without requiring an updated submission», как сказано на странице Apple об [информации о версии](https://developer.apple.com/help/app-store-connect/reference/app-information/platform-version-information). Первая покупка каждого типа уходит вместе с новой версией, последующие отправляются отдельно, согласно справке об [отправке покупок](https://developer.apple.com/help/app-store-connect/manage-submissions-to-app-review/submit-an-in-app-purchase). Expo относит нативный код, разрешения и версию SDK к тому, что требует нового бинарника, как и в случае [ITMS-90683](https://devoria.fr/ru/blog/itms-90683-missing-purpose-string-expo). Переключатель, который отключает сломанную функцию, не добавляет ничего нового, пока он не отнимает то, за что заплатили подписчики.

## Удалённые переключатели без отказа

Удалённый переключатель, или feature flag, сам по себе не запрещён: всё зависит от того, что он скрывает. Мои правила:

- **Всё, что переключатель может показать, присутствует и включено для ревьюера**, и названо в заметках для ревью.
- A/B-тест сравнивает два варианта, которые ревьюер может увидеть, а не проверенный вариант и никем не виденный.
- **Никакой код не определяет ревью, чтобы вести себя иначе.** Это и есть определение скрытой функции.
- Paywall, управляемый удалённо, показывает только одобренные продукты, и каждый вариант соблюдает [правило 3.1.2](https://devoria.fr/ru/blog/guideline-3-1-2-app-rejected-subscription).

## Демо-режим и тестовые аккаунты

[Guideline 2.1](https://devoria.fr/ru/blog/guideline-2-1-app-store-otkaz) требует демо-аккаунт, если в приложении есть вход, и включённый сервер. Если юридические требования или требования безопасности не позволяют его дать, его может заменить встроенный демо-режим, но только «with prior approval by Apple», и он должен показывать все функции. Страница Apple об информации о версии добавляет: аккаунт не должен истекать, а дополнительные аккаунты указываются в заметках, которые ограничены 4 000 байт.

Связь с 2.3.1: **демо-режим или тестовый аккаунт, который открывает экраны, иначе невидимые, нужно описать.** Секретный жест, код или особый аккаунт, о которых не сказано, это скрытая функция. Назови в заметках каждый особый доступ и то, что он открывает.

## Моё правило: никаких удалённых обновлений во время ревью

Почему EAS Update у меня везде: в Solys, моём будильнике, баг в JavaScript делал приложение немым, стоило одному звуку хоть раз не проиграться. Исправление занимало три строки, но чтобы его выпустить, нужен был полный билд и ревью. С тех пор во все мои приложения expo-updates встроен с первого билда: это нативный модуль, и бинарник, собранный без него, никогда ничего не получит. Моя конфигурация в `app.json`:

```json
{
  "expo": {
    "runtimeVersion": { "policy": "fingerprint" },
    "updates": { "fallbackToCacheTimeout": 0, "checkAutomatically": "ON_LOAD" }
  }
}
```

С `fingerprint` обновление доходит только до бинарников с совпадающим нативным кодом ([версии рантайма](https://docs.expo.dev/eas-update/runtime-versions/)). Но пока версия ждёт ревью или проходит его, я ничего не публикую. По двум причинам.

1. **Ревьюер не увидел бы исправление.** С `fallbackToCacheTimeout` равным 0 свежая установка стартует на встроенном коде и применяет обновление при следующем запуске ([документация expo-updates](https://docs.expo.dev/versions/latest/sdk/updates/)). Ревьюер устанавливает, запускает один раз и тестирует бинарник. Nolera отклонили, хотя исправление уже было выпущено удалённо: я рассказываю об этом в гайде по 2.1.
2. **То, что тестирует ревьюер, должно оставаться тем, что получают пользователи.** Если менять поведение во время ревью, появляется разрыв между проверенной версией и той, что выпущена.

## Точные заметки для ревью

2.3.1 не принимает общие описания. Шаблон, которым я бы пользовался, на английском:

```text
What's new in this version:
- [Feature]: [where to find it, how to test it].

Remote configuration:
- "[flag]" controls [feature]. It is ON for review.

Over-the-air updates:
- The app uses expo-updates for bug fixes only.
  No update will be published during this review.

Demo account: see Sign-in information. It never expires.
```

## Как ответить на отказ по 2.3.1?

Отвечай в App Store Connect, где ты [отвечаешь на сообщение App Review](https://developer.apple.com/help/app-store-connect/manage-submissions-to-app-review/reply-to-app-review-messages/).

- **Сообщение остаётся расплывчатым**: спроси, о какой функции речь, как разработчик из тикета 2024 года.
- **Настоящая функция плохо описана**: дай точный путь и короткое видео, затем дополни заметки.
- **Переключатель открывает новое**: убери его или включи и опиши, затем пересобери билд.
- **Претензия к коду обновлений**: объясни, что он нужен для исправлений, в рамках статьи 3.3.1(B). В тикете 2023 года Apple требовала его убрать.

## Чеклист по 2.3.1 перед отправкой

- Каждая новинка версии описана в заметках, с путём к ней.
- Каждый удалённый переключатель включён для ревью и назван в заметках.
- Никакой код не меняет поведение для ревьюера.
- Особые доступы описаны: демо-режим, тестовый аккаунт, жест.
- Страница приложения и paywall не обещают ничего, чего не делает билд.
- Во время ревью не публикуется ни одного удалённого обновления.

## Проверь свою отправку до отправки

[Review Kit](https://devoria.fr/ru/kits/app-review) собирает мой чеклист перед отправкой, шаблоны заметок для тестировщика и skills для Claude Code, которые перечитывают твой проект. Если нужен живой взгляд, [забронируй со мной час](https://devoria.fr/ru/coaching): вместе по видеосвязи перечитаем твои заметки для ревью и настройку обновлений. Весь подход описан на странице [приложение отклонено Apple](https://devoria.fr/ru/expertise/app-rejected-by-apple).

Готовишь свой первый релиз? [Пройди бесплатную диагностику](https://devoria.fr/ru/diagnostic): она расставит шаги по порядку и пришлёт тебе мой чеклист против отказов Apple.

## Частые вопросы

### Что значит «Guideline 2.3.1 - Performance - Hidden Features»?

Apple считает, что в твоём приложении есть скрытая или спящая функция либо функция, которой нет в заметках для ревью. Сообщение может также касаться кода, способного менять приложение после ревью, иногда вместе с отказом по 2.5.2.

### Разрешает ли Apple EAS Update?

Лицензия разрешает загружаемый интерпретируемый код, пока он не меняет основное назначение приложения. Исправлять можно. Добавлять функцию, не прошедшую ревью, нельзя.

### Можно ли использовать feature flags в iOS-приложении?

Да, если всё, что они могут показать, прошло ревью. Включи каждый переключатель на время ревью и опиши его в заметках.

### Нужно ли сообщать ревьюеру о демо-режиме?

Да. Встроенный демо-режим требует предварительного согласия Apple и должен показывать все функции.

### Можно ли публиковать удалённое обновление во время ревью?

Прямого запрета нет. Но ревьюер не увидит его при первом запуске, и оно создаёт разрыв с проверенной версией. Я всегда от этого воздерживаюсь.


## Твой проект

[Review Kit](https://devoria.fr/ru/kits/app-review): Мой чек-лист для ревью в App Store, исправленные отказы Apple, чек-лист для Google Play и три скилла для Claude Code. 55 $.

[iOS & Android Kit](https://devoria.fr/ru/kits/ios): Создай своё приложение для iPhone и Android с Claude Code: стартовый код моих приложений, путь до публикации в сторах и все киты. 109 $.

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