Sync Annotations Using External Files

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
(opens in a new tab) 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
(opens in a new tab)’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:)
(opens in a new tab), 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(opens in a new tab).
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(opens in a new tab).
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(opens in a new tab) 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
(opens in a new tab) — that will load and save annotations from and to an XFDF file automatically.
Using an XFDF Annotation Provider
The XFDF annotation provider uses XFDFParser
(opens in a new tab) and XFDFWriter
(opens in a new tab) internally and ensures the best performance.
You can use XFDFAnnotationProvider
(opens in a new tab) 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 = .externalFiledocument.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(opens in a new tab) for a runnable sample project.
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(opens in a new tab).
Importing from an XFDF File
You can also import annotations from an XFDF file to a document using XFDFParser
(opens in a new tab), 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
(opens in a new tab), 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
(opens in a new tab) protocol in a subclass of PDFContainerAnnotationProvider
(opens in a new tab) 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(opens in a new tab).
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(opens in a new tab) so that you can decide which approach satisfies your app’s requirements.