2016-08-18

Last week, Max Lynch from Ionic wrote an excellent guide on how to write Cordova plugins, detailing the specific steps you need to take to build and distribute a plugin in the Cordova ecosystem. Here at Progress the article really resonated with us, because through our Telerik background we have a lot of experience in the Cordova plugin world. We maintain a large collection of verified plugins at plugins.telerik.com; we sell premium tooling for building and testing Cordova apps; and we regularly speak about our plugin experiences at places like PhoneGap Day.

Because of this background we know that Cordova plugins are powerful, but we also know that these same Cordova plugins can be difficult to create. You can get a sense of the complexity by quickly skimming through Max’s article; you’ll see a lot of words like “obscure” and “esoteric”, as well as a decent amount of ceremony to build a plugin that logs a string and outputs a date.

The difficultly of accessing native APIs in Cordova is one of the reasons we built NativeScript, and why designed NativeScript to give you direct access to iOS and Android APIs in your JavaScript or TypeScript code. This direct access negates the entire need to write a plugin to access features of the underlying native platforms. For instance, you can recreate the entirety of Max’s Android getDate() call with one line of code in your NativeScript app.

Pretty cool, huh?

If you’re curious how this JavaScript code is able to allocate a Java Date object and invoke its toString() method, feel free to read more about how NativeScript works.

But even though you can access these APIs directly, we also realize there’s a lot of power in abstracting these native API calls behind far easier-to-use JavaScript APIs. I think most developers would prefer to write getDate() rather than deal with classes like java.util.Date or NSDateFormatter and NSLocale. That’s why we spent a lot of optimizing our NativeScript plugin model. Our goal was to give developers the ability to quickly incorporate native code without writing native code, and to also greatly reduce the barrier to entry for plugin authors.

To show how this works in action, I’m going to go through the same steps Max did in his article, but I’ll be building a NativeScript plugin instead of a Cordova one. Max creates a plugin with an echo() and getDate() method that runs on iOS and Android, and therefore I will do the same in this article. By comparing NativeScript and Cordova plugin authoring side by side, hopefully you’ll see just how much easier NativeScript makes accessing native code.

It’s worth noting that just because we believe NativeScript has an easier-to-use plugin model, that doesn’t make NativeScript a “better” technology. The right technology choice depends on what you’re building, and it always makes sense to evaluate your options before committing to a framework for a long-term endeavor. We at Progress love Cordova, and continue to build a lot of tooling based on the Cordova architecture. If you’re looking for more thorough information on choosing between NativeScript & Cordova, check out a chat we had with our Telerik Developer Experts on the topic last month.

1. Getting started: scaffolding our first plugin

NativeScript plugins were designed to be simple. Although there are more fully-featured community-written plugin generators to scaffold more advanced plugins, the simplest NativeScript plugins are just three files. Here’s what the world’s simplest NativeScript plugin looks like. You can create these as blank files for now, and we’ll look at what goes into these files as we complete this guide.

2. Configuring our Plugin

Cordova uses a plugin.xml file for configuration, and in NativeScript we use the package.json file you see above. NativeScript plugins are distributed through npm, so if you’ve used npm before in another JavaScript project, you already know most of what you need to know to configure a NativeScript plugin. And if not, you can refer to npm’s extensive documentation on the topic, and reuse that knowledge when developing libraries for other JavaScript-based apps.

To give you an idea of what a NativeScript plugin’s package.json looks like, here’s the full configuration for the NativeScript flashlight plugin.

The only thing in this package.json that’s unique to NativeScript is the "nativescript" object that starts on line 5. This object tells the NativeScript CLI the minimum NativeScript runtime version required to run this plugin — in this case, version 1.1.0 of both the iOS and Android runtimes. For the most part you can just set this object to the latest NativeScript version and forget about it, but if a breaking change in NativeScript breaks your plugin, this object gives you the ability to specify exactly what versions of NativeScript your plugin supports.

Personally, I start new NativeScript plugins by copying the flashlight plugin’s package.json contents verbatim, changing the small handful of names and descriptions, and calling my configuration complete.

3. Building our plugin: JavaScript

In NativeScript writing your JavaScript plugin involves filling out those index.android.js and index.ios.js files you see above. Unlike Cordova, you don’t have to code to any framework-specific APIs to write a plugin; there’s no equivalent of Cordova’s exec() function, for instance.

In fact, you don’t even have to provide separate index.js files for Android and iOS. The .android.js and .ios.js naming convention is a convenience NativeScript provides to separate your Android and iOS code, but you could choose to put all of your code in a single index.js file if you choose.

For this example we’ll use separate files for iOS and Android, and therefore we’ll put the following code in both our index.android.js and index.ios.js files to define the plugin API.

In NativeScript we use the same CommonJS specification that Node.js uses, so this file is going to look identical to a JavaScript module you’d write for that environment. It’s worth noting that we also support TypeScript as a first class citizen, and if you were to write this plugin in TypeScript it’d look something like this:

Regardless of the approach you use, you can again see that nothing you’re doing here is proprietary to NativeScript. The code and techniques you’re writing is totally reusable in Node, as well as other JavaScript-based environments.

