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-2025 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 PSPDFKitimport 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.