Usage

API Overview

The entry class into Instant is PSPDFInstantClient; this represents a connection to your PSPDFKit Server. An app would typically create one PSPDFInstantClient, although it could create several to connect to multiple PSPDFKit Server instances.

Instant manages downloading and storing the PDF files when your app requests this. The API to each document managed by Instant is PSPDFInstantDocumentDescriptor. A document descriptor may be used to create a PSPDFDocument and to request downloading the associated PDF file. Set this document on a PSPDFInstantViewController and then show it to the user. It will show a download progress bar and then refresh to show the PDF when the download completes. Changes to annotations in the document will be seen by all users viewing the same document.

PSPDFInstantViewController is a subclass of PSPDFViewController and may be used to show any PSPDFDocument — not only ones created by Instant, although there will be no annotation synchronization in that case. This means it is possible to create a PSPDFTabbedViewController whose internal pdfController is a PSPDFInstantViewController. You can mix documents managed by Instant with other documents in the tabbed bar. Showing a PSPDFDocument created by Instant in any PSPDFViewController except a PSPDFInstantViewController is not supported.

Document Lifecycle

When using a document managed by Instant, you might go through these typical stages:

  • Obtain a document identifier from your server
  • Obtain a document descriptor
  • Download the document
  • Show the document and synchronize annotations
  • Update the authentication token to keep synchronizing annotations
  • Lose access to the document
  • Clear local storage

Usage in Detail

After signing in to your server and finding out the identifier of the document you want to show to the user, obtain a document descriptor from Instant:

Copy
1
2
3
4
5
6
7
8
9
let instantClient = PSPDFInstantClient(serverURL: URL(string: "location of your PSPDFKit Server here")!)
let documentIdentifier = "some document identifier from your server"

do {
    let documentDescriptor = try instantClient.documentDescriptor(withIdentifier: documentIdentifier)
} catch {
    print("This document identifier is invalid: \(error)")
    return
}
Copy
1
2
3
4
5
6
7
8
9
PSPDFInstantClient *client = [[PSPDFInstantClient alloc] initWithServerURL:[NSURL URLWithString:@"location of your PSPDFKit Server here"]];
NSString *documentIdentifier = @"some document identifier from your server";

NSError *error;
id<PSPDFInstantDocumentDescriptor> documentDescriptor = [client documentDescriptorWithIdentifier:documentIdentifier error:&error];
if (documentDescriptor == nil) {
    NSLog(@"This document identifier is invalid: %@", error);
    return;
}

To show the document to the user, you need to show the view controller and download the document. These can happen in either order depending on your needs. If you show the view controller while the document is still downloading, PSPDFKit will display a progress bar and automatically refresh when the download finishes.

Show a PSPDFInstantViewController and set its document to a PSPDFDocument managed by Instant:

Copy
1
2
3
4
5
let pdfDocument = documentDescriptor.editableDocument
let pdfViewController = PSPDFInstantViewController(document: document)
self.navigationController?.pushViewController(pdfViewController, animated: true)
// or
self.present(pdfViewController, animated: true)
Copy
1
2
3
4
5
PSPDFDocument *pdfDocument = documentDescriptor.editableDocument;
PSPDFInstantViewController *pdfViewController = [[PSPDFInstantViewController alloc] initWithDocument:document];
[self.navigationController pushViewController:pdfViewController animated:YES];
// or
[self presentViewController:pdfViewController animated:YES completion:NULL];

Check if the document has been downloaded using the isDownloaded property on the document descriptor. If not, obtain a JSON Web Token (JWT) for this document from your server and use it to start the download. This would typically be asynchronous, so you need to avoid making multiple concurrent requests for the same token.

Copy
1
2
3
4
5
6
let JWT = "token from your server"
do {
    try documentDescriptor.downloadDocument(usingAuthenticationToken: JWT)
} catch {
    print("Could not start downloading document with identifier \(documentDescriptor.identifier): \(error)")
}
Copy
1
2
3
4
5
6
NSString *JWT = @"token from your server";

NSError *error;
if (![documentDescriptor downloadDocumentUsingAuthenticationToken:JWT error:&error]) {
    NSLog(@"Could not start downloading document with identifier %@: %@", documentDescriptor.identifier, error);
}

When a download finishes, PSPDFInstantViewController will refresh automatically. The Instant client’s delegate will be notified by a call to instantClient:didFinishDownloadForDocument:, and PSPDFInstantDidFinishDownloadNotification will be posted with the document descriptor as the object.

Instant does not persist authentication tokens, and the tokens have an expiry date. When a new token is needed for a document, the instantClient:didFailAuthenticationForDocument: delegate method will be called and PSPDFInstantDidFailAuthenticationNotification will be posted with PSPDFInstantDocumentDescriptor as the object. This will always be called during the first time syncing a document after app launch because tokens are not persisted.

When your app receives this callback, it should request a new token from your server and pass this token to Instant to keep synchronizing annotations:

1
2
let JWT = "token from your server"
documentDescriptor.updateAuthenticationToken(JWT)
1
2
NSString *JWT = "token from your server";
[documentDescriptor updateAuthenticationToken:JWT];

Your app might find out that a user no longer has access to a document from your server or from the PSPDFKit Server. If your app finds the user has lost access to the document, then you will probably want to stop showing the document and clear the local storage:

Copy
1
2
3
4
5
do {
    try documentDescriptor.removeLocalStorage()
} catch {
    // Errors are unlikely, but it is possible the delete from the file system fails.
}
Copy
1
2
3
4
5
NSError *error;

if (![documentDescriptor removeLocalStorageWithError:&error]) {
    // Errors are unlikely, but it is possible the delete from the file system fails.
}