Resumable File Transfers with URLSession in Swift
Large file uploads and downloads can be an integral part of modern applications, yet we often suffer from interruptions due to network issues. To provide a seamless user experience, implementing resumable file transfers in iOS apps could be essential. Apple’s URLSession provides powerful tools for handling these scenarios efficiently.
This article explores how to implement robust and resumable file transfers using URLSession in Swift, ensuring large uploads and downloads remain fault-tolerant and efficient.
Why Resumable Transfers Matter
When dealing with large files, any network interruption — such as Wi-Fi disconnection or app suspension — forces a restart without resumable protocols. This leads to:
- Wasted time restarting from the beginning.
- High bandwidth usage and unnecessary data consumption.
- Poor user experience due to failed downloads or uploads.
By leveraging modern HTTP protocols, apps can resume transfers instead of starting over, optimizing bandwidth and improving reliability.
Resumable Downloads with URLSession
How Do Resumable Downloads Work?
Initial Request:
- The app sends an HTTP GET request to fetch a file.
- The server responds with the file and includes two important headers:
Accept-Ranges: bytes
→ Indicates support for resumable downloads.ETag
→ A unique identifier for the file.
Handling Interruptions:
- If the connection is lost, the partially downloaded file is saved.
- The app sends another request with:
Range: bytes=x-
→ Requests only the missing part.If-Range: <ETag>
→ Ensures the file hasn't changed.
Resuming the Download:
- If the file is unchanged, the server responds with:
206 Partial Content
→ Sends only the missing bytes.- The app appends this data to the saved file, completing the download.
Using URLSession for Resumable Downloads
Start a Download
let urlSession = URLSession(configuration: .default)
let task = urlSession.downloadTask(with: url)
task.resume()
Pause a Download Manually
task.cancel(byProducingResumeData: { resumeData in
// Store resumeData for later use
})
Resume a Paused Download
let newTask = urlSession.downloadTask(withResumeData: resumeData)
newTask.resume()
Handle Network Failures
if let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data {
let newTask = urlSession.downloadTask(withResumeData: resumeData)
newTask.resume()
}
Requirements for Resumable Downloads
- The server must support range requests (
Accept-Ranges: bytes
). - The server must provide an
ETag
orLast-Modified
header. - The download must be an HTTP GET request.
- The temporary download file must not be deleted due to system pressure.
Resumable Uploads in iOS 17
Why Are Resumable Uploads Important?
- Upload speeds are generally slower than downloads.
- Restarting an upload due to a minor interruption wastes time and data.
- iOS 17 introduces resumable upload tasks in URLSession.
How Resumable Uploads Work
- Starting an Upload Task
- Pausing the Upload using
cancelByProducingResumeData
- Resuming with
uploadTask(withResumeData:)
- Handling Interruptions: iOS automatically retries if the server supports resumable uploads.
Server Requirements for Resumable Uploads
- The server must support the resumable upload protocol (under standardization by IETF).
- The client sends a request with:
Upload-Incomplete: ?0
→ Indicates support for resumable uploads. - If supported, the server responds with
104 Informational Response
→ Provides a resume URL. - In case of an interruption:
- The client sends a HEAD request to check how much data was received.
- The server responds with the upload offset (bytes uploaded so far).
- The client resumes from that offset.
Background URLSession for Large Transfers
Why Use Background URLSession?
- Transfers continue even if the app is closed or suspended.
- The system waits for internet connectivity before retrying failed tasks.
- iOS automatically reschedules uploads in a power-efficient way.
How to Use Background URLSession
Create a Background Session
let config = URLSessionConfiguration.background(withIdentifier: "com.app.uploads")
let backgroundSession = URLSession(configuration: config)
Use Background Session for Large Uploads/Downloads
let task = backgroundSession.downloadTask(with: url)
task.resume()
Monitor Progress in the Background
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL) {
// Handle completed download
}
Optimizing Background Transfers
- Set
isDiscretionary = true
→ Lets iOS schedule when optimal. - Use
allowsConstrainedNetworkAccess = false
→ Avoids Low Data Mode usage. - Set
countOfBytesClientExpectsToSend/Receive
→ Helps iOS allocate resources efficiently.
Conclusion
- Resumable transfers improve user experience by preventing restarts due to network issues.
2. Use URLSession for handling resumable downloads and uploads:
- Downloads: Use
cancelByProducingResumeData
anddownloadTask(withResumeData:)
. - Uploads: Utilize the new resumable upload APIs in iOS 17.
3. Ensure server support for resumable uploads (consider SwiftNIO for server-side implementation).
By implementing these techniques, your app’s networking capabilities will become more reliable, efficient, and user-friendly.