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

Connect to our example Nutrient Instant server and download a document for collaborative editing. Get additional resources by visiting our PDF collaboration library for iOS.


//
// 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 Instant
import PSPDFKit
import PSPDFKitUI
/// This example connects to our public Nutrient Web SDK examples server and downloads documents using Nutrient Instant.
/// You can then collaboratively annotate these documents using Instant.
/// Each document on the Nutrient Web SDK examples server can be accessed via its URL.
///
/// The example lets you either create a new collaboration group then share the URL,
/// or join an existing collaboration group by entering a URL.
/// To speed this up, the URL can be read from a QR code.
///
/// Other supported clients include:
///
/// - Nutrient Web SDK examples: https://web-examples.our.services.nutrient-powered.io
/// - PDF Viewer for iOS and Android: https://pdfviewer.io
/// - Nutrient Catalog example app for Android
///
/// As is usually the case with Instant, most of the code here deals with communicating with the
/// particular server backend (our Web examples server in this case), so is not particularly useful
/// as example code. The same applies to the other files in this folder.
///
/// The code actually interacting with the Instant framework API is in `InstantDocumentViewController.swift`.
class InstantExample: Example {
override init() {
super.init()
title = "Nutrient Instant"
contentDescription = "Downloads a document for collaborative editing."
category = .collaboration
targetDevice = [.vision, .phone, .pad]
priority = 1
}
weak var presentingViewController: UIViewController?
override func invoke(with delegate: ExampleRunnerDelegate) -> UIViewController? {
let presentingViewController = delegate.currentViewController!
// We store the document info once the session is created.
// We will try to find the stored info matching the document identifier for this example if available.
if let docInfoDict = UserDefaults.standard.object(forKey: InstantExampleLastViewedDocumentInfoKey) as? [String: String],
let lastViewDocumentInfo = InstantDocumentInfo(from: docInfoDict) {
// Present the InstantViewController directly using the existing document info extracted from the cache.
presentInstantViewController(for: lastViewDocumentInfo, on: presentingViewController)
} else {
presentNewSession(on: presentingViewController)
}
// Hold a reference to the presentingViewController to handle authentication challenges.
self.presentingViewController = presentingViewController
return nil
}
/// Connects to the Example API Client to get the document info for the current example
/// and then displays that document in a `InstantViewController`.
func presentNewSession(on viewController: UIViewController) {
// Create a `WebExamplesAPIClient` instance using the URL of the client server
// that has the list of documents the particular user can access.
//
// `WebExamplesAPIClient` can make calls to the server to get the document info required
// to connect to a Nutrient Document Engine instance.
//
// For the purpose of this example we are using the Nutrient Web SDK Catalog server.
// In your app, this should be replaced by the URL of your that is interacting with your Nutrient Document Engine.
// See https://www.nutrient.io/guides/ios/instant-synchronization/ for more details.
let apiClient = WebExamplesAPIClient(baseURL: InstantWebExamplesServerURL, delegate: self)
let progressHUDItem = StatusHUDItem.indeterminateProgress(withText: "Creating")
progressHUDItem.setHUDStyle(.black)
progressHUDItem.push(animated: true, on: viewController.view.window) {
// Asking the Nutrient Web SDK Catalog example server to create a new session
// for the given user and the specified document identifier.
// It should ideally provide a signed JWT (and the Nutrient Document Engine if not already available)
// that can be used by the `InstantClient` to access and download the document for iOS.
apiClient.createNewSession(documentIdentifier: .demoDocument) { result in
DispatchQueue.main.async {
progressHUDItem.pop(animated: true, completion: nil)
switch result {
case let .success(documentInfo):
self.presentInstantViewController(for: documentInfo, on: viewController)
case .failure(.cancelled):
break // Do not show the alert if user cancelled the request themselves.
case let .failure(otherError):
viewController.showAlert(withTitle: "Couldn’t Get Instant Document Info", message: otherError.localizedDescription)
}
}
}
}
}
private func presentInstantViewController(for instantDocumentInfo: InstantDocumentInfo, on viewController: UIViewController) {
let instantViewController = InstantDocumentViewController(documentInfo: instantDocumentInfo, lastViewedDocumentInfoKey: InstantExampleLastViewedDocumentInfoKey)
instantViewController.title = "Nutrient Instant Example"
let navigationController = UINavigationController(rootViewController: instantViewController)
navigationController.modalPresentationStyle = .fullScreen
viewController.present(navigationController, animated: true)
}
}
extension InstantExample: WebExamplesAPIClientDelegate {
func examplesAPIClient(_ apiClient: WebExamplesAPIClient, didReceiveBasicAuthenticationChallenge challenge: URLAuthenticationChallenge, completion: @escaping (URLCredential?) -> Void) {
presentingViewController?.presentBasicAuthPrompt(for: challenge) { username, password in
guard let user = username,
let pass = password else {
completion(nil)
return
}
let urlCredential = URLCredential(user: user, password: pass, persistence: .permanent)
completion(urlCredential)
}
}
}

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