iOS Background URLSession

iOS Background URLSession

Downloading large files like podcast episodes, high-resolution videos, or offline maps can take several minutes.

If a user starts a download and then puts their iPhone in their pocket, your app will be suspended, and a standard network request will fail.

To keep downloading files even when your app is completely inactive or killed, you must use a Background URLSession.


Creating a Background Configuration

To initiate background downloads, you configure a URLSession using a special background configuration identifier.

This identifier is crucial; if your app is terminated and later restarted, iOS uses this ID to re-attach your app to the ongoing downloads!

Configuring a Background Session:

import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

class DownloadManager: NSObject, URLSessionDownloadDelegate { lazy var backgroundSession: URLSession = { #if os(Linux) let config = URLSessionConfiguration.default // Fallback for the Linux web editor #else let config = URLSessionConfiguration.background(withIdentifier: "com.intricatedevo.videoDownload") // The system will schedule the download at the optimal time config.isDiscretionary = true #endif return URLSession(configuration: config, delegate: self, delegateQueue: nil) }() func startDownload(url: URL) { let task = backgroundSession.downloadTask(with: url) task.resume() print("Background download initiated.") } // Required delegate method to conform to URLSessionDownloadDelegate func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { // Handled in the next example } }


Handling Download Completion

Because background downloads are managed by the iOS operating system directly (not your app), you cannot use simple completion closures.

You must adopt the URLSessionDownloadDelegate protocol.

When the download finishes, the OS places the file in a temporary location and triggers the didFinishDownloadingTo delegate method.

Saving the Downloaded File:

import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

class DownloadManager: NSObject, URLSessionDownloadDelegate { func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { // The 'location' is a temporary URL. If you don't move it NOW, it will be deleted! let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let destinationURL = documentsPath.appendingPathComponent("downloadedVideo.mp4") do { try FileManager.default.moveItem(at: location, to: destinationURL) print("File permanently saved to Documents!") } catch { print("File move failed: \(error)") } } }


Waking Up the App

If your app was killed by the system while the download was happening, iOS will silently launch your app in the background when the download finishes.

It calls a special method in your AppDelegate (handleEventsForBackgroundURLSession).

You must reconnect to your background session using the same identifier to receive the completion callbacks, and then tell the system you are done so it can put your app back to sleep!


Exercise

When a background download finishes, where does the system initially place the downloaded file?