Реализация Background Tasks для iOS-приложения
iOS агрессивно ограничивает фоновое выполнение кода. Приложение, ушедшее в background, получает 30 секунд для завершения текущей работы, затем приостанавливается. BackgroundTasks framework (iOS 13+) — единственный официальный способ запустить код в фоне на регулярной основе: синхронизация данных, обновление контента, ML-inference.
Два типа задач
BGAppRefreshTask — короткая задача (30 секунд максимум). Система запускает её при подходящем условии: устройство заряжается или подключено к Wi-Fi, пользователь активен. Подходит для обновления данных интерфейса.
BGProcessingTask — длинная задача (минуты). Запускается только при зарядке и Wi-Fi. Подходит для тяжёлых операций: миграция базы данных, загрузка большого контента, переобучение CoreML-модели.
Регистрация и планирование
В Info.plist добавляем идентификаторы задач в BGTaskSchedulerPermittedIdentifiers:
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.yourapp.refresh</string>
<string>com.yourapp.processing</string>
</array>
Регистрация в AppDelegate до applicationDidFinishLaunching завершится:
import BackgroundTasks
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.yourapp.refresh",
using: nil
) { task in
self.handleRefresh(task: task as! BGAppRefreshTask)
}
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.yourapp.processing",
using: nil
) { task in
self.handleProcessing(task: task as! BGProcessingTask)
}
return true
}
Планирование — при переходе приложения в background:
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.yourapp.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // не раньше чем через 15 мин
try? BGTaskScheduler.shared.submit(request)
}
func scheduleProcessing() {
let request = BGProcessingTaskRequest(identifier: "com.yourapp.processing")
request.requiresNetworkConnectivity = true
request.requiresExternalPower = true
request.earliestBeginDate = Date(timeIntervalSinceNow: 3600)
try? BGTaskScheduler.shared.submit(request)
}
Планировать нужно каждый раз при уходе в background через sceneDidEnterBackground или applicationDidEnterBackground. Один вызов submit — одна постановка задачи в очередь.
Обработчик задачи
func handleRefresh(task: BGAppRefreshTask) {
// Перепланируем следующее выполнение
scheduleAppRefresh()
let syncTask = Task {
do {
try await DataSyncService.shared.sync()
task.setTaskCompleted(success: true)
} catch {
task.setTaskCompleted(success: false)
}
}
// Обязательно! Если система забирает CPU — отменяем и сигнализируем
task.expirationHandler = {
syncTask.cancel()
task.setTaskCompleted(success: false)
}
}
expirationHandler — самая частая ошибка, которую пропускают. Если не вызвать setTaskCompleted до истечения времени, система убивает процесс и помечает приложение как злоупотребляющее background execution. После нескольких таких случаев iOS перестаёт запускать задачи приложения.
Когда система реально запускает задачи
Нельзя гарантировать конкретное время запуска — iOS решает сама на основе паттернов использования, заряда батареи, сети. Приложения, которыми пользователь пользуется часто, получают больше фонового времени. Новое приложение или редко используемое — меньше.
Отладка в Xcode: задачи можно принудительно запустить через Xcode Debug Menu во время отладки на реальном устройстве:
Xcode → Debug → Simulate Background Fetch
Или через lldb:
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.yourapp.refresh"]
Симулятор не поддерживает BackgroundTasks — только реальное устройство.
URLSession background transfer
Для фоновой загрузки и выгрузки файлов BackgroundTasks не нужен — используем URLSessionConfiguration.background(withIdentifier:). Система сама управляет передачей, приложение получает колбэк при завершении через AppDelegate.application(_:handleEventsForBackgroundURLSession:completionHandler:). Это работает даже если приложение было выгружено из памяти.
Ориентиры по срокам
| Задача | Срок |
|---|---|
| BGAppRefreshTask (синхронизация данных) | 1 день |
| BGProcessingTask (тяжёлые операции) | 1–2 дня |
| + URLSession background transfer | +0.5–1 день |
| Полная фоновая инфраструктура | 2–3 дня |







