Реализация фоновой отправки данных на сервер
Пользователь выбрал 20 фотографий для загрузки и свернул приложение. Наивная реализация — загрузка прерывается. Правильная — пользователь получает уведомление «Загрузка завершена» через 10 минут после того как закрыл приложение.
iOS: URLSession с Background Configuration
Единственный правильный способ фоновой загрузки на iOS — URLSession с URLSessionConfiguration.background(withIdentifier:). Только такая сессия продолжает работу после того, как приложение ушло в фон или было завершено системой.
let config = URLSessionConfiguration.background(
withIdentifier: "com.myapp.upload.\(UUID().uuidString)"
)
config.isDiscretionary = false // загружать сразу, не откладывать
config.sessionSendsLaunchEvents = true // будить приложение по завершении
let session = URLSession(
configuration: config,
delegate: self,
delegateQueue: nil
)
Задача загрузки — только uploadTask(with:fromFile:). Данные должны быть записаны в файл на диске перед запуском задачи. uploadTask(with:from:) с Data в фоне не работает — система не может восстановить данные из памяти после перезапуска приложения.
Когда загрузка завершается (или приложение убито и перезапущено системой), iOS вызывает application(_:handleEventsForBackgroundURLSession:completionHandler:) в AppDelegate. Здесь нужно восстановить сессию с тем же идентификатором и вызвать completionHandler после обработки всех событий.
Типичная ошибка: создавать новую сессию с новым identifier при каждом запуске. При восстановлении из фона система свяжется с сессией по сохранённому идентификатору — если сессии с таким ID нет, события потеряются.
Прогресс и уведомления
Прогресс загрузки получаем через делегат urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:). В фоне обновлять UI не можем, поэтому показываем прогресс через UNMutableNotificationContent с progress — пользователь видит статус в Notification Center.
По завершении отправляем локальное уведомление через UNUserNotificationCenter:
let content = UNMutableNotificationContent()
content.title = "Загрузка завершена"
content.body = "20 фото успешно загружены"
let request = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: nil
)
UNUserNotificationCenter.current().add(request)
Android: WorkManager + чанковая загрузка
На Android WorkManager с CoroutineWorker подходит для загрузки файлов. Для больших файлов реализуем чанкование: файл разбивается на части, каждая часть загружается отдельным запросом с заголовком Content-Range. Если загрузка прервалась — продолжаем с последнего успешного чанка.
class UploadWorker(context: Context, params: WorkerParameters)
: CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val filePath = inputData.getString("file_path") ?: return Result.failure()
return try {
uploadFile(File(filePath))
Result.success()
} catch (e: Exception) {
if (runAttemptCount < 3) Result.retry()
else Result.failure()
}
}
}
runAttemptCount + Result.retry() — автоматические повторы при сбое с backoff-стратегией.
Multipart загрузка и Foreground Service
Для загрузки больших файлов (видео, архивы) на Android обязателен ForegroundService — система не убьёт процесс пока сервис показывает уведомление. WorkManager умеет делегировать задачу в Foreground через setForeground(). На Android 14+ для ForegroundService типа dataSync нужно явное разрешение в манифесте.
Сроки: базовая фоновая загрузка файлов — 1 неделя. Надёжная реализация с чанкованием, retry-логикой, прогресс-уведомлениями и тестированием на iOS + Android — 2-3 недели.







