Реалізація 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 фонова передача
Для фонової завантаження та вивантаження файлів BackgroundTasks не потрібен — використовуємо URLSessionConfiguration.background(withIdentifier:). Система сама керує передачею, програма отримує колбек при завершенні через AppDelegate.application(_:handleEventsForBackgroundURLSession:completionHandler:). Це працює навіть якщо програма була вивантажена з пам'яті.
Ориентири по срокам
| Завдання | Срок |
|---|---|
| BGAppRefreshTask (синхронізація даних) | 1 день |
| BGProcessingTask (важкі операції) | 1–2 дні |
| + URLSession фонова передача | +0.5–1 день |
| Повна фонова інфраструктура | 2–3 дні |







