Implementing Background Data Upload to Server
User selects 20 photos for upload and closes app. Naive implementation—upload stops. Correct—user gets "Upload complete" notification 10 minutes after closing.
iOS: URLSession with Background Configuration
Only right way for background upload on iOS—URLSession with URLSessionConfiguration.background(withIdentifier:). Only such session continues after app backgrounded or system terminates it.
let config = URLSessionConfiguration.background(
withIdentifier: "com.myapp.upload.\(UUID().uuidString)"
)
config.isDiscretionary = false // upload immediately, don't defer
config.sessionSendsLaunchEvents = true // wake app on completion
let session = URLSession(
configuration: config,
delegate: self,
delegateQueue: nil
)
Upload task—only uploadTask(with:fromFile:). Data must be written to disk before starting task. uploadTask(with:from:) with Data in background doesn't work—system can't restore data from memory after app restart.
When upload completes (or app killed and restarted), iOS calls application(_:handleEventsForBackgroundURLSession:completionHandler:) in AppDelegate. Restore session with same identifier and call completionHandler after processing all events.
Typical error: create new session with new identifier each launch. On restore from background, system connects by saved identifier—if no such session, events lost.
Progress and Notifications
Get upload progress via delegate urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:). Can't update UI in background, so show progress via UNMutableNotificationContent with progress—user sees status in Notification Center.
On completion send local notification via UNUserNotificationCenter:
let content = UNMutableNotificationContent()
content.title = "Upload Complete"
content.body = "20 photos successfully uploaded"
let request = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: nil
)
UNUserNotificationCenter.current().add(request)
Android: WorkManager + Chunked Upload
On Android, WorkManager with CoroutineWorker suitable for file upload. For large files, implement chunking: file split into parts, each uploaded separately with Content-Range header. If upload interrupts—continue from last successful chunk.
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() — automatic retries on failure with backoff strategy.
Multipart Upload and Foreground Service
For large files (video, archives) on Android, ForegroundService mandatory—system won't kill process while service shows notification. WorkManager can delegate task to Foreground via setForeground(). Android 14+ requires explicit permission in manifest for Foreground dataSync type.
Timeline: basic background file upload—1 week. Reliable implementation with chunking, retry logic, progress notifications, iOS + Android testing—2-3 weeks.







