Blog Post

Strategies for Saving and Syncing Annotations Using External Files

Illustration: Strategies for Saving and Syncing Annotations Using External Files
Information

This article was first published in July 2019 and was updated in July 2024.

In addition to saving annotations embedded into a PDF, PSPDFKit for iOS also allows you to use external files to store annotations. This can be useful if your app is syncing annotations to a server.

By saving annotations in an external file, you’ll only have to sync the external file to your backend as opposed to having to transfer the entire PDF.

This article will explore the various strategies you can use to save and sync annotations using external file formats like JSON or XML.

Instant JSON

Instant JSON is our approach to bringing annotations into a modern format. It allows you to store PDF changes, such as annotations, in a separate JSON file.

What this means is that a PDF document will only need to be transferred once, and all changes will be added to the existing PDF as an overlay. This approach significantly reduces the bandwidth, since you only need to transfer the JSON file and not the entire PDF.

Instant JSON allows you to import and export individual annotations, as well as changes for an entire document, in a single API call.

Read on to learn how you can accomplish this.

Instant Annotation JSON API

The Instant Annotation JSON API allows you to represent an Annotation as a JSON payload. Here’s how you can export an annotation to a JSON file:

let annotation: Annotation = ...

// The annotation's Instant JSON data can be saved to an external file as `Data`.
let instantJSONData = try annotation.generateInstantJSON()

// Upload the data to your server.

After syncing is complete, you can import the annotation from JSON on a different device, like so:

// Load the document.
let document: Document = ...
let loadedInstantJSONData: Data = ... // The Instant JSON data loaded from your server.
let documentProvider = document.documentProviders[0]

// Create an annotation from Instant JSON data and add the annotation to the document.
let annotation = try documentProvider.addAnnotation(fromInstantJSON: loadedInstantJSONData)

Instant Document JSON API

The Instant Document JSON is a serializable representation of the current changes to a document, which is the diff between the Document’s saved and unsaved changes. This can be used to transfer a set of changes across devices without having to send the entire PDF, which could potentially be very large.

Here’s how to generate an Instant JSON payload for documents:

let document: Document = ...

// The Document JSON can be saved to an external file as `Data`.
let data = try document.generateInstantJSON(from: document.documentProviders[0])

// Upload the data to your server.

This generated JSON can be saved to an external file, uploaded to a server, downloaded to a different device, and then applied to the document using applyInstantJSON(fromDataProvider:to:lenient:), like this:

let document: Document = ...

// The Document JSON data from the external file.
let data: Data = ...

// Create a data container provider.
let jsonContainer = DataContainerProvider(data: data)

// Apply the Instant Document JSON.
try document.applyInstantJSON(fromDataProvider: jsonContainer, to: document.documentProviders[0], lenient: false)

For more details, refer to our Instant JSON guide and the examples from InstantJSONExamples.swift in our Catalog app.

Internally, we use the Instant JSON format in PSPDFKit Instant, our solution for real-time collaboration and synchronization. You can see Instant in action here.

XFDF

You can also use the XFDF format to save and sync your annotations in an external XML file. XFDF is an XML-like standard from Adobe XFDF for encoding annotations and form field values. It’s compatible with Adobe Acrobat and PSPDFKit.

PSPDFKit for iOS supports both reading and writing XFDF, and it also offers an annotation provider subclass — XFDFAnnotationProvider — that will load and save annotations from and to an XFDF file automatically.

Using an XFDF Annotation Provider

The XFDF annotation provider uses XFDFParser and XFDFWriter internally and ensures the best performance.

You can use XFDFAnnotationProvider to set up an XFDF annotation provider for a document, which will ensure that all annotation changes will be saved into the XFDF file. This can be done like so:

// Load from an example XFDF file.
let externalAnnotationsFile = URL(fileURLWithPath: "path/to/XFDF.xfdf")

// Create the document and set up the XFDF provider.
let document: Document = ...
document.annotationSaveMode = .externalFile
document.didCreateDocumentProviderBlock = { documentProvider in
	let XFDFProvider = XFDFAnnotationProvider(documentProvider: documentProvider, fileURL: externalAnnotationsFile)
	documentProvider.annotationManager.annotationProviders = [XFDFProvider, documentProvider.annotationManager.fileAnnotationProvider!]
}

Refer to our XFDF guide for more information and the XFDFAnnotationProviderExample in the Catalog app for a runnable sample project.

Information

PSPDFKit for iOS also allows you to use an encrypted XFDF file in your XFDF annotation provider. For sample code, refer to EncryptedXFDFAnnotationProviderExample from the Catalog app.

Importing from an XFDF File

You can also import annotations from an XFDF file to a document using XFDFParser, like so:

// Load from an example XFDF file.
let externalAnnotationsFile = URL(fileURLWithPath: "path/to/XFDF.xfdf")

let dataProvider = FileDataProvider(fileURL: externalAnnotationsFile)
let documentProvider = document.documentProviders[0]

// Create the XFDF parser and parse all annotations.
let parser = XFDFParser(dataProvider: dataProvider, documentProvider: documentProvider)
let annotations = try parser.parse()

// Add the parsed annotations to the document.
document.add(annotations: annotations)

Exporting to an XFDF File

You can export annotations from a document to an XFDF file using XFDFWriter, as seen below:

// Collect all existing annotations from the document.
let annotations = document.allAnnotations(of: .all).values.flatMap { $0 }

// Write the file.
let dataSink = try FileDataSink(fileURL: externalAnnotationsFile)
try XFDFWriter().write(annotations, to: dataSink, documentProvider: document.documentProviders[0])

Custom Annotation Provider

If you’re looking to roll your own custom annotation provider to save annotations in a different or custom format, you can implement the AnnotationProvider protocol in a subclass of PDFContainerAnnotationProvider to achieve customized annotation saving and loading.

This is an advanced method, and we recommend using Instant JSON, PSPDFKit Instant, or XFDF instead. However, if you have a very specific use case, take a look at the CustomAnnotationProviderExample from our Catalog app.

Conclusion

This post provided you with an overview of how to load and save annotations from and to external files to avoid having to transfer an annotated PDF to all syncing devices. We recommend checking out the runnable Instant JSON, Instant, and XFDF examples from our Catalog sample app so that you can decide which approach satisfies your app’s requirements.

Share Post
Free 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
PRODUCTS  |  iOS • Releases

PSPDFKit 13.8 for iOS Brings SwiftUI API to the Main Toolbar

DEVELOPMENT  |  iOS • Xcode • Insights

Investigating a Dynamic Linking Crash with Xcode 16

DEVELOPMENT  |  iOS • Android • Room • Kotlin Multiplatform • Tips

Seamless Room Database Integration for Kotlin Multiplatform Projects