The NativeScript 1.5 release introduced first-class support for languages which
transpile to JavaScript. In plain English, if your language of choice generates JavaScript, it can now be used to create NativeScript apps. The NativeScript CLI has integrated support for TypeScript, Babel, and CoffeeScript (
here’s the full list of languages we support), but if you want to use another language, just create a plugin following the template of the existing
Babel one.
The upcoming ES7 language edition has the
async/await feature which makes the UI and networking code a breeze to write and support. I was very eager to experiment with async code in a NativeScript app, and decided to try it by implementing a simple app. I decided to mirror the
cuteness sample. But my project was going for something more awesome, and that's why it follows
/r/hardcoreaww sub-reddit instead.
I've started with a fresh project:
tns create hardcoreaww && cd hardcoreaww
Then I added support for Babel and Android platform:
tns install babel && tns platform add android
To set up my development environment, I started an Android emulator and my code editor. I am using the awesome Visual Studio Code to write this blog post.
Then, I needed to set up Babel. First of all, Babel requires a
runtime to work.
I used
npm install babel-polyfill --save
to add it to my project and then "require" it in my app.js:
"use strict"
;
require(
"babel-polyfill"
);
As I wanted to use the async keyword, I needed to load the appropriate Babel plugins. To do so, I created a .babelrc file at the root of my project with this content:
{
"presets"
: [
"stage-3"
,
"es2015"
]
}
The presets are discussed here. Please consult Babel's docs for other possible options.
As these components are only needed while developing the app, I installed them as "dev dependencies" using:
npm install babel-preset-stage-3 -D
npm install babel-preset-es2015 -D
Then I started the LiveSync process:
tns livesync --watch
and checked that the initial app from the template works in the emulator. I was ready to start hacking!
As I developed my app, I discovered some gotchas. It turned out that the "application" module from version 1.5 cannot be imported using the ES6 import syntax.
Therefore, this syntax
import * as application from
"application"
;
is not yet applicable. We will try
to lift this limitation for the upcoming 1.6 release. In the meantime, I am using
let application = require(
"application"
);
It gets the job done for the time being. As I was writing my code, I was able to use different ES6 constructs, like named and default exports, for example:
export
default
class AppViewModel extends Observable { ....
which I used later as
import AppViewModel from
"./reddit-app-view-model"
;
This code also uses ES6 classes with constructors and inheritance. I also used `let` and `const` everywhere, so that the compiler can check my code for dangerous behavior.
But this was not enough. I wanted to taste the simplicity and effectiveness of the ES7 async construct. The perfect opportunity presented itself when I was fetching the data out of reddit.
The code turned out to be as succinct and linear as the ads suggested:
try
{
const url = redditUrl + args.count + (after ?
"&after="
+ after :
""
);
const response = await fetch(url);
const result = await response.json();
const itemsToLoad = result.data.children.map(i =>
new
RedditViewModel(i.data));
this
._redditItems.load(args.index, itemsToLoad);
}
catch
(e) {
// not production code
console.log(
"Error fetching data: "
+ e.toString());
}
The same code with promises does not look too bad, but I find it harder to read, and thus maintain:
fetch(url)
.then(response => response.json())
.then(result => {
let itemsToLoad = result.data.children.map(i =>
new
RedditViewModel(i.data));
this
._redditItems.load(args.index, itemsToLoad);
}).
catch
(e => {
// not production code
console.log(
"Error fetching data: "
+ e.toString());
});
As you can see, the async code is easier to read and the error handling happens with the good old boring (and dependable) try/catch.
The entire sample app can be found at
GitHub. Please note that to run it on iOS, you need to manually change the info.plist. Read this
blog post for instructions.
The results are nice:
To conclude my experiment, I found I enjoyed writing ES6/ES7 style code. Modern JavaScript is a very different beast than it used to be just a couple of years ago. It is suitable for writing complex apps which need to be continuously maintained and improved. I am very happy how simple integration of NativeScript and Babel turned out to be.
tns install babel && tns platform add android
tns install babel && tns platform add android
tns install babel && tns platform add android
tns install babel && tns platform add android