Background Task — механизм iOS, который позволяет приложению завершить начатую операцию после перехода в фоновый режим. Система выделяет ограниченное время (до 30 секунд) на выполнение задачи, после чего приложение принудительно приостанавливается. По данным Apple, 2026, использование expiration handler является обязательным требованием для корректного завершения фоновой задачи.
Главное
Background Task — это программный механизм iOS, позволяющий приложению продлить время выполнения после перехода в фоновый режим. Когда пользователь сворачивает приложение, система вызывает UIApplicationDelegate.applicationDidEnterBackground. Если в этот момент выполняется критическая операция (сохранение данных, отправка запроса), приложение может запросить дополнительное время через beginBackgroundTask.
Без использования Background Task приложение принудительно приостанавливается через 3–5 секунд после ухода в фон. Все незавершённые операции прерываются: данные не сохраняются, сетевые запросы обрываются, состояния теряются. Background Task даёт приложению до 30 секунд для корректного завершения этих операций.
beginBackgroundTask появился в iOS 4.0 — первом релизе, поддерживающем многозадачность. До iOS 4 приложение завершалось полностью при нажатии кнопки Home. С iOS 7 появились Background Fetch и URLSession background configuration. Начиная с iOS 13, Apple рекомендует BGTaskScheduler для новых проектов, но beginBackgroundTask остаётся актуальным для краткосрочных операций.
beginBackgroundTask — метод UIApplication, который регистрирует задачу и возвращает уникальный идентификатор UIBackgroundTaskIdentifier. Система увеличивает таймер фонового выполнения приложения. По истечении времени вызывается expiration handler, после чего приложение должно вызвать endBackgroundTask(identifier:) для корректного завершения.
Шаг 1 — приложение получает уведомление didEnterBackground. Шаг 2 — вызывается beginBackgroundTask(expirationHandler:). Шаг 3 — выполняется критическая операция (сохранение, сетевой запрос). Шаг 4 — по завершении вызывается endBackgroundTask(identifier:). Если операция не завершилась за 30 секунд — система вызывает expiration handler, и приложение должно завершить задачу немедленно.
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
func startBackgroundTask() {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
// Expiration handler
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
}
func finishBackgroundTask() {
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
Свойство backgroundTimeRemaining возвращает количество секунд, оставшихся до принудительного завершения фоновой задачи. Значение уменьшается в реальном времени. Если приложение не на фоне — возвращает DBL_MAX. Используйте это свойство для адаптации поведения: при остатке менее 5 секунд прерывайте длительные операции и сохраняйте прогресс.
Приложение может зарегистрировать несколько Background Task одновременно. Каждая задача получает свой идентификатор. Общее время выполнения суммируется — если зарегистрировано 3 задачи, приложение может получить до 90 секунд. Однако система может завершить приложение раньше, если исчерпаны ресурсы или превышен лимит батареи.
Expiration handler — это блок кода, который система вызывает при истечении выделенного времени на фоновую задачу. Наличие expiration handler является обязательным требованием Apple. Без него приложение может быть принудительно завершено системой, а данные — потеряны.
Expiration handler должен выполнить минимальные действия для сохранения состояния приложения: сохранить текущие данные в постоянное хранилище, вызвать endBackgroundTask с соответствующим идентификатором и установить идентификатор в .invalid. Внутри expiration handler запрещено запускать новые длительные операции — время выполнения ограничено 1–2 секундами.
func handleExpiration() {
// Сохраняем прогресс выполнения
saveProgressToUserDefaults()
// Отменяем активные сетевые запросы
currentTask?.cancel()
// Завершаем Background Task
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
// Регистрация с expiration handler
backgroundTaskID = UIApplication.shared.beginBackgroundTask(
withName: "SaveDocument",
expirationHandler: handleExpiration
)
Самая частая ошибка — не вызывать endBackgroundTask внутри expiration handler. В этом случае система продолжает считать задачу активной, приложение не переходит в состояние Suspended, и батарея разряжается. Вторая ошибка — запуск длительных операций внутри expiration handler. Система может завершить приложение до их окончания, и данные будут потеряны.
В expiration handler время выполнения критически мало — обычно менее 1 секунды. Поэтому внутри handler нельзя выполнять: сложные вычисления, сетевые запросы, запись больших объёмов данных в CoreData. Только атомарные операции: запись одного ключа в UserDefaults, установка флага состояния, вызов endBackgroundTask.
Рассмотрим полную реализацию Background Task для сохранения документа при уходе приложения в фон. Пример включает регистрацию задачи, выполнение операции с проверкой оставшегося времени и корректное завершение через endBackgroundTask.
При уходе в фон приложение инициирует сохранение большого документа. Background Task даёт до 30 секунд на операцию. Если время истекает — expiration handler сохраняет промежуточные результаты. После завершения сохранения вызывается endBackgroundTask для освобождения ресурсов.
class DocumentManager {
private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
func saveDocumentInBackground(document: Document) {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
self.saveProgress(document)
self.endBackgroundTask()
}
DispatchQueue.global().async {
document.save()
self.endBackgroundTask()
}
}
private func endBackgroundTask() {
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
}
Сетевые запросы в фоне требуют особого внимания — expiration handler может прервать запрос до получения ответа. Используйте URLSession с dataTask и проверяйте backgroundTimeRemaining перед отправкой. Если времени осталось менее 5 секунд — отложите отправку до следующего пробуждения приложения.
func sendCriticalData(data: Data) {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
// Сохраняем данные для повторной отправки
saveForRetry(data)
self.endBackgroundTask()
}
let task = URLSession.shared.dataTask(with: request) { _, _, error in
if error != nil {
saveForRetry(data)
}
self.endBackgroundTask()
}
task.resume()
}
Для длительных операций отслеживайте UIApplication.shared.backgroundTimeRemaining. Если значение падает ниже порога (10 секунд), прервите текущую операцию и запустите сохранение промежуточных результатов. Это позволяет корректно завершить работу до принудительного вызова expiration handler.
Background Task — мощный механизм, но его неправильное использование приводит к проблемам с батареей и производительностью. Соблюдение рекомендаций Apple и проверенных практик сообщества поможет избежать типичных ошибок и обеспечить стабильную работу приложения.
Завершайте Background Task как можно быстрее. Каждая секунда фонового выполнения расходует заряд батареи. Оптимальное время — менее 5 секунд. Если операция занимает больше — рассмотрите использование BGTaskScheduler или Background Modes. Не продлевайте задачу искусственно через beginBackgroundTask вызовы без реальной необходимости.
Храните UIBackgroundTaskIdentifier как свойство класса или структуры. Никогда не передавайте его как глобальную переменную — это приводит к конфликтам при множественных задачах. Устанавливайте идентификатор в .invalid после вызова endBackgroundTask для предотвращения двойного завершения.
В Xcode Simulator переключите приложение в фон через Home button (Command + Shift + H). Используйте Debug — Simulate Background Fetch для тестирования фонового пробуждения. Для эмуляции истечения времени установите Environment Variable BACKGROUND_TASK_SIMULATE_EXPIRATION = YES — expiration handler будет вызван через 5 секунд.
// Проверка оставшегося времени перед началом операции
let remaining = UIApplication.shared.backgroundTimeRemaining
guard remaining > 10.0 else {
// Недостаточно времени — отложить задачу
scheduleForNextLaunch()
return
}
// Выполнение операции с контролем времени
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
saveProgress()
self.endBackgroundTask()
}
Expiration handler захватывает self — это может создать retain cycle, если backgroundTaskID хранится как свойство того же объекта. Используйте [weak self] в замыкании или храните идентификатор отдельно. Retain cycle приведёт к утечке памяти — объект не будет освобождён до завершения приложения.
С выходом iOS 13 Apple представила BGTaskScheduler — современную замену старому Background Task API. Оба механизма решают сходные задачи, но BGTaskScheduler предоставляет больше контроля и гибкости. Понимание различий поможет выбрать правильный инструмент для конкретного сценария.
| Характеристика | Background Task | BGTaskScheduler |
|---|---|---|
| Версия iOS | 4.0+ | 13.0+ |
| Макс. время | 30 секунд | 30 с / 10 мин (processing) |
| Инициация | didEnterBackground | Планирование + система |
| Гарантия выполнения | Только при переходе в фон | В любое удобное для системы время |
| Expiration handler | Да, обязателен | Да, через task.expirationHandler |
| Интернет | Требуется при запуске | Доступен через requiresNetworkConnectivity |
| Зарядка | Не требуется | Опционально для processing |
beginBackgroundTask подходит для краткосрочных операций, которые нужно выполнить немедленно при уходе в фон: сохранение состояния, завершение сетевого запроса, кэширование данных. API простой и не требует настройки Info.plist или регистрации идентификаторов. Идеален для операций, которые занимают менее 10 секунд.
BGTaskScheduler подходит для задач, которые могут быть выполнены в любое удобное время: периодическая синхронизация, очистка кэша, обновление виджетов. Система сама выбирает оптимальное время с учётом поведения пользователя и состояния батареи. Для длительных операций (до 10 минут) используйте BGProcessingTask.
Оба API можно использовать в одном приложении. Background Task — для немедленных операций при уходе в фон (сохранение черновика). BGTaskScheduler — для плановых обновлений (синхронизация каждые 6 часов). Разделение ответственности позволяет гарантировать корректное сохранение данных и энергоэффективное фоновое обслуживание.
Часто задаваемые вопросы
Да, beginBackgroundTask можно вызывать в любой момент работы приложения. Однако системное выделение времени начнётся только после перехода в фон. Если приложение на переднем плане, вызов beginBackgroundTask не имеет эффекта — backgroundTimeRemaining возвращает DBL_MAX, а задача будет активирована при уходе в фон.
Если не вызвать endBackgroundTask, система продолжит считать приложение активным в фоне. Через 30 секунд будет вызван expiration handler, но если и в нём нет endBackgroundTask — приложение останется в памяти, расходуя батарею. В iOS 13+ система принудительно завершает такое приложение через 3 минуты.
Стандартное время — 30 секунд. Увеличить его можно только через Background Modes: Audio (воспроизведение), Location (геолокация), Bluetooth (BLE). Или через BGProcessingTask (iOS 13+) — до 10 минут при условии зарядки и Wi-Fi. beginBackgroundTask сам по себе не предоставляет способа увеличить лимит.
Да, iPadOS полностью поддерживает beginBackgroundTask с теми же ограничениями, что и iOS. На iPad с Stage Manager приложение может оставаться в памяти дольше — система реже приостанавливает приложения из-за большего объёма RAM. Но лимит 30 секунд на фоновую задачу сохраняется.
Подключите устройство к Xcode, запустите приложение, сверните его — в консоли появятся логи системы. Используйте sysdiagnose для сбора детальных логов: принудительно вызовите sysdiagnose с устройства (Volume Up + Down + Power). В Xcode Debug Navigator отслеживайте активность фоновых задач.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также