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.
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!
import Foundation #if canImport(FoundationNetworking) import FoundationNetworking #endifclass 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 } }
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.
import Foundation #if canImport(FoundationNetworking) import FoundationNetworking #endifclass 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)") } } }
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!
When a background download finishes, where does the system initially place the downloaded file?