Guideline 2.5.4: Apple отклонила фоновый режим
Автор: Mehdi Azizi
Опубликовано , 9 мин
Guideline 2.5.4: Apple отклоняет фоновый режим, если его не использует ни одна функция. Удали ключ или покажи ревьюеру звук, геолокацию или звонок.
Guideline 2.5.4 разрешает фоновые режимы только для того, для чего они созданы: звук, геолокация, VoIP-звонки, завершение задачи, локальные уведомления. Отказ прилетает, когда твой Info.plist объявляет один из этих режимов, а ревьюер не находит ни одной функции, которой он нужен. Удали ключ, если его ничто не использует, а если используется, покажи функцию ревьюеру за тридцать секунд.
Я публикую свои iOS-приложения на Expo. Solys, мой будильник с мини-играми, объявляет режим audio: я его удалил, а потом вернул по другой причине. Этот гайд опирается на текст Apple, на отказы, которые публиковали другие разработчики, и на мои проекты. Общую картину причин отказов смотри в статье почему Apple отклоняет приложение.
Что говорит guideline 2.5.4?
Правило умещается в одну фразу в разделе 2 «Performance», часть 2.5 «Software Requirements», App Review Guidelines: «Multitasking apps may only use background services for their intended purposes: VoIP, audio playback, location, task completion, local notifications, etc.»
Проще говоря: фоновый сервис служит только тому, для чего он существует. Отсюда и заголовок «Guideline 2.5.4 - Performance - Software Requirements».
По умолчанию приложение, ушедшее в фон, приостанавливается. Чтобы продолжать работать, оно объявляет один или несколько режимов в ключе UIBackgroundModes своего Info.plist. Документация Apple о фоновых режимах просит использовать их экономно, из-за батареи, и выбирать другое решение, если оно есть. Каждый объявленный режим: это обещание, которое ревьюер может проверить.
Фоновые режимы и их назначение
| Режим | Ключ | Назначение |
|---|---|---|
| Audio, AirPlay, PiP | audio | Слышимый звук в фоне |
| Геолокация | location | Непрерывная геолокация |
| Voice over IP | voip | Звонки через CallKit |
| Background fetch | fetch | Регулярный сетевой контент |
| Background processing | processing | Запланированные задачи |
| Remote notifications | remote-notification | Push о новом контенте |
Эта таблица резюмирует страницу Apple. В отказах, опубликованных на форумах, чаще всего встречаются audio, location и voip.
Что значит сообщение об отказе 2.5.4?
Сообщение называет проблемный режим. Два примера, которые разработчики опубликовали на форумах Apple:
- Audio: «Your app still declares support for audio in the UIBackgroundModes key in your Info.plist but did not include features that require persistent audio.» Дальше уточняется, что ключ
audioпредназначен для приложений, которые воспроизводят слышимый контент в фоне, например музыкальный плеер. Предлагаются два выхода: добавить такой контент или удалить ключ (тред на форуме). - Геолокация: в треде декабря 2024 Apple не нашла ни одной функции, кроме отслеживания сотрудников, которой нужна постоянная геолокация, и написала, что одного этого использования недостаточно.
Как я это понимаю: ревьюер не читает твой код, он ищет функцию. Он должен суметь её запустить, свернуть приложение и убедиться, что оно продолжает работать.
Бывает и обратная ситуация. В треде мая 2026 приложение для чтения вслух, которое действительно использовало режим audio, отклоняли несколько раз: ревьюер не мог запустить звук. Другой разработчик пишет там, что, по его мнению, его случай разблокировало приложенное видео. Это свидетельство, а не правило Apple.
Откуда берётся ключ, если ты его не добавлял?
В Expo Info.plist заполняют config-плагины во время prebuild. Так что фоновый режим может попасть в твой бинарник без твоего решения.
Плагин expo-audio с настройками по умолчанию
Документация expo-audio говорит прямо: опция enableBackgroundPlayback по умолчанию равна true и на iOS добавляет режим audio. Значит, достаточно объявить плагин в короткой форме, "expo-audio", чтобы объявить режим.
Именно это я нашёл, перепроверяя TradaZen перед первой отправкой: короткие звуки интерфейса на переднем плане, но плагин объявлен в короткой форме. Исправление умещается в app.json:
{
"expo": {
"plugins": [["expo-audio", { "enableBackgroundPlayback": false }]]
}
}
Другие пакеты добавляют режим по-своему. Опция isIosBackgroundLocationEnabled в expo-location добавляет location, только если ты её включишь. А expo-background-task добавляет processing при prebuild сразу после установки.
Папка ios/, в которой остался старый ключ
Вторая ловушка, на которую я наступил с Solys в июле 2026. Я удалил audio из app.json, но ключ остался в ios/Solys/Info.plist, сгенерированном раньше. Prebuild без --clean изменяет этот проект, а не пересоздаёт его, и плагин добавляет ключ, но никогда не удаляет. Локальный билд унёс бы его с собой. Облачный билд EAS начинает с чистого prebuild, когда ios/ в gitignore, а у меня было именно так.
Перед каждым билдом я проверяю, что реально уйдёт:
npx expo config --type introspect --json | grep -o '"UIBackgroundModes":\[[^]]*\]'
npx expo prebuild --clean --platform ios
Первая команда показывает режимы, которые prebuild запишет в Info.plist. Вторая пересоздаёт нативный проект для локального билда, стирая папку ios/. На Solys первая выводит "UIBackgroundModes":["audio"], на TradaZen ничего: оба результата ожидаемые.
Добавление или удаление режима меняет Info.plist, а значит, и бинарник: нужен новый билд, удалённого обновления недостаточно. Документация EAS Update относит такие изменения к тем, что требуют нового бинарника, как и в случае ITMS-90683.
Solys: режим audio объявлен, и на то есть причина
Solys: это будильник, но режим audio нужен не для звонка будильника: сигнал идёт через AlarmKit, сервис будильников Apple, доступный с iOS 26 (документация AlarmKit). Звонит система, даже в беззвучном режиме, а не приложение.
История ключа, потому что логика важнее самой строчки:
- В конце июля 2026 я его удаляю. В фоне ничего не играло: звуки мини-игр остаются на переднем плане, а будильник от приложения не зависит.
- Через два дня возвращаю его вместе с ночным прослушиванием. Эта опция записывает звуки твоей ночи через микрофон и анализирует их на iPhone. Запись должна продолжаться при заблокированном экране, а документация expo-audio указывает, что опция фоновой записи тоже добавляет режим
audio.
Обоснование поменялось по сути: режим нужен для записи, а не для воспроизведения. Три предохранителя, видимые в приложении:
- прослушивание: это опция, выключенная по умолчанию, она включается в настройках или в вечернем ритуале;
- у неё свой экран согласия, как требует правило 2.5.14 для любой записи, и ничего не покидает телефон;
- микрофон отключается за десять минут до будильника, чтобы прослушивание и сигнал никогда не мешали друг другу.
Я не могу гарантировать, что ревьюер всегда примет такое объявление. Но про каждый элемент я могу сказать, зачем он нужен и где его увидеть.
Будильники, которые не давали приложению уснуть
До AlarmKit сторонний будильник не мог звонить так, как будильник Apple. Поэтому некоторые библиотеки держали приложение активным: документация Flutter-пакета alarm2 описывает беззвучный аудиоплеер, который работает до самого будильника. Но беззвучный звук ничего не воспроизводит. С iOS 26 AlarmKit делает этот обходной путь ненужным.
Оставить или удалить режим: таблица решений
| Твоя ситуация | Что делать |
|---|---|
| Короткие звуки на переднем плане | Удалить audio |
| Музыка или аудиокнига | Оставить, показать |
| Долгая запись | Оставить + согласие |
| Будильник или таймер | AlarmKit (iOS 26) |
| Геолокация время от времени | Significant location changes |
| Несрочная синхронизация | fetch или processing |
| Аудио- или видеозвонки | voip с CallKit |
Для геолокации страница Apple называет сервис significant location changes альтернативой режиму location.
Другие пункты 2.5: 2.5.1 и 2.5.2
2.5.1: публичные API и фреймворки по назначению
«Apps may only use public APIs and must run on the currently shipping OS.» Приложение использует только публичные API, работает на текущей версии системы и отказывается от устаревшего. Как и в 2.5.4, каждый фреймворк служит своему назначению, указанному в описании: HealthKit для здоровья, HomeKit для умного дома. Если библиотека вызывает приватный API, отказ получает твоё приложение.
2.5.2: никакого загружаемого кода, меняющего приложение
Приложение остаётся самодостаточным внутри своего пакета. Оно не может «download, install, or execute code which introduces or changes features or functionality of the app». Ограниченное исключение есть только у образовательных приложений, которые учат программировать, и код в них должен быть полностью виден и редактируем пользователем.
В Expo этот вопрос касается EAS Update: его документация напоминает, что обновления должны соответствовать правилам App Store. Используй удалённые обновления для исправлений, но никогда для выпуска функции, которую ревьюер не видел. Исправление, связанное с ревью, идёт через новый билд, как я объясняю в гайде по guideline 2.1.
Как ответить на отказ 2.5.4?
Переписка идёт в разделе App Review твоего приложения, где ты отвечаешь на сообщение App Review. Два случая:
- Режим ни для чего не нужен: удали его, пересобери билд, проверь introspection, потом ответь в двух фразах.
- Режим обслуживает реальную функцию: ответь с точным путём и коротким видео, потом повторяй этот текст в заметках для ревью к каждой версии.
Шаблон, который я бы использовал, адаптируй под себя (убери фразу про будильник, если его в приложении нет):
Hello,
The "audio" background mode is used only by [feature], an
optional feature that [records / plays] audio while the
device is locked. It is off by default.
How to test:
1. Open [screen] and turn on [feature].
2. Accept the microphone permission if asked.
3. Tap [button], then lock the device.
[What the reviewer will hear or see.]
Our alarm does not use this mode: it is scheduled with
AlarmKit and rings through the system.
Attached: a short screen recording of these steps.
Best regards,
Чеклист 2.5.4 перед отправкой
- Introspection Expo показывает только нужные режимы, и ты можешь назвать функцию для каждого.
- Ни один плагин, который по умолчанию добавляет режим (как expo-audio), не объявлен в короткой форме.
- В твоей локальной папке
ios/не осталось старых ключей, или ты собираешь из чистого prebuild. - Функция запускается быстро, без скрытых настроек, а заметки для ревью объясняют, какой режим для чего и как его проверить.
- Любая запись запрашивает явное согласие и показывает видимый индикатор. Правила о запросах разрешений есть в гайде по guideline 5.1.1.
- В будильнике сигнал идёт через AlarmKit, а не через звук, проигрываемый приложением.
Проверь своё приложение перед отправкой
Review Kit собирает мой чеклист перед отправкой, шаблоны заметок для тестировщика и skills для Claude Code, которые проверяют твой проект. Если нужен живой взгляд, забронируй час со мной: разберём сообщение Apple и твой Info.plist вместе, по видеосвязи. Весь подход описан на странице приложение отклонено Apple.
Готовишь первый релиз? Пройди бесплатную диагностику: она расставит шаги по порядку и пришлёт тебе мой чеклист против отказов Apple.
Никто не может гарантировать, что Apple примет твоё приложение. А вот хорошо обоснованный фоновый режим защищается за тридцать секунд.
Частые вопросы
Что значит «Guideline 2.5.4 - Performance - Software Requirements»?
Это заголовок отказа, когда Apple считает, что фоновый режим в твоём Info.plist используется не по назначению. Сообщение называет режим и предлагает показать функцию, которая его использует, или удалить ключ.
Нужен ли новый билд после отказа 2.5.4?
Если ты удаляешь или меняешь режим, да: ключ лежит в Info.plist, а значит, в бинарнике. Если режим обоснован, может хватить ответа с точным путём и видео.
Можно ли оставить режим audio ради пары звуков интерфейса?
Нет. Коротким звукам на переднем плане он не нужен. В Expo выставь enableBackgroundPlayback в false в плагине expo-audio.
Нужен ли будильнику режим audio?
Для звонка нет, если он работает через AlarmKit, доступный с iOS 26: будильник воспроизводит система. В Solys режим audio нужен для ночного прослушивания, которое записывает при заблокированном экране.
Почему Apple ссылается на 2.5.4, если я ничего не объявлял?
Возможно, это сделал за тебя плагин: expo-audio по умолчанию добавляет режим audio, а старая папка ios/ может хранить ключ, уже удалённый из конфигурации. Проверь командой npx expo config --type introspect.

