This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/ios/samples/pdf-download-progress.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. Show PDF download progress in Swift for iOS

Show file download progress before the PDF file becomes available. Get additional resources by visiting our guide on downloading PDF from a remote URL.


//
// Copyright © 2017-2026 PSPDFKit GmbH. All rights reserved.
//
// The Nutrient sample applications are licensed with a modified BSD license.
// Please see License for details. This notice may not be removed from this file.
//
import PSPDFKit
import PSPDFKitUI
/// This example showing asynchronous document loading.
/// The downloaded document can be modified, such as adding annotations.
/// If you don’t need such fine-grained control and the document can be read-only, see the simpler `RemoteDocumentURLExample` instead.
class DocumentProgressExample: Example {
let destinationFileURL = FileHelper.temporaryPDFFileURL(prefix: "document")
let remoteURL = URL(string: "https://github.com/PSPDFKit/pspdfkit-ios-catalog/raw/master/Samples/Magazine.pdf")!
var downloader: Downloader?
override init() {
super.init()
title = "Download and annotate a PDF from a remote URL"
contentDescription = "Downloads a PDF using URLSession. Shows progress using the data provider progress property."
category = .documentDataProvider
priority = 3
}
override func invoke(with delegate: ExampleRunnerDelegate) -> UIViewController {
let cancelButton = UIBarButtonItem(title: localizedString("Cancel"), style: .plain, target: self, action: #selector(cancelButtonPressed))
downloader?.cleanup()
downloader = Downloader(remoteURL: remoteURL, destinationFileURL: destinationFileURL)
downloader?.didFinishDownloading = { _ in
cancelButton.isEnabled = false
}
// Configure the data provider with our NSProgress instance.
let provider = CoordinatedFileDataProvider(fileURL: destinationFileURL, progress: downloader?.progress)
let document = Document(dataProviders: [provider])
let controller = PDFViewController(document: document)
controller.navigationItem.setRightBarButtonItems([cancelButton] + controller.navigationItem.rightBarButtonItems!, animated: false)
return controller
}
@objc func cancelButtonPressed(sender: UIBarButtonItem?) {
sender?.isEnabled = false
guard let downloader = self.downloader else { return }
downloader.progress.cancel()
downloader.cleanup()
}
}
class Downloader: NSObject {
let progress = Progress(totalUnitCount: 100)
let downloadProgress = Progress(totalUnitCount: 1)
let moveProgress = Progress(totalUnitCount: 1)
let destinationFileURL: URL
let remoteURL: URL
var session: URLSession?
var task: URLSessionDownloadTask?
var didFinishDownloading: ((_ location: URL) -> Void)?
init(remoteURL: URL, destinationFileURL: URL) {
self.remoteURL = remoteURL
self.destinationFileURL = destinationFileURL
super.init()
// Download the file using URLSession API.
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main)
self.session = session
let task = session.downloadTask(with: remoteURL)
self.task = task
task.resume()
// Setup progress hierarchy. The download should take much longer than the final move.
progress.addChild(downloadProgress, withPendingUnitCount: 99)
progress.addChild(moveProgress, withPendingUnitCount: 1)
}
func cleanup() {
do {
task?.cancel()
session?.invalidateAndCancel()
try FileManager().removeItem(at: destinationFileURL)
} catch {
print(error)
}
}
}
extension Downloader: URLSessionDelegate, URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
do {
try FileManager().moveItem(at: location, to: destinationFileURL)
// We must ensure that the complete progress (`progress`) only completes when the file is already at its final location.
moveProgress.completedUnitCount = moveProgress.totalUnitCount
didFinishDownloading?(location)
} catch {
print(error)
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
downloadProgress.totalUnitCount = totalBytesExpectedToWrite
downloadProgress.completedUnitCount = totalBytesWritten
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error {
print(error.localizedDescription)
// Complete the progress.
progress.completedUnitCount = progress.totalUnitCount
}
}
}

This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.