📥 How to Handle Background File Downloads in SwiftUI (the Right Way)

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)

Leave a Reply