Building Plugins
Building NativeScript plugins is a great way to learn more about how NativeScript works, to create functionality that you can share across applications, and to leverage some really powerful functionality - such as the ability to use native iOS and Android frameworks.
Let's start by looking at the basics of how to structure a NativeScript plugin, and then move on to look at how you can generate that structure using the NativeScript plugin seed.
Plugin Basics
At their basic level NativeScript plugins are simple JavaScript modules that use well established npm conventions. For example, here's what the world's simplest NativeScript plugin looks like.
nativescript-hello-world/
└── src
├── index.js
└── package.json
IMPORTANT: Putting your source in a sub-folder is required for local LiveSync debugging.
Older plugins should be updated to move their source code in to a sub-folder.
And here is the simplest possible implementation of that plugin.
// index.js
module.exports = {
helloWorld: function() {
console.log("Hello World");
}
}
/* package.json */
{
"name": "nativescript-hello-world",
"version": "1.0.0",
"nativescript": {
"platforms": {
"ios": "3.0.0",
"android": "3.0.0"
}
}
}
There are a few things to note in this implementation.
- NativeScript uses the CommonJS format for defining JavaScript modules. In practical terms that just means you need to know the syntax for importing and exporting functionality (
require
,export
, andmodule.exports
). It's pretty straightforward, and you'll see several examples throughout this guide. - NativeScript plugins must have a "nativescript" key in their
package.json
file that specifies the minimum version of the iOS and Android runtimes that the plugin supports. Don't worry about this at the moment, other than to note that it's there.
TIP: Other than the
"nativescript"
key, everything about your plugin'spackage.json
file will be identical to any other npm package. Therefore, the npm docs about thepackage.json
file are a great reference when tinkering with your NativeScript plugin's metadata during development.
To use the above plugin all you need to do is install it in one of your apps.
tns plugin add /path/to/nativescript-hello-world/src
TIP: The
tns plugin add
command lets you install plugins from non-npm locations, such as GitHub repos, local folders, or .tgz archives. For details, runtns plugin add --help
from your command line.
With the plugin installed, you can use the CommonJS require()
method to import your plugin and use its helloWorld()
method.
var helloWorldModule = require("nativescript-hello-world");
helloWorldModule.helloWorld();
And with that you have a functional, albeit extremely basic, NativeScript plugin.
Most plugins need to do much more than log a simple string. To build a robust plugin you need some conventions for building, testing, and distributing the plugin you're building. And that's where the official NativeScript plugin seed comes in.
The NativeScript Plugin Seed
The NativeScript plugin seed is a cloneable GitHub repository that makes starting, building, and maintaining NativeScript plugins easy.
Let's look at how the plugin seed works, and in the process build a simple plugin that can retrieve an app's version number on both Android and iOS.
NOTE: Although we recommend using the NativeScript plugin seed, you do have the ability to build your NativeScript plugin from scratch. If you're interested in creating your own plugin development workflow, refer to the NativeScript plugin reference for details on exactly how NativeScript plugins are structured.
Step 1: Start Your Plugin
Because the NativeScript plugin seed is a public and open source repository, you'll start your plugin by running git clone
. The command below clones the seed into a new folder called "nativescript-version-number".
git clone https://github.com/NativeScript/nativescript-plugin-seed nativescript-version-number
TIP:
- The
git clone
command takes an optional<directory>
argument, and you can use it to change the folder name of any repository that you clone. The above command uses this to clone a repository named "nativescript-plugin-seed" into a folder named "nativescript-version-number".- By convention, NativeScript plugins use a naming convention of nativescript-name-of-plugin, which is why this plugin uses the name "nativescript-version-number" instead of something like "nativescript-VersionNumber" or "NativeScriptVersionNumber". Sticking to this naming convention allows developers to find your plugin easily.
You'll learn about the file structure of your plugin momentarily, but first there's one last setup script you need to run. After your git clone
command finishes, cd
into your new plugin's src
folder, and then run the npm postclone
script.
cd nativescript-version-number/src
npm run postclone
The postclone
script will ask you a few questions, such as your GitHub username, your plugin's name, and whether you'd like to set up a new git repository for your plugin (You probably want to, as otherwise your repo will start with the source control history of the NativeScript plugin seed itself).
After the postclone
script completes, your plugin should have a folder structure that looks looks this.
nativescript-version-number/
├── demo
├── publish
└── src
Here are what these folders do.
-
demo
: Contains a pre-built NativeScript demo app that you'll use to test your plugin in action. -
publish
: Contains shell scripts that will help you publish your plugin to npm. We'll come back to this later on. -
src
: Contains your plugin's source code.
During development, the NativeScript plugin seed allows you to work on your plugin's source code in your src
folder, and have the plugin update live in a demo app that lives in your demo
folder. Let's look at how to set up that workflow.
Step 2: Set Up a Development Workflow
In short, the process can be summarized with the following steps:
1. Run the demo app (which references the plugin), e.g. using npm run demo ios|android
.
2. Update the plugin code and see the result in the refreshed app.
Let's run your plugin in the demo app so you can see your plugin in action. Start by opening a new terminal window or command prompt on your development machine. Next, run either npm run demo.ios
or npm run demo.android
, to start up the demo app on iOS or Android.
# Pick one of these commands and run it while still in the src folder.
npm run demo.ios
npm run demo.android
If all went well, you should see the demo app start up and show one of the following screens.
To show how the development process works, next, open your plugin's src/version-number.common.ts
file, find the line of code that contains the "Your plugin is working" string, and make a small change. For example you could change the entire line of code that sets the msg
variable to the following.
let msg = `Your plugin is working on ${app.android ? 'Android' : 'iOS'}!`;
After you save this change a few things are going to happen. Next, your demo
command (tns run
) will detect the change and automatically refresh your demo app to show your string update.
NOTE: Wondering how this works? The demo application's
package.json
file is set to reference the plugin's source code in thesrc
folder directly. This link allows you to edit files insrc
, and see those changes in your demo immediately.
Pretty cool, huh? With this workflow you have the ability to develop NativeScript plugins just like they're any other files in your NativeScript apps.
Now that you have a workflow in place, let's take a step back and look at the files in src
in detail, and discuss how you can alter the plugin's default structure to meet your needs.
Step 3: Write Your Plugin
Your plugin's src
folder is where you'll work on writing your plugin. There are a number of files in this folder, but let's start with the .ts
files as they're where you'll spend the majority of your time.
.
├── version-number.android.ts
├── version-number.common.ts
└── version-number.ios.ts
NOTE:
- Each
.ts
file will have have a generated.js
file with the same name, but you can safely ignore those. If you're using Visual Studio Code you can add a bit of configuration to hide generated.js
files in your editor.- Each
.ts
file also has a generated.d.ts
file. Don't worry about these files as the NativeScript CLI takes care of creating these files for you. The only.d.ts
file you need to worry about issrc/index.d.ts
, and you'll learn about that file momentarily.
The .android.ts
file is where you put the Android implementation of your plugin; the .ios.ts
file is where you put the iOS implementation of your plugin; and the .common.ts
file is an optional file where you can put any code you intend to share on both platforms. The shared code in the .common.ts
file must be referenced in your platform specific .android.ts
and .ios.ts
files. For example in version-number.ios.ts
you will see:
import { Common } from './version-number.common';
To get an idea of what all this looks like in action let's implement a basic version of the version number plugin. Start by opening your src/version-number.common.ts
file deleting all of the starting code, as you'll start with a simple plugin that doesn't share logic across iOS or Android.
Next, open your version-number.ios.ts
file and paste in the following code.
export class VersionNumber {
get() {
var version = NSBundle.mainBundle.objectForInfoDictionaryKey("CFBundleShortVersionString");
return version;
}
}
The NativeScript plugin seed automatically sets up the necessary TypeScript configuration for working with native iOS and Android APIs, so as you develop your plugins you'll get a little help working with these native APIs in TypeScript-friendly editors like Visual Studio Code. For example, if you start to type out NSBundle
, Visual Studio Code helps you find the native iOS APIs that are available.
TIP: If you're new to working with native APIs in NativeScript, check out our documentation on accessing native APIs in JavaScript and TypeScript. You might also want to dive into a few existing plugins' source code just to see how they work. Almost every plugin on the NativeScript marketplace is open source.
There are still a few more changes you need to make before your plugin is ready to test. Next, open your src/version-number.android.ts
file and paste in the following code:
import * as application from "tns-core-modules/application";
export class VersionNumber {
get() {
var PackageManager = android.content.pm.PackageManager;
var pkg = application.android.context.getPackageManager().getPackageInfo(application.android.context.getPackageName(),
PackageManager.GET_META_DATA);
return pkg.versionName;
}
}
With this you have a functional plugin implementation for both iOS and Android, but you still have one minor configuration change to make.
Open your src/index.d.ts
file and paste in the following code.
export declare class VersionNumber {
get(): string;
}
The index.d.ts
file serves two purposes: first, it serves as a contract for any application that uses this plugin (you'll see this in a minute when you switch your demo to use this new API). Second, the .d.ts
file is what enables intellisense or code completion features in TypeScript-friendly editors.
The NativeScript CLI does not generate your index.d.ts
file, but in most situations you can copy the contents of your .android.d.ts
file or your .ios.d.ts
file into your index.d.ts
file. (The only time you wouldn't want to do that is if your plugin had Android- or iOS-specific APIs that you wanted to expose.)
TIP For more detailed information on writing declaration files refer to TypeScript's documentation on the topic.
With that, your plugin is completely functional and can retrieve your app's version number on both iOS and Android. To test this out, head back to your demo app, open your demo/app/main-view-model.ts
file, find the line of code that sets this.message
, and change it to use the following line of code.
this.message = this.versionNumber.get();
If you still have npm run demo.ios|android
running, you should see your demo app update to show your app's version number on the screen. (If not, refer back to step 2 and refamiliarize yourself with the plugin development workflow.)
Now that you have a complete plugin, you're ready to use your plugin in your apps, and to do that you'll need to publish it.
Step 4: Publish Your Plugin
WARNING The publish script of the NativeScript plugin seed requires that you use a bash-enabled terminal or command prompt. If you're on Windows, you can install GIT SCM and use Git Bash to run these scripts.
Your NativeScript plugin is currently a collection of TypeScript files in a src
folder. The NativeScript plugin seed provides a series of scripts that can build those files into a distributable npm package.
There are two different scripts that you can run depending on whether you just want to build a plugin package, or whether you want to additionally register that plugin on the npm registry.
Both scripts are in the publish
folder in your plugin's root folder, so start by using the cd
command to navigate into that folder:
cd ../publish
Next, if you just want to create a package, execute the pack.sh
script using the following command:
./pack.sh
The pack command will build your plugin, and place the built archive in your plugin's publish/package
folder, for example publish/package/nativescript-version-number-1.0.0.tgz
. You can then take that plugin package and install it in other NativeScript apps using the tns plugin add
command:
tns plugin add nativescript-version-number-1.0.0.tgz
If you want to publish your new plugin in npm, there are a few additional steps you need to take. First, open your plugin's src/package.json
file.
Your plugin's package.json
contains the metadata npm will display about your plugin, so you'll want to make sure that the information listed in this file is correct. Specifically, make sure the "description"
and "author"
fields have appropriate values, and also that your "version"
contains the version number you want npm to use. (You'll need to increment that "version"
with each subsequent release of your plugin.)
Next, open the README.md
file in the root of your plugin. The NativeScript plugin seed generates a basic README with a simple outline, but you'll want to replace this file with more complete documentation before you publish your plugin for the world to see. If you're not sure what to put here take a look at what other plugins do. The version number plugin has a dead-simple README you can refer to, and plugins like the fingerprint auth plugin or MapBox plugin provide more complex README patterns you may want to copy from.
Once you're all set with your package.json
configuration, as well as your README.md
documentation, return to the publish
folder or your plugin and run the publish.sh
script.
./publish.sh
The publish command runs through the same build process as the pack command, but after the build completes the command will additionally push your plugin to the npm registry.
NOTE: For the publish command to work you need to be logged into the
npm
CLI usingnpm adduser
ornpm login
. For details on how to use these commands see the npm documentation on the topic.
And that's it! You have now built a simple plugin, set up a robust development workflow, and got everything ready to share your plugin with the world.
And these are just the basics. The plugin seed supports more advanced workflows for your plugin development. You may be interested in trying out:
If you run into any problems during your plugin development, reach out on Stack Overflow. And if you'd like to chat with other NativeScript plugin authors, sign up for the NativeScript slack and join us in the #plugins channel.