Creating PDF Annotation Layers on iOS

An Instant layer is a container for annotations on a specific PDF document. For everyone with write access to this container, the layer provides an editing context for the annotations it contains. People who only have read access to a layer may still download and view the document with all the annotations in the container. However, they won’t be able to make any changes to those annotations.

Instant layers are particularly useful for implementing review workflows where several parties are invited to provide feedback on a document independent of one another. One example would be student-teacher workflows, where all students independently annotate the same PDF document, while the teacher can annotate each student’s private layer, which only the student can see.

Because all layers with the same document identifier can share the same PDF file, adding a layer is cheap in terms of storage and bandwidth — on the clients as well as on the server.


In Instant for iOS, we represent a layer using the InstantDocumentDescriptor protocol. Compared to earlier releases, the responsibilities and general semantics of that API remain untouched. However, the terms “document,” “document descriptor,” and “PDF file” are no longer used interchangeably. Instead:

  • A PDF file corresponds to the file for a certain document identifier.

  • There can be multiple layers for any document identifier, and each layer corresponds to exactly one document descriptor.

  • For display and editing purposes, you can obtain one or more Document instances (i.e. documents) from a document descriptor.

ℹ️ Note: To further clarify the distinction between document and document descriptor, the old delegate methods in InstantClientDelegate have been deprecated. Their replacements now all explicitly refer to a document descriptor in their names.

Instant Layers in Practice

When you upload a new PDF file to Document Engine, the server assigns a document identifier to that file. All annotations in that file are extracted into an immutable base layer. You can create custom layers for a document identifier by issuing JSON Web Tokens (JWTs) that contain the desired name for the layer in the (optional) layer claim, where each layer is uniquely identified by the combination of document identifier and layer name. A new layer mirrors the content of the base layer until the first change is made to its content, which means layers don’t have to be created manually, rather they’re created on demand once the first changes are synced.

If the layer claim is omitted in the JWT, the “default layer” for that document identifier is used. So if you’re already using Instant, all your annotations will be in this default layer, and you can continue using the default layer without having to change anything on your server or in your client code.

For more technical information about layers, take a look at our Instant Layers guide for Document Engine. To learn more about some of the design considerations and our vision for Instant layers, please read the Instant layers blog post.

Try Layers for Yourself

To see layers in action without having to touch your server setup, you can use our example applications:

  1. Follow the steps in our Example Projects guide for Document Engine, and log in to the example web app in your favorite browser as the user test. A fresh server will be prepopulated with a sample PDF called Document#1, and you can always upload additional PDFs using the Upload PDF button.

  2. Open a document by clicking on its thumbnail, and create a new text annotation with the content default layer on the first page — this will make it easy later on for you to see which layer you’re dealing with.

  3. Type test in the Create New Layer field (in the right pane) and click the Create Layer button.

  4. Create a new text annotation with the content layer 'test' on the first page — you can now switch back and forth between the default layer and the test layer by clicking on the entries of the Available Layers list in the right pane. If you want, you can create additional layers by typing their names in the text field and adding an annotation to the new layer.

Follow the steps in our Get Started guide for Instant to build and run the iOS example application. This is what allows you to see your existing layers on iOS. When running, the app will display a table view with one section for each PDF file you’ve uploaded to the example server containing one cell for each layer. After tapping one of the cells, the appropriate PDF file and annotation data will be downloaded as necessary. If you tap another cell in the same section, you’ll notice how much more quickly the second layer for that file is opened; because the file is shared, only the annotation data needed to be fetched.

Using Layers in Your Own App

To use non-default layers, your JWTs need to include the layer claim. To obtain the document descriptor for any layer, call the documentDescriptor(forJWT:) method on your InstantClient, passing in the JWT.

ℹ️ Note: Because you need a JWT to obtain a document descriptor for a custom layer that hasn’t yet been loaded, we suggest designing the API endpoint that lists all the layers a user has access to in such a way that the response includes the JWT for each layer. This also greatly reduces the number of server roundtrips needed to, for example, update all downloaded layers.

To make it quicker and easier to identify integration errors, updating the JWT for a document identifier no longer requires a server roundtrip to reject incompatible tokens. And if your JWT contains the user_id claim, we now also reject those mismatches immediately. This is especially useful in combination with the new localDocumentDescriptors() API.

For the complete list of changes, head over to our changelog.