Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2023 System Services

WWDC23 · 21 min · System Services

Build robust and resumable file transfers

Find out how URLSession can help your apps transfer large files and recover from network interruptions. Learn how to pause and resume HTTP file transfers and support resumable uploads, and explore best practices for using URLSession to transfer files even when your app is suspended in the background.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

  • 0:00 — Welcome
  • 2:42 — Explore the resumable downloads protocol in HTTP
  • 4:23 — Pause and resume downloads with URLSession
  • 7:50 — Pause and resume uploads with URLSession
  • 9:45 — Explore the resumable uploads protocol in HTTP
  • 12:46 — Add resumable uploads to SwiftNIO
  • 16:11 — Use background URLSession

Code shown on screen · 12 snippets

Pausing and resuming a URLSessionDownloadTask swift · at 4:53 ↗
let downloadTask = session.downloadTask(with: request)
downloadTask.resume()
Pausing and resuming a URLSessionDownloadTask swift · at 5:21 ↗
let downloadTask = session.downloadTask(with: request)
downloadTask.resume()

guard let resumeData = await downloadTask.cancelByProducingResumeData() else {
    // Download cannot be resumed
    return
}
Pausing and resuming a URLSessionDownloadTask swift · at 6:11 ↗
let downloadTask = session.downloadTask(with: request)
downloadTask.resume()

guard let resumeData = await downloadTask.cancelByProducingResumeData() else {
    // Download cannot be resumed
    return
}

let newDownloadTask = session.downloadTask(withResumeData: resumeData)
newDownloadTask.resume()
Retrieving resume data on error swift · at 6:34 ↗
do {
    let (url, response) = try await session.download(for: request)
} catch let error as URLError {
    guard let resumeData = error.downloadTaskResumeData else {
        // Download cannot be resumed
        return
    }
}
Pausing and resuming a URLSessionUploadTask swift · at 8:29 ↗
let uploadTask = session.uploadTask(with: request, fromFile: fileURL)
uploadTask.resume()
Pausing and resuming a URLSessionUploadTask swift · at 8:37 ↗
let uploadTask = session.uploadTask(with: request, fromFile: fileURL)
uploadTask.resume()

guard let resumeData = await uploadTask.cancelByProducingResumeData() else {
    // Upload cannot be resumed
    return
}
Pausing and resuming a URLSessionUploadTask swift · at 8:57 ↗
let uploadTask = session.uploadTask(with: request, fromFile: fileURL)
uploadTask.resume()

guard let resumeData = await uploadTask.cancelByProducingResumeData() else {
    // Upload cannot be resumed
    return
}

let newUploadTask = session.uploadTask(withResumeData: resumeData)
newUploadTask.resume()
Retrieving resume data on error swift · at 9:22 ↗
do {
    let (data, response) = try await session.upload(for: request, fromFile: fileURL)
} catch let error as URLError {
    guard let resumeData = error.uploadTaskResumeData else {
        // Upload cannot be resumed
        return
    }
}
Before resumable uploads in Swift NIO swift · at 13:15 ↗
NIOTSListenerBootstrap(group: NIOTSEventLoopGroup())
    .childChannelInitializer { channel in
        channel.configureHTTP2Pipeline(mode: .server) { channel in
            channel.pipeline.addHandlers([
                HTTP2FramePayloadToHTTPServerCodec(),
                ExampleChannelHandler()
            ])
        }.map { _ in () }
    }
    .tlsOptions(tlsOptions)
Add resumable uploads in Swift NIO swift · at 14:06 ↗
import NIOResumableUpload

let uploadContext = HTTPResumableUploadContext(origin: "https://example.com")

NIOTSListenerBootstrap(group: NIOTSEventLoopGroup())
    .childChannelInitializer { channel in
        channel.configureHTTP2Pipeline(mode: .server) { channel in
            channel.pipeline.addHandlers([
                HTTP2FramePayloadToHTTPServerCodec(),
                HTTPResumableUploadHandler(context: uploadContext, handlers: [
                    ExampleChannelHandler()
                ])
            ])
        }.map { _ in () }
    }
    .tlsOptions(tlsOptions)
Informational responses in URLSession swift · at 15:48 ↗
protocol URLSessionTaskDelegate : URLSessionDelegate {
    optional func urlSession(_ session: URLSession, task: URLSessionTask,
                             didReceiveInformationalResponse response: HTTPURLResponse)
}
Using background URLSession swift · at 18:19 ↗
// Configuring your background session
let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.app")
configuration.isDiscretionary = true
configuration.allowsConstrainedNetworkAccess = false
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)

// Configuring your background task
let backgroundTask = session.uploadTask(with: url, fromFile: fileURL)
backgroundTask.earliestBeginDate = .now.addingTimeInterval(60 * 60)
backgroundTask.countOfBytesClientExpectsToSend = 500 * 1024
backgroundTask.countOfBytesClientExpectsToReceive = 200

Resources