Downloading big files? Users leave your app? No problem. Here’s a quick, modern guide to make background downloads in SwiftUI seamless; files saved even if your app gets nuked.
🗂️ What You’ll Build
- Background downloads with
URLSessionDownloadTask
- Auto-moving files to permanent storage
- Resuming and finishing downloads post-termination
- A clean SwiftUI integration
⚙️ 1️⃣ Build a Download Manager
Your download powerhouse: a singleton with a background URLSession
.
import Foundation
class DownloadManager: NSObject, ObservableObject, URLSessionDownloadDelegate {
static let shared = DownloadManager()
private var backgroundSession: URLSession!
private override init() {
super.init()
let config = URLSessionConfiguration.background(withIdentifier: "com.yourapp.download")
config.sessionSendsLaunchEvents = true
config.isDiscretionary = false
backgroundSession = URLSession(configuration: config, delegate: self, delegateQueue: nil)
}
func startDownload(from url: URL) {
backgroundSession.downloadTask(with: url).resume()
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
let fm = FileManager.default
let docs = fm.urls(for: .documentDirectory, in: .userDomainMask)[0]
let dest = docs.appendingPathComponent(downloadTask.response?.suggestedFilename ?? UUID().uuidString)
do {
if fm.fileExists(atPath: dest.path) { try fm.removeItem(at: dest) }
try fm.moveItem(at: location, to: dest)
print("✅ Saved to \(dest)")
} catch {
print("❌ Move failed: \(error)")
}
}
var backgroundCompletionHandler: (() -> Void)?
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
self.backgroundCompletionHandler?()
self.backgroundCompletionHandler = nil
}
}
}
📡 2️⃣ Handle Background Events in AppDelegate
Background sessions need an AppDelegate, even with SwiftUI.
import UIKit
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void) {
print("🌙 Resuming: \(identifier)")
DownloadManager.shared.backgroundCompletionHandler = completionHandler
}
}
Plug it into your main app:
@main
struct YourApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
📲 3️⃣ Start the Download in SwiftUI
Simple button, big files — done.
import SwiftUI
struct ContentView: View {
var body: some View {
Button("Download File") {
if let url = URL(string: "https://example.com/file.zip") {
DownloadManager.shared.startDownload(from: url)
}
}
.padding()
}
}
✅ Pro Tips
sessionSendsLaunchEvents = true
wakes your app when needed.- Always move files out of the temp location.
- Save download state if you need to handle multiple files or resume later.
- Want a progress bar? Implement
didWriteData
.
With this setup, your app handles background downloads like a pro; no lost files, no headaches.
Learn more 📥 How to Handle Background File Downloads in SwiftUI (the Right Way)