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-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 під час 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 змінює цей проєкт замість того, щоб створити його заново, а плагін додає ключ, але ніколи не прибирає. Локальний build узяв би його з собою. Build EAS у хмарі починає з нового prebuild, коли ios/ ігнорується git, і в мене було саме так.
Перед кожним build я перевіряю, що насправді піде:
npx expo config --type introspect --json | grep -o '"UIBackgroundModes":\[[^]]*\]'
npx expo prebuild --clean --platform ios
Перша команда показує режими, які prebuild запише в Info.plist. Друга створює нативний проєкт заново для локального build, видаляючи теку ios/. На Solys перша показує "UIBackgroundModes":["audio"], на TradaZen нічого: обидва результати навмисні.
Додавання чи видалення режиму змінює Info.plist, а отже бінарник: потрібен новий build, віддаленого оновлення недостатньо. Документація 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) |
| Геолокація час від часу | Значні зміни розташування |
| Синхронізація без поспіху | fetch або processing |
| Аудіо- чи відеодзвінки | voip з CallKit |
Щодо геолокації, сторінка Apple називає сервіс значних змін розташування (significant-change) альтернативою режиму 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. Використовуй віддалені оновлення для виправлень, ніколи для доставки функції, якої рецензент не бачив. Виправлення, пов'язане з рецензією, йде через новий build, як я пояснюю в гайді про guideline 2.1.
Як відповісти на відмову 2.5.4?
Розмова ведеться в розділі App Review твого застосунку, де ти відповідаєш на повідомлення App Review. Два випадки:
- Режим нічому не служить: прибери його, зроби новий build, перевір інтроспекцію, потім відповідай двома реченнями.
- Режим служить справжній функції: відповідай точним шляхом і коротким відео, потім повторюй цей текст у нотатках для рецензії кожної версії.
Шаблон, який я б використав, підлаштуй під себе (прибери речення про будильник, якщо в твоєму застосунку його немає):
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 перед поданням
- Інтроспекція 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 не використовується за призначенням. Повідомлення називає режим і пропонує показати функцію, яка його використовує, або прибрати ключ.
Чи потрібен новий build після відмови 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.