4. Building our plugin: Native iOS

Much like Cordova, writing native code is the hardest part of building a NativeScript plugin. The good news is in NativeScript you don’t have to write a single line of Objective-C, Swift, or Java. All of the platform APIs are available to you in JavaScript directly, so you can just use them, and it’ll save you a whole lot of code. For example, here’s the full iOS implementation of Max’s Cordova plugin, which includes an interface file and an implementation file (per Objective-C conventions).

MyCordovaPlugin.h

MyCordovaPlugin.m

And here’s what that same implementation looks like in NativeScript:

Note: Why console.log instead of NSLlog? Because NSLog is a variadic function, you would normally have to take one extra step to use that API in NativeScript (see the documentation) However, NativeScript’s global console object is implemented with the same NSLog API, so you get the same result using it.

Personally, I think iOS APIs like NSDateFormatter and NSLocale look arcane regardless of how I write them, but in NativeScript I appreciate two things:

I get to drop a whole lot of Cordova and Objective-C boilerplate code.

I get to stay in a language where I understand the syntax and semantics.

There is a bit of a learning curve for figuring out how to transfer Objective-C and Swift code into NativeScript JavaScript code, but in most cases the transfer is relatively straightforward.

The biggest thing you have to learn is the unique, and quite frankly bizarre, syntax Objective-C uses for method calls.

Yep, that’s a method call. Who knew? In NativeScript since you’re writing JavaScript, you have to switch this code to use the JavaScript syntax for method invocation, or NSDate.date().

The other big thing to know is how to transfer “set” method calls from Objective-C to NativeScript. For example consider the code below.

This code calls the dateFormatter object’s setDateFormat() method, and passes that method an argument of "yyyy-MM-dd'T'HH:mm:ssZZZZZ". In NativeScript this setter becomes a more straightforward property assignment using JavaScript semantics.

These conventions can take some time to get used to, but once you do, I think you’ll find writing in NativeScript a lot easier than writing in Objective-C or Java.

And there are some massive advantages to writing all your code in one language. To start, you can write your code in the editor you’re already comfortable with, so there’s no need to hop between Xcode, Android Studio, and your editor of choice. You can also reuse JavaScript or TypeScript linters and style checkers.

One other advantage is that if you choose to write your plugin in TypeScript, which we recommend for non-trivial plugins, we provide complete TypeScript declaration files for both iOS and Android. So you can get Xcode-like features like syntax checking and code complete, without actually having to use Xcode. The same is true with Android and Android Studio.

5. Building our Android plugin

Much like Cordova, building the Android portion of a plugin is conceptually a lot like building the iOS portion. In NativeScript, building the Android version of a plugin is a bit easier because Java as a language is syntactically closer to JavaScript than Objective-C. For example, here’s the full source of Max’s Android implementation.

And here’s what the same functionality looks like in NativeScript:

As you can see, the difference in the amount of code you have to write is drastic. With NativeScript there are no Cordova APIs to write to, and no Java boilerplate to worry about; you just write the APIs you need. The one tip to keep in mind is that in NativeScript you do need to fully qualify any Java APIs that you intend to use. In this case new java.util.Date() invokes a new Java Date object, and toString() invokes that object’s toString() method.

6. Testing plugins

Cordova has a cordova plugin add command, and in NativeScript we have a tns plugin add command. If you’re testing a plugin locally you can point the command directly at the source:

And once you publish your plugin to npm, you can call the exact same command, but pass it the npm module name instead of a path. For instance, the following command installs the NativeScript flashlight plugin.

That’s really all there is to it. The great thing about working in NativeScript is, unlike Cordova, you can start by just writing your plugin as a function inside one of your existing apps. If you think that function would be useful in other projects, you can abstract that function into its own file, and perhaps even publish it to npm so everyone can benefit from the abstraction you created.

7. Where to go from here

There’s a whole lot more you can do with NativeScript plugins. We haven’t even touched on the fact that you can easily import native libraries such as CocoaPods for iOS and package them directly within your plugin. Or that you can build plugins for user interface components.

If you’re interested in building a NativeScript plugin, your first step is to learn a bit about NativeScript itself first. Start by going through one of our getting started guides to learn the basics of what NativeScript is and how to build apps with the framework.

Get Started with NativeScript and JavaScript

Get Started with NativeScript, TypeScript, and Angular 2

When you’re ready to write a plugin, start by looking at a few existing plugins that exist on npm. It’s a good idea to start with a simple plugin such as flashlight while you’re getting up and running. As you get more advanced, you may want to use one of our community-written plugin generators, which can generate plugins with fully-functional demo projects, TypeScript support, and more.

Nathanael’s Anderson’s NativeScript plugin generator

Nathan Walker’s NativeScript plugin seed

If you need help during the plugin process, feel free to ask any questions you have on Stack Overflow with the “NativeScript” tag, or reach out to us in the NativeScript Community Slack Team. We even have a #plugins channel dedicated to plugin authoring.

Best of luck, and happy NativeScript-ing!

Header image courtesy of Samuel M. Livingston

The post How to Write NativeScript Plugins and Why They’re Easier Than Cordova Plugins appeared first on Telerik Developer Network.

Show more