How to Expose Native iOS APIs to Cordova

This is a secret URL preview of an unreleased article.

We recently updated our Cordova plugin to add the annotation manipulation API, and today we are excited to announce that we have further extended the APIs of our Cordova plugin to add the document processing API.

In our native SDKs, we expose a lot of APIs for full customization, but we only use a subset of those APIs in our Cordova plugin. In this article, we will show you how to bring native iOS APIs to Cordova, which makes it easier for everyone to contribute to our open source repository or to expose native iOS code to Cordova in general.

In this tutorial, we’ll go through how we implemented the recently added processAnnotations API that allows you to export documents with embedded, flattened, or removed annotations.

So let’s get started!

Declaring the Cordova JavaScript API

First, we declare the JavaScript API in pspdfkit.js to make it available to Cordova.

We can achieve this by using the cordova.exec command, like so:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Document Processing
this.processAnnotations = function(
  annotationChange,
  processedDocumentPath,
  callback,
  annotationType
) {
  cordova.exec(
    function(success) {
      if (callback) callback(success, null);
    },
    function(error) {
      console.log(error);
      if (callback) callback(null, error);
    },
    "PSPDFKitPlugin",
    "processAnnotations",
    [annotationChange, processedDocumentPath, annotationType]
  );
};

We can also accomplish this by using our helper addMethods function, like so:

Copy
1
2
3
4
5
6
7
8
9
// Document Processing
addMethods({
  processAnnotations: [
    "annotationChange",
    "processedDocumentPath",
    "callback",
    "annotationType"
  ]
});

For more details, please take a look at the official Cordova JavaScript Interface guide.

Implementing the Objective-C Logic

Now that we’ve declared the JavaScript API in Cordova, we need to implement the functionality in Objective-C in PSPDFKitPlugin.m:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- (void)processAnnotations:(CDVInvokedUrlCommand *)command {
    PSPDFAnnotationChange change = (PSPDFAnnotationChange)[self optionsValueForKeys:@[[command argumentAtIndex:0]] ofType:@"PSPDFAnnotationChange" withDefault:PSPDFAnnotationChangeEmbed];
    NSURL *processedDocumentURL = [self writableFileURLWithPath:[command argumentAtIndex:1] override:YES copyIfNeeded:NO];

    // The annotation type is optional. We default to `All` if it's not specified.
    NSString *typeString = [command argumentAtIndex:2] ?: [command argumentAtIndex:3];
    PSPDFAnnotationType type = PSPDFAnnotationTypeAll;
    if (typeString.length > 0) {
        type = (PSPDFAnnotationType) [self optionsValueForKeys:@[typeString] ofType:@"PSPDFAnnotationType" withDefault:PSPDFAnnotationTypeAll];
    }

    PSPDFDocument *document = self.pdfController.document;
    VALIDATE_DOCUMENT(document)

    // Create a processor configuration with the current document.
    PSPDFProcessorConfiguration *configuration = [[PSPDFProcessorConfiguration alloc] initWithDocument:document];

    // Modify annotations.
    [configuration modifyAnnotationsOfTypes:type change:change];

    // Create the PDF processor and write the processed file.
    PSPDFProcessor *processor = [[PSPDFProcessor alloc] initWithConfiguration:configuration securityOptions:nil];
    NSError *error;
    BOOL success = [processor writeToFileURL:processedDocumentURL error:&error];
    if (success) {
        [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:success] callbackId:command.callbackId];
    }
    else {
        CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
                                                      messageAsDictionary:@{@"localizedDescription": error.localizedDescription, @"domain": error.domain}];
        [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
    }
}

Using the API in Cordova JavaScript Code

Now that we’ve exposed the API to process annotations to our Cordova plugin, here’s how we can use it in our app’s JavaScript code:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
// Flatten ink annotations from the current document.
PSPDFKitPlugin.processAnnotations(
  "flatten",
  "pdf/processed.pdf",
  function(success, error) {
    if (success) {
      console.log("Successfully processed annotation");
    } else {
      console.log("An error has occurred: " + error);
    }
  },
  "Ink"
);

That’s all!

Pull Request Contributions and Forking

If you come across any missing APIs that you think would be useful to have, feel free to submit pull requests to help improve our wrapper.

However, if your use case is very specific, and if making native APIs available in our official Cordova wrapper doesn’t make sense, you can fork the repository and implement the new API in your fork.

💡 Tip: PSPDFKit for iOS comes with several sample projects, like PSPDFCatalog, which is a great source of Objective-C code that you can use to bridge to Cordova.

Conclusion

We hope that this article will help you improve Cordova wrappers, be it the PSPDFKit one or other ones.

PSPDFKit for iOS

Download the free 60-day trial and add it to your app today.

iOS