Make your app extensible with JavaScript Core

Undoubtedly, productivity apps are major time-savers over the use of homemade scripts and hacks. Not having to reinvent the wheel every time and getting a much improved experience is where the value comes from for the users. However, even the best apps are limited by the set of features the developer has bundled into, and sometimes it doesn’t cover all the needs a user may have.

Craft plugin displaying an interface in Sketch

Some apps are inherently extensible by their use of a scripting language as core (e.g. Sublime with Python, Emacs with Lisp, Atom with JavaScript). It’s not the case of many native apps, which are compiled, code-signed and sometimes sandboxed to guarantee their integrity. Very few native Mac apps have succeeded to become truly extensible: Sketch is an amazing example, which has even succeeded to build a large ecosystem of plugins around it.

Since the early days, Paw has been sticking to the system as a native, code-signed and fully sandboxed app. But I realized the need of extensibility the day the Apiary team showed interest in an integration with their open description format API Blueprint. The idea of Paw importing 3rd party API descriptions to let users visually test their API calls and generate client code was extremely attractive. It was late 2014 and I was working alone on Paw. I knew I wouldn’t be able to implement every feature request, so I wanted to make it scriptable and tell the Apiary folks: “go ahead, you can build it yourself”. It was challenging. But in just a few weeks I’ve been able to give them a beta version. Soon after, Kyle Fuller, an Apiary guy and core CocoaPods maintainer, built the two first Paw extensions (one to import & one to export API Blueprint). It was an amazing feeling to see others actually build stuff for the app I was developing.

CocoaScript, Python or JavaScript?

There was a dilemma on the choice of the scripting language. Sketch exposes an CocoaScript interface that lets users mix JavaScript with a syntax inspired by Objective-C, and gives access to all available OS X APIs. This would have been a serious security concern for Paw being a place where developers may store their production server credentials: letting 3rd party have unrestricted access to the app didn’t feel right.

I had a personal preference for Python. The fully bidirectional PyObjC bindings would have made nice API for Paw, and would give powerful control to the developers. Though, it had the same security concerns as CocoaScript, and OS X sandboxing would have become a challenge to achieve.

JavaScript was the most interesting choice because of its popularity, and OS X bundles a full JavaScript runtime in the system, which is the same as used by Safari. Since OS X 10.9 / iOS 7, the JavaScript Core framework exposes great Objective-C to JavaScript bindings (it had a C API before). This meant objects from either language would be bridged to the other by just following a simple protocol and following a few rules for memory management. The whole thing would run in a JavaScript Virtual Machine we would fully control. We would create JavaScript APIs to let 3rd parties safely access the data we wanted to expose.

Exposing a public interface is great for your codebase

Giving access to the internals of Paw to 3rd party developers has been a great experience: it forces us to think bigger and build more reusable components of which we aren’t the only users anymore. It defines a clear contract between the application and the extensions, against which we’re building unit tests.

We’re probably the primary users of our own JavaScript API. Using it just after building it is nice to make sure it’s functional when used in real projects and is easy to understand for others. We’ve built many extensions for Paw, mostly to import and export data, and to generate client code. We don’t need to ship the app with the code necessary to read or write every possible file format. We keep our core code-base cleaner and the application’s binary smaller.

A JavaScript snippet running in Paw to generate a HMAC-SHA256 signature based on other inputs

Shipping updates

It’s always exciting when I see an app I love get an update. But too many
updates becomes bothersome, each requiring a new download and relaunch. It’s
where shipping features as extensions becomes great as it doesn’t require to
ship an update to fix a small issue in a component. We often improve the code
generators for Paw, and we’re currently refactoring all our importers/exporters:
everything will be shipped without updating the app’s binary.

Paw displays nonintrusive notifications for extension updates

The downside is if you’re building your own app, you’re going to have to implement your own extension update process. A typical Paw extension is compiled with Travis CI and pushed to a GitHub release. Our server only keeps the list of available extensions. When an update is needed, we just bump the Git tag number, and Travis and GitHub do the rest.

Open source

We would absolutely love to open source a significant chunk of Paw, but doing it right takes times, and time is the most valuable asset of a small team. Ultimately, we will do so by releasing some core components as beautiful libraries! Though, for extensions it was a no-brainer: we publish all our extensions on GitHub, users can help us improve them, and they use them as templates for new extensions.

It really worked! We’ve recently noticed a promising increase in the number of users asking questions about building extensions. People want to do more with Paw, make their workflow smoother, and use the app in scenarios we haven’t integrated yet.

Integrating JavaScript Core in a Core Data-backed Cocoa app

When you get to implement your extension mechanism, the JSContext object is the first thing you’ll need to know about. It’s the execution context of your JavaScript code, a single-threaded object that holds all the references to *JSValues *you have created either in your Objective-C or JavaScript code. Let’s see a simple example:

You see here that JSValue encapsulates the return value of the function. Effectively, a JSValue can be anything: string, number, boolean, dictionary, array, date … even a function or an object — and it’s where it becomes interesting! The framework exposes an Objective-C protocol JSExport that enables classes that implements it be easily bridged to JavaScript.

Here, we’re allowing any instance of the Objective-C class LMRequest to be passed to JavaScript as an object that will expose the three methods we defined: setHeader, setUrl, setBody. This means whenever a JavaScript script calls request.setHeader(name,value) our Objective-C method will be invoked behind the scenes.

Now, let’s see how this integrates with a multithreaded Core Data environment! Thanks to the use of background threads, in which all the heavy computations happen, including the JavaScript execution, Paw is faster than other API testing tools and has a smoother user interface. Core Data is really nice at making background processing safe and easy: fire up a new private queue context and run your code asynchronously in it. Make sure your JSContext and
all JSValues that belong to it are only accessed from that queue.

Here’s a simplified snippet of how Paw runs an importer written in JavaScript:

Our app is full of Core Data object classes—effectively NSManagedObject subclasses — that are also implementing the JSExport protocol and it works like a charm! Users writing little JavaScript snippets to integrate a new feature in Paw are unaware that most of the functions and properties they call are effectively resulting in Core Data fetches and mutations. They don’t need to think of contexts and multithreading either.

Probably the most common use of the JavaScript Core is when linked to a WebView so one can interact with the webpage’s main JSContext. The approach we had at Paw was slightly different, using JavaScript as a pure interpreter detached from any web components. You’ll find more articles about the JavaScript Core framework in general, including one from NSHipster.

This little preamble to extensibility for OS X and iOS apps wasn’t thought to be a complete tech tutorial on how to build apps using JavaScript Core, but will hopefully be insightful for your next developments.

Originally posted on Medium / iOS App Development