Імплементація фонової завантаження/скачування файлів у мобільному застосунку
Різниця між звичайною та фоновою завантаженням — принципіальна. Звичайна працює, поки користувач дивиться на екран. Фонова продовжується після згортання застосунку, блокування екрана й навіть після примусового завершення процесу системою. Реалізація фонової передачі файлів — одна з тих завдань, де «майже працює» та «працює правильно» розділяють кілька тонких деталей.
iOS: URLSession з фоновою конфігурацією
На iOS єдиний підтримуваний Apple спосіб фонової передачі файлів — URLSessionConfiguration.background. Система бере процес завантаження під управління, застосунок може бути вивантажений та відновлений при завершенні.
let config = URLSessionConfiguration.background(withIdentifier: "com.app.bgTransfer")
config.sessionSendsLaunchEvents = true
config.isDiscretionary = false // для срочних передач
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
Обов'язково реалізувати в AppDelegate:
func application(_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void) {
backgroundSessionCompletionHandler = completionHandler
}
Та в делегаті сесії викликати completionHandler по завершенні всіх завдань у urlSessionDidFinishEvents(forBackgroundURLSession:). Якщо не викликати — iOS подумає, що застосунок завис та убить його.
Типова помилка: створювати кілька URLSession з одним identifier. При відновленні застосунку iOS шукає існуючу сесію по ідентифікатору — якщо створити дубль, завдання втратяться.
Завантаження великих файлів має використовувати uploadTask(with:fromFile:) а не uploadTask(with:from:) — другий варіант вимагає завантаження всього файлу в пам'ять, що гарантовано убить процес на відео > 100 МБ.
Android: WorkManager vs DownloadManager vs ForegroundService
Три підходи, у кожного свої сценарії:
| Підхід | Підходить для | Мінуси |
|---|---|---|
DownloadManager |
Завантаження публічних URL | Лише download, немає fine-grained контролю |
WorkManager |
Завантаження/скачування з логікою повтору | Обмеження 10 хвилин на завдання |
ForegroundService |
Довгі передачі > 10 хв | Вимагає уведомлення, користувач може убити |
Для upload великих файлів на Android WorkManager + setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) — оптимальний вибір. Worker.doWork() має повертати Result.retry() при помилці мережі, WorkManager перезапустить з backoff.
class UploadWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
override suspend fun doWork(): Result {
val fileUri = inputData.getString("fileUri") ?: return Result.failure()
return try {
uploadFile(fileUri)
Result.success()
} catch (e: IOException) {
if (runAttemptCount < 3) Result.retry() else Result.failure()
}
}
}
Constraints задаємо при постановці завдання:
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
Прогрес для фонових завдань
Уведомлення з прогресом — через WorkManager setProgressAsync + спостереження через WorkManager.getInstance().getWorkInfoByIdLiveData(workId) в UI. На iOS системний URLSession показує прогрес у Centre управління для завантажень автоматично. Для кастомного прогресу — URLSessionDownloadDelegate.urlSession(_:downloadTask:didWriteData:).
Flutter: flutter_downloader вирішує фонове завантаження для обох платформ через нативні реалізації. WorkManagerPlugin — для custom upload завдань. Для React Native — react-native-background-upload (iOS) та кастомний HeadlessTask + WorkManager (Android).
Resumable uploads
Якщо файл великий (відео 500 МБ+), реалізуємо resumable upload: файл ділиться на чанки (наприклад, 5 МБ), кожен чанк завантажується окремо, сервер збирає файл. При обриві — продовжуємо з останнього успішного чанка. Для AWS S3 — multipart upload API, для GCS — resumable upload sessions, для власного бекенду — tus протокол (TUSKit на iOS, tus-android-client).
Що входить у роботу
Визначаємо підходящий механізм під платформу та вимоги, реалізуємо прогрес у уведомленнях, обробку помилок з retry, інтеграцію з UI. Якщо потрібен chunked/resumable upload — проектуємо протокол спільно з бекендом.
Строк: 4–8 днів з врахуванням тестування на реальних пристроях в умовах обриву мережі.







