---
title: "Open PDF from Document Engine on iOS | Nutrient SDK"
canonical_url: "https://www.nutrient.io/guides/ios/open-a-document/from-document-engine/"
md_url: "https://www.nutrient.io/guides/ios/open-a-document/from-document-engine.md"
last_updated: "2026-06-19T07:57:11.440Z"
description: "Learn to display PDFs from your server using the Instant API on iOS. Explore document lifecycle stages and manage JWTs for seamless user access and annotation synchronization."
---

# Open a PDF from Document Engine on iOS

This guide will give you an overview of the Instant API and how to use it for displaying documents from the server, in addition to talking about the lifecycle documents go through while being displayed.

For a more complete example application, refer to our guide on [getting started with real-time collaboration](https://www.nutrient.io/guides/ios/pspdfkit-instant/getting-started.md#quick-start-with-the-example-project), which shows the expected lifetimes of the key components of Instant in better detail.

## Document lifecycle

When using a PDF file managed by Instant, you might go through these stages:

- Obtaining a JSON Web Token (JWT) for a layer from your server.

- Obtaining a document descriptor for the layer encoded in the JWT from the [`InstantClient`](https://www.nutrient.io/api/ios/documentation/instant/instantclient).

- Using the document descriptor to download the layer data with the JWT.

- Showing the document descriptor’s [`editableDocument`](https://www.nutrient.io/api/ios/documentation/instant/instantdocumentdescriptor/editabledocument) and synchronizing annotations.

- Updating the JWT to keep synchronizing annotations.

- Losing access to the layer.

- Clearing the local storage for the layer or the document identifier.

You can read more about the lifecycle of a document descriptor in our guide on [document state](https://www.nutrient.io/guides/ios/pspdfkit-instant/instant-document-state.md).

## Detailed use

After signing in to your server and getting the JWT for the layer you want to show to the user, obtain a matching document descriptor from Instant:

### SWIFT

```swift

do {
    let instantClient = try InstantClient(serverURL: URL(string: "location of your Document Engine here")!)
    let JWT = "a JWT for some layer from your server"
    let documentDescriptor = try instantClient.documentDescriptor(forJWT: JWT)
    // IMPORTANT: The `instantClient` needs to be kept alive for as long
    // as you want to work with Instant. Store it in a property, generally
    // of your `AppDelegate`.
} catch {
    // Handle error.
}

```

### OBJECTIVE-C

```objc

PSPDFInstantClient *client = [[PSPDFInstantClient alloc] initWithServerURL:[NSURL URLWithString:@"location of your Document Engine here"]];
NSString *JWT = @"a JWT for some layer from your server";

NSError *error;
id<PSPDFInstantDocumentDescriptor> documentDescriptor = [client documentDescriptorForJWT:JWT error:&error];
if (documentDescriptor == nil) {
    NSLog(@"This JWT is invalid: %@", error);
    return;
}
// IMPORTANT: The `instantClient` needs to be kept alive for as long
// as you want to work with Instant. Store it in a property, generally
// of your `AppDelegate`.

```

To display the layer to the user, you need to show it in an [`InstantViewController`](https://www.nutrient.io/api/ios/documentation/instant/instantviewcontroller) and download the layer’s data. This can happen in either order, depending on your needs. If you show the view controller while the layer’s data is still downloading, Nutrient will display a progress bar and automatically refresh when the download finishes.

Show an [`InstantViewController`](https://www.nutrient.io/api/ios/documentation/instant/instantviewcontroller) and set its `document` to a [`Document`](https://www.nutrient.io/api/ios/documentation/pspdfkit/document) managed by Instant:

### SWIFT

```swift

let pdfDocument = documentDescriptor.editableDocument
let pdfViewController = InstantViewController(document: pdfDocument)
self.navigationController?.pushViewController(pdfViewController, animated: true)
// Or
self.present(pdfViewController, animated: true)

```

### OBJECTIVE-C

```objc

PSPDFDocument *pdfDocument = documentDescriptor.editableDocument;
PSPDFInstantViewController *pdfViewController = [[PSPDFInstantViewController alloc] initWithDocument:pdfDocument];
[self.navigationController pushViewController:pdfViewController animated:YES];
// Or
[self presentViewController:pdfViewController animated:YES completion:NULL];

```

Check if the layer data has been downloaded by using the [`isDownloaded`](https://www.nutrient.io/api/ios/documentation/instant/instantdocumentdescriptor/isdownloaded) property on the document descriptor. If not, use the JWT for this layer to start the download. This would typically be asynchronous, so you need to avoid making multiple concurrent requests for the same JWT:

### SWIFT

```swift

let JWT = "JWT for the layer from your server"
do {
    try documentDescriptor.download(usingJWT: JWT)
} catch {
    print("Could not start downloading layer '\(documentDescriptor.layerName)' of document '\(documentDescriptor.identifier)': \(error)")
}

```

### OBJECTIVE-C

```objc

NSString *JWT = @"token from your server";

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

```

When the download finishes, [`InstantViewController`](https://www.nutrient.io/api/ios/documentation/instant/instantviewcontroller) will refresh automatically. The Instant client’s [`delegate`](https://www.nutrient.io/api/ios/documentation/instant/instantclient/delegate) will be notified by a call to [`instantClient(_:didFinishDownloadFor:)`](https://www.nutrient.io/api/ios/documentation/instant/instantclientdelegate/instantclient(_:didfinishdownloadfor:)), and [`PSPDFInstantDidFinishDownload`](https://www.nutrient.io/api/ios/documentation/instant/foundation/nsnotification/name/pspdfinstantdidfinishdownload) will be posted with the document descriptor as the object.

### Updating the JWT for a layer

Instant doesn’t permanently store JWTs, and the JWTs have an expiration date. When a new JWT is needed for a layer, the [`instantClient(_:didFailAuthenticationFor:)`](https://www.nutrient.io/api/ios/documentation/instant/instantclientdelegate/instantclient(_:didfailauthenticationfor:)) delegate method will be called, and [`PSPDFInstantDidFailReauthentication`](https://www.nutrient.io/api/ios/documentation/instant/foundation/nsnotification/name/pspdfinstantdidfailreauthentication) will be posted with the corresponding [`InstantDocumentDescriptor`](https://www.nutrient.io/api/ios/documentation/instant/instantdocumentdescriptor) as the object. This will always be the case the first time syncing a document descriptor after app launch.

When your app receives this callback, it should request a new JWT for the document descriptor from your server and pass this JWT to Instant to keep synchronizing annotations:

### SWIFT

```swift

let JWT = "new JWT for (documentDescriptor.identifier, documentDescriptor.layerName) from your server"
documentDescriptor.reauthenticate(withJWT: JWT)

```

### OBJECTIVE-C

```objc

NSString *JWT = "new JWT for (documentDescriptor.identifier, documentDescriptor.layerName) from your server";
[documentDescriptor reauthenticateWithJWT:JWT];

```

When it’s successful, the document descriptor will post a [`PSPDFInstantDidFinishReauthentication`](https://www.nutrient.io/api/ios/documentation/instant/foundation/nsnotification/name/pspdfinstantdidfinishreauthentication), and [`instantClient(_:documentDescriptor:didFinishReauthenticationWithJWT:)`](https://www.nutrient.io/api/ios/documentation/instant/instantclientdelegate/instantclient(_:documentdescriptor:didfinishreauthenticationwithjwt:)) will be called on your [`InstantClientDelegate`](https://www.nutrient.io/api/ios/documentation/instant/instantclientdelegate), letting you know it’s safe to persist the JWT (e.g. in the keychain). Should reauthentication fail, the document descriptor will post a [`PSPDFInstantDidFailReauthentication`](https://www.nutrient.io/api/ios/documentation/instant/foundation/nsnotification/name/pspdfinstantdidfailreauthentication), and [`instantClient(_:documentDescriptor:didFailReauthenticationWithError:)`](https://www.nutrient.io/api/ios/documentation/instant/instantclientdelegate/instantclient(_:documentdescriptor:didfailreauthenticationwitherror:)) will be called on your [`InstantClientDelegate`](https://www.nutrient.io/api/ios/documentation/instant/instantclientdelegate) instead.

### Losing access to a layer

Your app might find out that a user no longer has access to a layer from your server or from Document Engine. In that case, you’ll probably want to stop showing the document and clear its local storage:

### SWIFT

```swift

do {
    try documentDescriptor.removeLocalStorage()
} catch {
    // Errors are unlikely, but it is possible the delete from the file system fails.
}

```

### OBJECTIVE-C

```objc

NSError *error;

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

```

Because all document descriptors with the same `identifier` share the same PDF file, [`InstantDocumentDescriptor.removeLocalStorage()``](https://www.nutrient.io/api/ios/documentation/instant/instantdocumentdescriptor/removelocalstorage()) only removes the annotation data for the layer with the name stored in the document descriptor’s`layerName`property. To get rid of the PDF file for a given`identifier`, along with all associated layer data, use [`InstantClient.removeLocalStorage(forDocumentIdentifier:)``](/api/ios/documentation/instant/instantclient/removelocalstorage\(fordocumentidentifier:\)) instead. If you simply want to purge all PDF files for which no layer data is available, use [InstantClient.removeUnreferencedCacheEntries()`](/api/ios/documentation/instant/instantclient/removeunreferencedcacheentries\(\)).
---

## Related pages

- [File coordination on iOS](/guides/ios/features/file-coordination.md)
- [Open a PDF from in-memory data on iOS](/guides/ios/open-a-document/from-in-memory-data.md)
- [Open a PDF from a custom data provider on iOS](/guides/ios/features/data-providers.md)
- [Open a local file on iOS](/guides/ios/open-a-document/from-local-storage.md)
- [Troubleshoot opening a document](/guides/ios/open-a-document/troubleshooting.md)
- [Document Downloads](/guides/ios/miscellaneous/document-downloads.md)
- [Open password-protected PDFs on iOS](/guides/ios/open-a-document/password-protected-pdfs.md)
- [Open a PDF on iOS](/guides/ios/open-a-document.md)

