Ответ
В iOS для фонового выполнения используются Background Modes и API beginBackgroundTask. Почему это важно: Система приостанавливает приложение в фоне для экономии заряда, поэтому для завершения критичных операций (например, сохранения данных или завершения сетевого запроса) требуется явно запросить дополнительное время.
Пример: Завершение сетевого запроса при уходе в фон
- Объявите capability в Xcode (например, Background fetch) или добавьте ключ в
Info.plistдля специальных режимов (аудио, локация, VOIP). - Запросите фоновое время в
AppDelegateилиSceneDelegate:
func applicationDidEnterBackground(_ application: UIApplication) {
// Объявляем идентификатор фоновой задачи
var backgroundTask: UIBackgroundTaskIdentifier = .invalid
// Запрашиваем у системы дополнительное время
backgroundTask = application.beginBackgroundTask(withName: "Finish Network Call") {
// Этот блок выполнится, если время истечёт. Нужно завершить операцию и очистить ресурсы.
application.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
// Запускаем асинхронную операцию, которую нужно завершить
DispatchQueue.global().async {
// Выполняем долгую операцию (например, отправку логов на сервер)
self.sendLogsToServer()
// По завершении ОБЯЗАТЕЛЬНО сообщаем системе
application.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
}
Ключевые моменты:
- Лимит времени: Обычно ~30 секунд. Точное время доступно через
application.backgroundTimeRemaining. - Обязательная очистка: Всегда вызывайте
endBackgroundTask, иначе приложение будет убито системой. - Для периодических задач (iOS 13+) используйте
BackgroundTasks.frameworkдляBGAppRefreshTaskилиBGProcessingTask.
Ответ 18+ 🔞
А, ну вот, подъехали к самому сокровенному — как заставить эту суку-систему не убивать твой код, когда юзер тыкает в домашнюю кнопку. Слушай сюда, это важно, как дважды два, блядь.
iOS, она же хитрая жопа, считает, что раз приложение не на экране — значит ему батарейку жрать незачем. И просто приостанавливает все процессы, накрывает их медным тазом. А если ты там сетевое рукопожатие не закончил или данные не сохранил — всё, пиздец, операция в говне. Поэтому надо ей явно сказать: «Э, подожди, сука, дай мне минуточку, я тут дельце одно доделаю».
Как это работает, на пальцах:
- Ты должен легализоваться. Иди в Xcode, в настройках таргета, найди там
Background Modesи галочку поставь. Ну, или вInfo.plistключик нужный впиши, если у тебя там аудио плеер, навигация или ещё какая специальная хрень. Без этого система на тебя посмотрит как на идиота и времени не даст. - Просишь время у системы. Это делается через
beginBackgroundTask. Это как сказать: «Система, я ща быстро, на пять минут, честно-честно».
Вот смотри, как это в коде выглядит, ёпта:
func applicationDidEnterBackground(_ application: UIApplication) {
// Заводим себе «пропуск» на фоновую деятельность
var backgroundTask: UIBackgroundTaskIdentifier = .invalid
// Идём к системе и говорим: «Начальница, можно мне фон? Называться будет "Закончить запрос"»
backgroundTask = application.beginBackgroundTask(withName: "Finish Network Call") {
// А это блок, который вызовется, если ты проёбываешься и не успеваешь.
// Типа последний шанс всё похерить и прибраться, чтобы тебя не прибили.
application.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
// Получил пропуск — теперь делай что хотел, но в отдельной очереди, не в главной!
DispatchQueue.global().async {
// Допустим, шлёшь последние логи перед смертью
self.sendLogsToServer()
// КАК ТОЛЬКО ЗАКОНЧИЛ — БЛЯДЬ, СРАЗУ СДАЙ ПРОПУСК НАЗАД!
// Не будь мудаком, который систему обманывает.
application.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
}
А теперь главное, что ты должен вынести, чтобы не обосраться:
- Время — не резиновое. Обычно это около 30 секунд, не больше. Узнать, сколько осталось, можно через
application.backgroundTimeRemaining. Не расслабляйся. - Обязательно сдай пропуск! Вызов
endBackgroundTask— это святое. Не вызовешь — система тебя возненавидит, сожрёт всю батарею и в итоге всё равно прибьёт, но уже с позором. - Если тебе надо что-то делать периодически (типа раз в час данные подтягивать), то для этого с iOS 13 есть отдельная, более сложная бюрократия —
BackgroundTasks.framework. Там уже задачиBGAppRefreshTaskиBGProcessingTask. Это уже для серьёзных пацанов, которые планируют свои дела заранее, а не в последний момент, как мы тут.
Короче, суть в том, чтобы не надеяться на авось, а чётко договориться с системой. Сделал дело — гуляй смело, а иначе получишь пизды.