Upgrading Hybrid Apps to Native with NativeScript

When it comes to native development, some of the most frequently asked questions involve comparing native to hybrid web frameworks, such as Apache Cordova. Why would you develop native applications when you can develop cross-platform applications using common web technologies? Isn't it easier to develop hybrid web applications rather than use native technologies?

This site should document how to accomplish similar tasks using either native development or hybrid web development, with the goal of helping developers familiar with hybrid migrate to a more performant native solution.

Introduction

The core examples in this guide will be based on the very popular Ionic and NativeScript frameworks. Both frameworks have seen heavy adoption amongst mobile developers and share a common language, Angular. However, the same basic concepts exist when comparing a native application against a hybrid web application. For example, comparing NativeScript against Onsen UI or Kendo UI would be valid because both Onsen UI and Kendo UI are hybrid web frameworks, while NativeScript is not a hybrid framework. Comparing NativeScript against React Native and Xamarin would not be valid because all three are native mobile frameworks. The goal here is to migrate from hybrid to native, not native to native. The focus will be NativeScript and Ionic.

This guide will cover all areas of mobile application development ranging from UI components to data management and working with web services. It will not teach you how to develop mobile applications, but it will act as a solid resource when making a decision between development technologies and frameworks.

Let's start by taking a closer look at the hybrid web and native development technologies, what they do well, and where they fall short.


A Technology Overview

Hybrid web application development uses the strategy of taking common web technologies such as HTML, CSS, and JavaScript, to build mobile applications that can be listed in app stores such as, but not limited to, Google Play or iTunes. This is made possible through the use of the platform web view, which will be discussed throughout the guide. While the option to develop mobile applications using the hybrid approach hasn't been around nearly as long as native development, it has been around long enough to become stable and attract a large community.

Ionic Framework Showcase

Hybrid web applications solve many pitfalls to mobile application development, but also introduce others that had not previously existed.

For more information on what hybrid apps are, check out this article on the subject.

The Strengths of a Hybrid Web Application

Hybrid web applications have many strengths, the most obvious being that you can write Android and iOS mobile applications using a single set of code. These applications are typically written with JavaScript, CSS, and HTML, technologies very familiar to web developers that may not be writing mobile applications otherwise. With hybrid web applications using common web technologies, you're now eliminating the barrier to entry for many developers that may be turned off from learning native development languages like Java, Objective-C, or Swift.

Development Time is Reduced

By consolidating the development technologies and offering the means to develop using skills that many developers already have, developing hybrid web applications becomes quite easy and with a minimal learning curve. It also makes development very fast because you're no longer working with native application tooling and compiled languages, but instead the same things one would use when developing websites.

Where Hybrid Web Applications Fall Short

It's not all fun and games when it comes to hybrid web applications. Like with any technology, there are places where it falls short.

A common complaint amongst hybrid application developers, or mobile application developers in general is performance. Take Mark Zuckerberg of Facebook for example, in an interview he gave on VentureBeat:

"The biggest mistake we’ve made as a company is betting on HTML5 over native."

Facebook had given hybrid app development a shot, but found it to be insufficient because of performance issues.

Performance in a Web View

To be clear, many mobile application developers will talk ill of hybrid web application performance without the facts. While Facebook's reason to switch was probably legitimate, the fact is, in some scenarios you cannot determine the difference between an application that was developed using native code versus one that was created using hybrid technologies. These scenarios include applications with few moving parts, and limited animations or GPU processing.

This is because hybrid web applications are rendered in a web view, similar to the Android and iOS devices web browser. Every device, platform, and operating system version handles the processing of web views differently, creating an inconsistent experience in performance. While the web does include WebGL, it isn't readily available on all platforms without the help of external products such as Crosswalk, adding an extra layer of complexity to tasks that most devices should be able to accomplish with minimal effort.

Plugin Availability and Development

While frameworks such as Ionic let you access native platform features such as the camera or accelerometer through plugins, it isn't particularly easy to access native features that don't already exist as a plugin. The process of writing your own plugins involves an understanding of Objective-C and Java, as well as an understanding on how plugins must be wrapped for Apache Cordova consumption. This can make things tricky if planning on accessing platform features that might not be popular amongst developers because plugins may not exist.

Use Cases of a Hybrid Web Application

Hybrid web applications built with frameworks such as Ionic are not as bad as some people will lead you to believe. Hybrid web applications are extremely good at the following:

  • Brochure type applications
  • A mobile conversion of a static website
  • Application prototyping and quick development

Based on a few of the things mentioned above in regards to hybrid pitfalls, some use cases that might not be a good fit for a hybrid web development technology include:

  • Mobile games
  • Animation heavy applications
  • Media heavy applications
  • Applications with list views or large amounts of data
  • Applications that require native platform features, but have no plugins available

Progressive Web Applications

In 2016 progressive web applications (PWA) started getting a lot of buzz. In case you're unfamiliar, progressive web applications are more or less web applications that use responsive layouts and UI components that look similar to those found in native mobile applications, particularly Android.

Progressive Web Applications via Google

Progressive web applications are cached to the device so they work offline like mobile applications, but they don't solve any of the problems that hybrid web applications or standard web applications face when being run on mobile devices. For example you don't have access to native platform features in a progressive web app and your applications don't get published to the various app stores.

Like with hybrid web applications, progressive web applications are still rendered via a device web view. As previously mentioned, web views have inconsistent performance based on numerous factors such as platform operating system, platform version, and device specs. High end devices such as those from the Google Pixel line will be able to process these applications with little to no performance issues, however not everyone in the world has a high end device.

Filling the Gap with NativeScript

NativeScript is a cross-platform framework, similar to Ionic Framework. The difference with NativeScript is that you're building native applications with JavaScript, CSS and XML markup instead of applications destined to be run in a web view.

NativeScript Showcase

Subtracting the web view gives you all the benefits that you'd find in Ionic Framework, or similar, with only a few of the shortcomings.

NativeScript works by using V8 for Android and JavaScriptCore for iOS. These JavaScript virtual machines know what Android and iOS is because the NativeScript runtime injects them.

More information on how NativeScript works, can be found here.

Since NativeScript doesn't use a web view, the DOM does not exist, removing HTML from the equation. Depending on how you look at it, this could be a downside because you're giving up a familiar markup language for XML. However, since the web view is not used in NativeScript, you're making a trade for much better performance.

The Similarities Between Two Frameworks

It’s probably a good idea to put emphasis on the similarities between native development with NativeScript and hybrid development with, in this case, Ionic Framework. After all, they do share a common language stack.

Open Source

Apache Cordova, Ionic Framework, and NativeScript are all open source in some way. NativeScript is open source under the Apache 2.0 license. You can feel safe that your development tools are being contributed to by a community of very intelligent developers and that you can use them free of charge. You can even join in and contribute to any of these frameworks as well.

Media Resources

It doesn't matter if you're using Java, Objective-C, NativeScript, or Ionic Framework, your application resources such as launch icons, and splash screens will remain consistent.

For example, the Android platform has image expectations based on screen size, screen density, and orientation. These sizing specifications are based on acronyms like xhdpi, mdpi, ldpi, or similar. Typically images need to be created for every size specification you wish to support. While the iOS platform does not use the Android acronyms, they still have their own sizing guidelines.

Store and Device Deployment

Whether it be native or hybrid, deployment to physical devices or publishing to the app stores will be the same for iOS and Android. Publishing your NativeScript application to Google Play will follow the same process as Ionic Framework and likewise with iTunes. The process for publishing an application to Google Play is not the same as publishing to iTunes. This is not based on how the application was developed, but based on the app store policies and procedures.

NativeScript and Ionic with Angular

Angular has opened the door when it comes to development. It is what puts not only NativeScript and Ionic in the same playing field, but also NativeScript and web development. While web development is very important, the focus here will be the difference between native and hybrid development.

Since Ionic and NativeScript share the same framework, the barrier to making a switch becomes much lower. You can take most of your Ionic code and bring it to NativeScript. This will give you a more performant application and a more positive user experience.

Another commonality between Ionic and NativeScript is that the command line interfaces (CLI) work in a similar fashion. Let's compare how the two work towards creating and running a project.


The NativeScript and Ionic CLI

Getting started with NativeScript development is very similar to that of developing applications with Ionic. Both use a command line interface (CLI) and have a similar project directory structure.

Going forward we're going to assume that you already have the Ionic CLI installed, and the NativeScript CLI installed. In both frameworks the Android SDK is required for building Android applications and Xcode is required for building iOS applications.

Creating an Ionic Project

With the Ionic CLI installed and configured, creating a project is as simple as the following:

ionic start MyProject blank

The above command will create an Ionic project that uses TypeScript and Angular.

Creating a NativeScript Project

The NativeScript CLI makes creating Angular projects just as easy. Using the command line, execute the following:

tns create MyProject --ng

The above command will create a NativeScript project that uses TypeScript and Angular, just like with Ionic.

Adding Build Platforms to Ionic

Up until now, Android and iOS have not been included as a deployment option for the Ionic application, meaning we cannot deploy anywhere beyond the browser.

Android, iOS, and the browser are referred to as "build platforms". Build platforms are the operating systems you wish to support, whether it be Android, or iOS, or both. To add a build platform, execute the following from a command line:

ionic platform add [platform]

Swap out [platform] with either android or ios. To an extent, Ionic Framework also supports the windows platform as described here.

Adding Build Platforms to NativeScript

Adding build platforms to a NativeScript application is exactly the same as previously seen for Ionic. Using a command line, execute the following:

tns platform add [platform]

Remember to swap out [platform] with the appropriate ios or android build platform.

Testing an Ionic Application

There are many ways to build or test an Ionic application. A popular thing to do is to use the Ionic CLI's live-reload feature. To do this, execute the following:

ionic emulate [platform] --livereload --consolelogs --serverlogs

After swapping out [platform] with the appropriate build platform, the Ionic CLI will quickly rebuild and deploy your app after every change you make.

Testing a NativeScript Application

Like with Ionic, there are many ways to build and test a NativeScript application. NativeScript has a live-reload feature that accomplishes the same thing as Ionic. To make use of this, execute the following:

tns livesync [platform] --emulator --watch

When you save your application code, the NativeScript CLI automatically recompiles and deploys your code, making your development process quick.


The Application Project Structure

Because both Ionic and NativeScript use Angular, the projects share a common directory structure. Ionic uses a lot of proprietary components rather than raw Angular, which you'll see throughout the guide, so the project structures between Ionic and NativeScript will not be exactly the same, but they will be similar. The project structures between NativeScript and an Angular web application will be more similar as NativeScript uses a near raw form of Angular.

The Ionic Directory Structure

The Ionic application file and directory structure has a taste of Angular, but it is in fact very much proprietary. This means that changes to the Angular framework will happen independently from changes to Ionic.

MyProject
    platforms
    plugins
    resources
    src
        app
            app.component.ts
            app.html
            app.module.ts
            app.scss
        pages
            home
                home.ts
                home.html

All development will happen in the project's src directory. The application bootstrapping will happen in the project's src/app/main.ts file and each page of the application will exist as an HTML file and TypeScript file in the src/pages directory. This is similar to how things are done in Angular, but not quite the same.

The platforms directory will contain compiled Android and iOS code. When you build your application, the binaries will be found in either of the appropriate directories. All Apache Cordova plugins will reside in the project's plugins directory and all the application resources such as icons and splash screens will reside in the project's resources directory.

The NativeScript Directory Structure

After using the CLI to create a NativeScript project that uses Angular, you'll be left with the following files and directories. Of course there are many other files and directories generated, but below is what matters to application developers.

MyProject
    app
        App_Resources
        app.component.html
        app.component.ts
        main.ts
        app.css
    platforms

All development happens in the project's app directory, similar to how things are done in Ionic. The difference here is that NativeScript with Angular does not use proprietary libraries and components. Although not directly apparent when migrating from Ionic to NativeScript, it is more apparent when trying to migrate from an Angular web application to NativeScript.

The application bootstrapping will happen in the project's app/main.ts file and the root component for the application will be the app/app.component HTML and TypeScript file pair. While you could do all your development in the app/app.component files, you typically want to create a components directory, similar to how Ionic is structured in how it uses an src/pages directory.


Differences in the Framework UIs

There are a few things to understand when switching from hybrid web frameworks such as Ionic to NativeScript. While both will have very similar TypeScript components, the UI components are not the same.

Developing a UI with Ionic

Ionic is a hybrid web application framework. This means that you use HTML and CSS, the same used in a web application accessible by a web browser.

Take the following for example:

<ion-header>
    <ion-navbar>
        <ion-title>Ionic Blank</ion-title>
    </ion-navbar>
</ion-header>
<ion-content padding class="content">
    <h2>Tap the button</h2>
    <button primary>TAP</button>
    <div class="message">16 taps left</div>
</ion-content>

Hold the phone! Those don't look like any HTML tags I've ever heard of!

Ionic has its own markup, which can technically be considered XML. However, you can still use standard HTML tags if you choose to.

The above HTML will create a UI that looks like the following:

Basic Ionic UI

Ignoring the flavor of markup, notice there is an action bar, title, button, and some kind of message. We're also using CSS class names. Ignore the actual components used because they will be explained in the next section. We only want to get a basic feel for how to develop an Ionic UI.

Developing a UI with NativeScript

This is where things can potentially become complicated. NativeScript will build native applications, and native applications hold no concept of HTML markup. Remember, we're not using a web view in NativeScript.

Instead we have to use XML markup. While HTML is a flavor of XML, the tags are not going to be the same. Let's migrate the Ionic UI to NativeScript.

<ActionBar title="NativeScript Blank"></ActionBar>
<StackLayout>
    <Label text="Tap the button" class="title"></Label>
    <Button text="TAP"></Button>
    <Label [text]="message" class="message"></Label>
</StackLayout>

The above XML will create a UI that looks like the following:

Basic NativeScript UI

The NativeScript version contains the same components, with nearly the same markup. The iOS and Android renderings are different because iOS and Android use different UI components. For example, underneath the NativeScript covers, iOS uses a UIButton and Android uses an android.widget.button. Ionic uses CSS to style components to look the same on both platforms; NativeScript aims to keep things looking native.

It is no lie that the NativeScript markup is a little more difficult to remember. HTML has been around forever and is easily remembered. However, the two markups between Ionic and NativeScript are not too different and even share all the same Angular template syntax. The main difference here is that NativeScript will build a native application with native performance.

CSS

Both Ionic Framework and NativeScript applications can receive custom styles and theming through standard web CSS. However, NativeScript only uses a CSS subset. This means that not all CSS attributes available for Ionic or web applications will work in NativeScript, but all CSS attributes found in NativeScript will work in Ionic and web applications. Remember that NativeScript is using native UI components, and as such, many web CSS properties do not have equivalent APIs on iOS and Android.

Let's take a look at some CSS that will work in both Ionic and NativeScript:

.title {
    font-weight: bold;
}

The above CSS is a classic example of creating a class name that could represent a title where you might need it to be bolded. It will work great on the web, in Ionic, and in NativeScript.

To add this title class name to a UI component in Ionic, you'd do something like this:

<span class="title">My Title</span>

In NativeScript it would look like the following:

<Label text="My Title" class="title"></Label>

Not much difference between the two. However, take the following example:

h1 {
    text-shadow: 2px 2px #ff0000;
}

That will work fine on the web and in Ionic, but not in NativeScript.

To find the complete list of supported NativeScript CSS properties, check out the official UI documentation.

So what are our options when it comes to NativeScript UI components?


Comparing UI Components

Both Ionic and NativeScript offer pretty much the same set of UI components. This is because both try to match what Apple and Google have laid out in their design specs. However, when it comes to NativeScript, the big advantage for you, the developer, is that these are native UI components built by engineers at Apple and Google that have devoted entire teams to optimizing their controls. NativeScript passes this performance benefit straight through to the developer by always using the native controls available on a device. In Ionic, these components are made to look native with CSS, and can limit the performance of the application.

We're going to compare how to use each component for each of the two frameworks.

While there are plenty of other components in each of the two frameworks, the UI components listed below are some of the most common that you'll find yourself using in any application.

Buttons

Buttons are one of the most common UI components in any mobile application. They are used to perform actions, whether it’s to navigate, to start a process, or anything else.

To create a button in Ionic, you would include the following at a desired location within your HTML markup:

<button>Show Alert</button>

Ionic Buttons

The above markup was taken from the Ionic Button documentation.

To create a button in NativeScript, you would include the following at a desired location within your XML markup:

<Button id="button" text="Show Alert"></Button>

NativeScript Buttons

The above markup was taken from the NativeScript Button documentation.

Images

Many applications have the need for images, whether the images are loading dynamically from some remote resource or from resources bundled within the application.

In Ionic, images can be included within an application by using standard HTML img tags like the following:

<img src="https://placehold.it/350x150" />

Ionic Images

To include an image in NativeScript, you would include the following at a desired location within your XML markup:

<Image src="https://placehold.it/350x150"></Image>

NativeScript Images

The above markup was taken from the NativeScript Image documentation.

Spans and Labels

There is a place for read-only text in nearly every mobile application. Maybe you're creating text to sit beside an input field or maybe you're adding text to a list element. There are many scenarios. This read-only text is identified within <span> tags for web applications including hybrid like Ionic. In NativeScript these are referred to as <Label> tags.

To include <span> text within an Ionic application, it might look like the following:

<span>This is some static text</span>

Ionic Labels

In NativeScript, the same might look like this:

<Label text="This is some static text"></Label>

NativeScript Labels

The markup demonstrated above was taken from the NativeScript Label documentation.

Select Boxes and List Pickers

In web applications there are often drop-down boxes for selecting various pieces of data. They go by many names including drop-down, pull-down, selects, and list-pickers.

Ionic refers to these UI components as Selects, and they can be included in an application by using the following markup:

<ion-select [(ngModel)]="gaming">
    <ion-option value="nes">NES</ion-option>
    <ion-option value="n64">Nintendo64</ion-option>
    <ion-option value="ps">PlayStation</ion-option>
    <ion-option value="genesis">Sega Genesis</ion-option>
    <ion-option value="saturn">Sega Saturn</ion-option>
    <ion-option value="snes">SNES</ion-option>
</ion-select>

The above markup was taken from the Ionic Select documentation.

NativeScript refers to these components as Listpickers, and the same implementation as seen in Ionic can be done with the following markup:

<ListPicker
    row="1"
    [items]="games">
</ListPicker>

NativeScript Listpicker

The markup above was taken from the NativeScript Listpicker documentation.

The games variable used in the NativeScript example, is an Array<string> defined in TypeScript like so:

public games: Array<string> = ["NES", "Nintendo 64", "Playstation", "Sega Genesis"];

Lists

Another very popular component is the list view. Every application will have at least one of these, making it an essential component.

To create a list view in Ionic, you would include the following within your HTML markup:

<ion-list>
    <ion-item *ngFor="let item of items">
        {{ item.title }}
    </ion-item>
</ion-list>

Ionic List Views

The above markup was taken from the Ionic Lists documentation.

To accomplish the same in NativeScript, you would include the following XML markup:

<ListView [items]="items">
    <ng-template let-item="item">
        <Label [text]="item.title"></Label>
    </ng-template>
</ListView>

NativeScript ListView

The above markup was taken from the NativeScript ListView documentation.

A more thorough discussion of listviews, and why the syntax is a bit different here is coming in chapter 7.

Text Inputs

There are many ways to display text inputs on the screen in a mobile application. You can have placeholder text in the inputs or you can have labels describing the text input.

An example of text inputs with inline labels in an Ionic application can be seen below:

<ion-list>
    <ion-item>
        <ion-label>Username</ion-label>
        <ion-input type="text"></ion-input>
    </ion-item>
    <ion-item>
        <ion-label>Password</ion-label>
        <ion-input type="password"></ion-input>
    </ion-item>
</ion-list>

Ionic Inputs

The above HTML markup was taken from the Ionic Input documentation for inline labels. More options in regards to input layout can be found in the documentation.

To accomplish the same input fields and layout in NativeScript, include the following XML markup:

<GridLayout rows="auto auto" columns="90 *">
    <Label text="Username" row="0" col="0"></Label>
    <TextField text="" row="0" col="1"></TextField>
    <Label text="Password" row="1" col="0"></Label>
    <TextField secure="true" text="" row="1" col="1"></TextField>
</GridLayout>

NativeScript Text Fields

The above XML markup was taken from the NativeScript Text Field documentation. Don’t worry about the specifics of the GridLayout control for now, as we’ll be covering NativeScript layouts later in this guide.

Action Bars

Action bars can also be referred to as navigation bars or headers. Each page can have up to one.

To create an action bar in Ionic, add the following markup to the top of your HTML file:

<ion-header>
    <ion-navbar>
        <ion-title>Ionic Application</ion-title>
        <ion-buttons end>
            <button>Next</button>
        </ion-buttons>
    </ion-navbar>
</ion-header>

Ionic Action Bar

There was no official Ionic documentation for creating an action bar, but it can be loosely read about in the Ionic navigation documentation.

To do the same in NativeScript, add the following XML markup to a UI file of your choosing:

<ActionBar title="NativeScript Application">
    <ActionItem text="Next"></ActionItem>
</ActionBar>

NativeScript Action Bar

The NativeScript action bar markup was taken from the NativeScript documentation.

Searchbars

When building an application with a list of data, sometimes it makes sense to have a means to search for data in said list. Searching isn't limited to that scenario, but there are components for both Ionic and NativeScript to make your life easier.

To include a search bar in your Ionic application, include the following markup:

<ion-searchbar (ionInput)="getItems($event)"></ion-searchbar>

In the above example, any changes to the input field will call the getItems method, passing in the value of the search.

The above markup was taken from the Ionic Searchbar documentation.

To accomplish the same kind of searching in NativeScript, include the following markup into your project:

<SearchBar #sb
    (textChange)="getItems(sb.text)">
</SearchBar>

NativeScript Search Bar

Like with the Ionic example, the input data would be bound and passed into a getItems method.

The above markup was taken from the NativeScript Search Bar documentation.

Loading and Activity Indicators

When performing long-running or asynchronous tasks, it might be a good idea to show some kind of indicator to your users to let them know the application didn't freeze on their device, or that your app is actually performing a task.

In Ionic Framework, these indicators are referred to as loading indicators. Including them in your application is done through TypeScript rather than markup.

import { LoadingController } from "ionic-angular";

export class MyPage {
    constructor(public loadingCtrl: LoadingController) { }

    presentLoading() {
        let loader = this.loadingCtrl.create({
            content: "Please wait...",
            duration: 3000
        });
        loader.present();
    }
}

Ionic ActivityIndicator

The above TypeScript code was taken directly from the Ionic Loading documentation.

To accomplish something similar in NativeScript you'd use the ActivityIndicator tags in your markup. This would look like the following:

<ActivityIndicator #activityIndicator width="100" height="100" row="1" ></ActivityIndicator>

NativeScript ActivityIndicator

How do you trigger it in NativeScript?

The ActivityIndicator has a busy property that when true, will show the control—otherwise it will be hidden. The following is an example of using the indicator:

<Button
    text="Toggle Activity"
    (tap)="(activityIndicator.busy = !activityIndicator.busy)">
</Button>

The markup seen above was taken from the NativeScript ActivityIndicator documentation.

Switches and Toggles

Toggles, often referred to as Switches, are an alternate form of checkbox and are commonly used throughout mobile applications.

Ionic refers to them as Toggles, and they can be included in a UI via the following markup:

<ion-item>
    <ion-label>Audio</ion-label>
    <ion-toggle checked="false"></ion-toggle>
</ion-item>

Ionic Toggles

Toggles, such as the one above, are outlined in the Ionic Toggle documentation.

In NativeScript, these components are called Switches. To accomplish the same functionality in NativeScript, add the following XML markup:

<GridLayout rows="auto" columns="90 *">
    <Label text="Audio" row="0" col="0"></Label>
    <Switch checked="false" row="0" col="1"></Switch>
</GridLayout>

NativeScript Switches

The markup above was taken from the NativeScript Switch documentation.

Of course in the two examples, the containing UI elements are a bit different, not because they have to be, but because in this scenario it makes more sense. In Ionic, a list item was used and in NativeScript a GridLayout was used.

Sliders and Ranges

Ranges, often referred to as Sliders, are available UI components for choosing from a range of values without the use of a dropdown or keyboard input.

Ionic refers to these UI components as Ranges and they can be included in your application with the following HTML markup:

<ion-item>
    <ion-range min="0" max="100" [(ngModel)]="brightness" secondary>
        <ion-label range-left>0</ion-label>
        <ion-label range-right>100</ion-label>
    </ion-range>
</ion-item>

Ionic Ranges

In the above example, the Range will have a minimum of 0 and a maximum of 100 with a step value of 1. All three of which can be changed. This markup was taken from the Ionic Range documentation.

NativeScript refers to these UI components as Sliders. To accomplish the same UI as found in the Ionic example, include the following NativeScript XML markup:

<GridLayout rows="auto" columns="50 * 50">
    <Label text="0" textWrap="true" row="10" col="0"></Label>
    <Slider minValue="0" maxValue="100" [(ngModel)]="brightness" row="0" col="1"></Slider>
    <Label text="100" textWrap="true" row="0" col="2"></Label>
</GridLayout>

NativeScript Sliders

The above markup was taken from the NativeScript Slider documentation.

Dialogs

Alerts, prompts, and popups are all a type of dialog. They can be used strictly for displaying information or accept user input through button press or input fields. While these are UI components, they are triggered via the Angular code rather than markup.

Ionic refers to these dialogs as alerts. To include an alert in your application, execute the following code:

import { AlertController } from "ionic-angular";

export class MyPage {

    constructor(public alertCtrl: AlertController) { }

    showAlert() {
        let alert = this.alertCtrl.create({
            title: "Attention!",
            subTitle: "Oh no, something went wrong...",
            buttons: ["OK"]
        });
        alert.present();
    }

}

Ionic Dialogs

The above TypeScript example uses the AlertController to create a basic dialog that can be dismissed. Other dialogs can be implemented via the Ionic Alert documentation.

In NativeScript, these alerts are referred to as dialogs. To create the same dialog as seen above in NativeScript, add the following TypeScript code:

import * as dialogs from "ui/dialogs";

export class MyComponent {

    constructor() { }

    showAlert() {
        dialogs.alert({
            title: "Attention!",
            message: "Oh no, something went wrong...",
            okButtonText: "OK"
        });
    }

}

NativeScript Dialogs

The above TypeScript was taken from the NativeScript Dialogs documentation. More examples of what can be done with dialogs can be found in that document.

Keep in mind that the important stuff is the dialogs component. The entire MyComponent class is just to put things into perspective.

Tabs

Sometimes the UX calls for something a bit different. Many applications find value in a tabbed interface where there are a series of tabs at the bottom or top of the UI, used for navigation to other pages or components.

To implement a basic tabbed interface in Ionic, you would create HTML markup that looks like the following:

<ion-tabs class="tabs-basic">
    <ion-tab tabTitle="People" [root]="peoplePage"></ion-tab>
    <ion-tab tabTitle="Places" [root]="placesPage"></ion-tab>
</ion-tabs>

Ionic Tabs

The above example displays two tabs. The remaining UI is loaded from the currently active page, whether it be peoplePage or placesPage. These pages are defined in the TypeScript that is paired with the HTML containing the tab foundation. For example, it would look something like this:

import { Component } from "@angular/core";
import { ionicBootstrap } from "ionic-angular";
import { PeoplePage } from "./people-page";
import { PlacesPage } from "./places-page";

@Component({
    template: `
    <ion-tabs class="tabs-basic">
        <ion-tab tabTitle="People" [root]="peoplePage"></ion-tab>
        <ion-tab tabTitle="Places" [root]="placesPage"></ion-tab>
    </ion-tabs>`
})
class MyApp {

    peoplePage: any;
    placesPage: any;

    constructor() {
        this.peoplePage = PeoplePage;
        this.placesPage = PlacesPage;
    }

}

The TypeScript code and HTML markup was taken from the Ionic Tabs documentation.

To accomplish the same tabbed interface in NativeScript, the following XML markup would need to be included:

<ActionBar title="NativeScript Application"></ActionBar>
<GridLayout>
    <TabView #tabview [selectedIndex]="tabindex">
        <StackLayout *tabItem="{title: 'People'}">
            <ListView [items]="people">
                <ng-template let-item="item">
                    <Label [text]="item" margin="10"></Label>
                </ng-template>
            </ListView>
        </StackLayout>
        <StackLayout *tabItem="{title: 'Places'}">
            <ListView [items]="places">
                <ng-template let-item="item">
                    <Label [text]="item" margin="10"></Label>
                </ng-template>
            </ListView>
        </StackLayout>
    </TabView>
</GridLayout>

NativeScript Tab View

In the above markup the UI components to each tab are loaded and switched between depending on the active tab. More information on the NativeScript Tab View can be found in the documentation.

Segments

Segmented bars are very similar to tabs and tab views. They are generally thought of as a horizontal collection of buttons and are a good way to change information within the same view.

To create a segmented bar of buttons in Ionic, include the following HTML markup:

<div padding>
    <ion-segment [(ngModel)]="pet">
        <ion-segment-button value="kittens">
            Kittens
        </ion-segment-button>
        <ion-segment-button value="puppies">
            Puppies
        </ion-segment-button>
    </ion-segment>
</div>
<div [ngSwitch]="pet">
    <ion-list *ngSwitchCase="'puppies'">
        <ion-item>Siberian Husky</ion-item>
        <ion-item>German Shepard</ion-item>
    </ion-list>
    <ion-list *ngSwitchCase="'kittens'">
        <ion-item>British Shorthair</ion-item>
        <ion-item>Himalayan</ion-item>
    </ion-list>
</div>

Ionic Segmented Bar

Selecting a segment button will show or hide content based on the ngSwitchCase value. The above HTML markup was taken from the Ionic Segment documentation.

In NativeScript, the segmented bar is a little more TypeScript driven. To accomplish the same in NativeScript, include the following XML markup:

<SegmentedBar #sb
    [items]="segments" selectedIndex="0"
    (selectedIndexChange)="change(sb.selectedIndex)">
</SegmentedBar>
<ListView [items]="items">
    <ng-template let-item="item">
        <Label [text]="item"></Label>
    </ng-template>
</ListView>

NativeScript Segmented Bar

The SegmentedBar in NativeScript accepts an array of SegmentedBarItem that we define in TypeScript. When a button is selected, we'll populate the list with different data depending on the selected button.

The TypeScript might look like this:

import { Component, ChangeDetectionStrategy } from "@angular/core";
import { SegmentedBarItem } from "ui/segmented-bar";

@Component({
    selector: "basic-segmented-bar-component",
    templateUrl: "segmented-bar/basic-segmented-bar/basic-segmented-bar.component.html",
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class BasicSegmentedBarComponent {

    public segments: Array<SegmentedBarItem>;
    public items: Array<string>;

    constructor() {
        this.items = [];
        let puppiesSegment = <SegmentedBarItem>new SegmentedBarItem();
        puppiesSegment.title = "Puppies";
        let kittensSegment = <SegmentedBarItem>new SegmentedBarItem();
        kittensSegment.title = "Kittens";
        this.segments = [puppiesSegment, kittensSegment];
    }

    change(index) {
        if(index == 0) {
            this.items = ["Siberian Husky", "German Shepard"];
        } else {
            this.items = ["British Shorthair", "Himalayan"];
        }
    }

}

In the above TypeScript code we define each of the segments to be shown. When the change method is called, the list is changed based on the button index.

More on the NativeScript Segmented Bar can be found in the official documentation.

Date and Time Pickers

Pickers for choosing the date and time are common in many applications. No one wants to have to use keyboard input to accomplish the task.

In Ionic, this is referred to as a DateTime picker. To include such functionality in an Ionic application, one would include the following HTML markup:

<ion-item>
    <ion-label>Start Time</ion-label>
    <ion-datetime displayFormat="h:mm A" pickerFormat="h mm A" [(ngModel)]="event.timeStarts"></ion-datetime>
</ion-item>

Ionic DateTime Picker

In the above markup you can define how you want the picker to display and how you want the picked value to display. Either date or time can be chosen. This markup was taken from the Ionic DateTime documentation.

Things are a little different with NativeScript because there is a UI component for both date and time, instead of mashing both into one.

To choose date values in NativeScript one would include the following XML markup:

<datePicker #datePicker (loaded)="configure(datePicker)" verticalAlignment="center"></datePicker>

NativeScript Date Picker

The configuration defined in the loaded event handler lets you define the default value and minimum and maximum date values. This configuration looks something like this:

configure(datePicker: DatePicker) {
    datePicker.year = 1980;
    datePicker.month = 2;
    datePicker.day = 9;
    datePicker.minDate = new Date(1975, 0, 29);
    datePicker.maxDate = new Date(2045, 4, 12);
}

The code used in the Date Picker can be found in the NativeScript documentation.

Similarly, the markup for the NativeScript Time Picker would look like the following:

<timePicker #timePicker (loaded)="configure(timePicker)" verticalAlignment="center"></timePicker>

NativeScript Time Picker

Likewise, the configuration that gets defined in the loaded event handler would look like the following:

configure(timePicker: TimePicker) {
    timePicker.hour = 9;
    timePicker.minute = 25;  
}

The markup and code seen above can be found in the NativeScript Time Picker documentation.

Scrolling Capabilities

There are often times where UI components will fill up more space than exists on the screen. For example, maybe you have a really lengthy form of some sort. To accommodate this, scrolling will need to be an option.

To scroll in Ionic, there is an ion-scroll component. It can be used like the following:

<ion-scroll scrollY="true"></ion-scroll>

The above markup was taken from the Ionic Scroll documentation. You can decide to scroll vertically or horizontally.

The same effect can be accomplished in NativeScript in a very similar fashion:

<ScrollView orientation="vertical"></ScrollView>

In the above markup, the scroll orientation is vertical, but horizontal is an option as well. The markup was taken from the NativeScript Scroll View documentation.

Component Theming

Out of the box, Ionic UIs look more cosmetically appealing than the same with NativeScript. This ties into the fact that all of the Ionic Framework components are crafted with CSS and the DOM, while NativeScript components are native to whatever Apple and Google provide with no changes.

If you'd like to use a pre-made, visually appealing, theme for NativeScript, there are a few available. The officially supported NativeScript theme can be obtained here and can be included in your application by importing a few CSS files. With this theme included, your application will look stunning.

NativeScript Theme

The Web View

Just because NativeScript doesn't run in a web view, doesn't mean you can't include a web view within your application. There may be a scenario where you need to embed web content or launch web pages.

All of Ionic runs within a web view, but generally if you wish to launch websites you would use the Apache Cordova InAppBrowser plugin. While plugins will be discussed later on in the guide, the InAppBrowser in particular will not be. However, knowing that it is a way to render or launch web code, should be enough from an Ionic Framework perspective.

Assuming the InAppBrowser plugin for Apache Cordova was included in an Ionic project, a website could be launched from the following TypeScript:

var ref = cordova.InAppBrowser.open("https://www.google.com", "_blank", "location=yes");

More information on using the Apache Cordova InAppBrowser plugin in an Ionic application can be read about here.

To use a web view in NativeScript there are no required plugins. Instead you would include the following markup:

<WebView id="wv" src="https://www.google.com"></WebView>

The markup above was taken from the NativeScript Web View documentation. With the web view component you have access to various listeners that offer the same functionality as the InAppBrowser for Ionic. While the APIs won't be the same, such listeners include determining if the page has finished loading or if the page started loading.

NativeScript Layouts

Components, can, but probably shouldn't be mashed randomly within a NativeScript HTML file. Instead, there are a few different layout strategies that can be followed. Jen Looper from Progress wrote a great article on demystifying layouts, but we'll review a few of the common layouts.

The NativeScript StackLayout

The NativeScript StackLayout can be thought of as div tags for native development. By default, any components added within the StackLayout will be presented one after another vertically, in well, a stack. Optionally, these layouts can be set up to stack UI components horizontally as well.

Let's take the following web example:

<div><span>Item 1</span></div>
<div><span>Item 2</span></div>

The above markup will stack two read-only text elements in order. The same in NativeScript would look like the following:

<StackLayout>
    <Label text="Item 1"></Label>
    <Label text="Item 2"></Label>
</StackLayout>

Adding an orientation attribute to the StackLayout tag will let you choose horizontal. By default the layout is vertical.

The NativeScript GridLayout

The NativeScript GridLayout gives you a bit more precision when it comes to presenting UI components on the screen. Think of the GridLayout as a table tag for native development. You can define how many rows and columns will appear as well as how much space each row or column takes on the screen. The GridLayout was demonstrated earlier in the guide when presenting input fields on the screen.

Let's take the following web example:

<table>
    <tr>
        <td>Column 1</td>
        <td>Column 2</td>
    </tr>
</table>

The above markup would create a one row table with two columns. In NativeScript, the following GridLayout markup would accomplish the same:

<GridLayout rows="auto" columns="* *">
    <Label text="Column 1" row="0" col="0"></Label>
    <Label text="Column 2" row="0" col="1"></Label>
</GridLayout>

In the above NativeScript example, the rows would take up as much space as they need and the columns will fill the width evenly.

Official documentation on all the available NativeScript layouts can be seen on the NativeScript website.


While single page mobile applications exist, it is more common to have an application broken up into multiple pages, sometimes referred to as views, components, or routes.

Navigation in an Ionic application happens with the Ionic Framework navigation controller. This is a proprietary component that sits on top of the Angular router. With the navigation controller you can navigate forward or backwards in the navigation stack.

Let's assume that you've created an Ionic application with two pages, Page1 and Page2. For the sake of simplicity, it doesn't matter what these two pages do.

There are two ways to navigate between pages. You can navigate via the HTML tags, or via the TypeScript class. To navigate via TypeScript from Page1 to Page2, you might have something that looks like the following:

import { NavController } from "ionic-angular";
import { Page2 } from "./page2";

export class Page1 {

    public constructor(private navCtrl: NavController) { }

    public navigate() {
        this.navCtrl.push(Page2);
    }

}

Notice that Page2 was imported in the Page1 file. To navigate, Page2 was pushed to the navigation stack. It is common to pass data between data between pages. To do this, change the push statement to look like this:

this.navCtrl.push(Page2, {
    "name": "Nic Raboy"
});

Notice the optional object that was passed. This object is passed to Page2 and can be accessed like the following:

import { NavParams } from "ionic-angular";

export class Page2 {

    constructor(private navParams: NavParams) {
        let name = navParams.get("name");
    }

}

To navigate via the HTML in an Ionic application, it might look something like this:

<button [navPush]="page2">Navigate to Page2</button>

Notice the [navPush] tag in the above. It adds page2 to the navigation stack, where page2 is defined in a TypeScript file like so:

import { Page2 } from "./page2";

export class Page1 {

    public page2: any;

    public constructor() {
        this.page2 = Page2;
    }

}

How you choose to navigate in your Ionic application is left to your imagination.

In a NativeScript application, the navigation concepts are the same as an Ionic application, but the syntax is not. This is because NativeScript doesn't use any proprietary navigation components. Instead navigation happens with the Angular router, exactly as it would in a web application.

Let's assume again that you've got two pages already created in your NativeScript application. Let's call them Page1 and Page2. To navigate from Page1 to Page2, your TypeScript code might look like the following:

import { Router } from "@angular/router";

export class Page1 {

    public constructor(private router: Router) { }

    public navigate() {
        this.router.navigate(["/page2"]);
    }

}

Notice the use of the Angular router. Now let's say we want to pass some data to Page2 during navigation. The navigate command might look like the following:

this.router.navigate(["/page2", {
    "name": "Nic Raboy"
}]);

To access the data that was passed via Page2, you can do so like this:

import { ActivatedRoute } from '@angular/router';

export class Page2 {

    private name: string;

    public constructor(private route: ActivatedRoute) {
        this.route.params.subscribe(params => {
            this.name = params["name"];
        });
    }

}

Let's say you want to navigate through the XML markup rather than the TypeScript file. This is done in a similar fashion to how it is done with Ionic.

<a [nsRouterLink]="['/page2']">Navigate to Page2</a>

Notice the use of the nsRouterLink above. To use this tag in your markup you need to import it in your @NgModule typically found in the project's app/main.ts file. The @NgModule would look like the following:

import { NativeScriptRouterModule } from "nativescript-angular/router";

@NgModule({
    declarations: [AppComponent],
    bootstrap: [AppComponent],
    imports: [
        NativeScriptFormsModule,
        NativeScriptModule,
        NativeScriptRouterModule,
    ],
    providers: []
})

In the above TypeScript code we are importing the NativeScriptRouterModule and injecting it. This allows us to use them in the XML markup.

Now to be fair, when it comes to navigation in NativeScript, the routes must be defined ahead of time. They do not get imported on a per-need basis like in Ionic. Generally, these routes are defined in the project's app/app.routing.ts file like so:

import { Page1 } from "./page1";
import { Page2 } from "./page2";

export const AppRoutes: any = [
    { path: "", component: Page1 },
    { path: "page2", component: Page2 }
]

export const appComponents: any = [
    Page1,
    Page2
];

To include the routing information, it would be injected in the @NgModule as seen previously. The new @NgModule would look something like this:

@NgModule({
    declarations: [AppComponent, ...appComponents],
    bootstrap: [AppComponent],
    imports: [
        NativeScriptFormsModule,
        NativeScriptModule,
        NativeScriptRouterModule,
        NativeScriptRouterModule.forRoot(appRoutes)
    ],
    providers: []
})

In the above, notice the ...appComponents and NativeScriptRouterModule.forRoot(appRoutes). While the above might have seemed like a lot, it is actually a one time effort and is a very clean approach to managing navigation components.

Specifying NativeScript Page Transition

By default, navigation between pages in NativeScript will be animated using UINavigationController transitions for iOS and Fragment transitions for Android. These transitions can be changed via the pageTransition attribute on the nsRouterLink tag or via TypeScript.

For example, take the following:

<Button
    text="Flip Transition"
    [nsRouterLink]="['/page2']"
    pageTransition="flip">
</Button>

The above transition would show a flip animation.

The list of available transitions can be found in the NativeScript API documentation.

To accomplish the same change in page transitions via TypeScript, the code might look like the following:

flipToNextPage() {
    this.router.navigate(["/page2"], {
        transition: {
            name: "flip",
            duration: 2000,
            curve: "linear"
        }
    });
}

For more information on navigation transitions, head over to the official NativeScript documentation.

It is common that at least one of your pages will contain a list of data, but how that list operates differs between hybrid and native. We're going to explore those differences.


Exploring List Components

The ListView is one of many UI components that Ionic and NativeScript provide, however, because ListViews are so common in mobile applications, a thorough discussion of the control warrants its own chapter.

When you have a list of data in a native application, only the data that is visible is rendered to the screen. For example, if you have a list with 10,000 elements, but the screen only fits 10, then only 10 elements are rendered. Rendering all 10,000 elements would prove terrible on performance. To put things simply, the native controls manage memory for you, whereas on the web or in hybrid you're using the DOM, and the DOM has no preventative measures in regards to excessive memory usage. The DOM was built to structure documents, not complex user interfaces and memory management.

So what do Ionic and NativeScript do?

Lists in Hybrid Frameworks Like Ionic

When HTML is too long to fit on the screen, the browser adds scrollbars, but the content is still rendered, even if it isn't visible on the screen.

Take the following list in Ionic:

<ion-list>
    <ion-item *ngFor="let pokemon of pokemonList">
        {{ pokemon.name }}
    </ion-item>
</ion-list>

By default, the performance will be quite bad if pokemonList has enough items to need a scrollbar.

Now there are ways to "improve" performance. Back in Ionic Framework 1, collection-repeat and native scrolling was introduced, both with their own pros and cons towards an acceptable solution. These attempts to improve performance can be read in an article by Thomas Maximini.

In Ionic, things changed slightly.

In Ionic, VirtualScroll was introduced which allows for recycling of elements in the DOM as they are brought into the screen. This prevents everything from being rendered at the same time.

Using VirtualScroll can be seen below:

<ion-list [virtualScroll]="pokemonList">
    <ion-item *virtualItem=="let pokemon">
        {{ pokemon.name }}
    </ion-item>
</ion-list>

This is not a magic, fix-all solution though to Ionic applications. Just like with collection-repeat and native scrolling, there are some things to be worried about.

Per the official Ionic Framework documentation, the dataset must not change after the list is populated as changing it would be very expensive on the device. The list rows must also be uniform between all the elements in the list.

Lists in NativeScript

When it comes to NativeScript, lists are accomplished in a native fashion for both Android and iOS. Take the snippet of XML below:

<ListView [items]="pokemonList">
    <ng-template let-item="pokemon">
        <Label [text]="pokemon.name"></Label>
    </ng-template>
</ListView>

Why is the syntax in NativeScript so much different than in Ionic? The NativeScript ListView uses the underlying UITableView control on iOS, and the android.widget.ListView control on Android. NativeScript needs the <ng-template> tag to apply the contents of the template to the native controls appropriately.

In the above example, pokemonList is an array of objects, but it can easily be an array of strings, or any other type of array. Each item of the array, when populating the list, is identified by pokemon, at which point we can render it.

Items are not rendered to the screen unless they are within the view at the calculated scroll position. This is called lazy data loading, or only loading data when it is needed.

For more detail, Valio Stoychev wrote an excellent article regarding the performance benefits of lists in NativeScript.

The best part of using lists in a native fashion is that you don't need to jump through hoops to get the performance you need. No virtual scrolling. No need to worry about row sizes. Everything will work fast without headache.

You can also easily add on native features of a list view, such as pull to refresh. The free-to-use NativeScript RadListView control, for instance, makes implementing a variety of list layouts, pull to refresh, swipe to execute, endless scroll, and a variety of other features a breeze.


Pushing the Limits with Animations

Part of your application experience might include rich animations. Even the little things such as bouncing buttons can make a huge difference when it comes to how visually pleasing your application is. With Angular in the mix, there are several ways to animate components within an application.

Animate Ionic Components via CSS

Let's try to animate a button within an Ionic application. In one of your HTML UI files, include the following button:

<button primary class="spin">Spinning</button>

The button above is pretty standard, with the exception of the spin class. Open the HTML's corresponding SCSS file and create the spin class like follows:

.spin {
    -webkit-animation-name: spin;
    -webkit-animation-duration: 1s;
    -webkit-animation-delay: 1s;
    animation-name: spin;
    animation-duration: 1s;
    animation-delay: 1s;
}

@-webkit-keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

@keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

The animation will look liked this:

Ionic CSS Animations

The spin class will rotate the button 360 degrees over the span of one second.

Animate NativeScript Components vs CSS

Animating via CSS in NativeScript is more or less the same as with Ionic. For example, the same spinning effect would be done with the following XML:

<Button id="button" class="spin" text="Spinning"></Button>

If we wanted to take a look at the spin class, it would look like the following:

.spin {
    animation-name: spin;
    animation-duration: 1s;
    animation-delay: 1s;
}

@keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

In NativeScript, the animation would look like the following:

NativeScript CSS Animations

Notice that the -webkit stuff is no longer necessary in NativeScript. No need to try to accommodate multiple browsers because NativeScript doesn't use a browser.

Animate Ionic Components via Angular

Animating with CSS isn't the only way when it comes to an Angular application. There are actually animation components baked into Angular that can accomplish the job. Let's take the following Ionic button:

<button ion-button
    [@state]="currentState ? 'active' : 'inactive' "
    (click)="toggleState()">
    Change Color
</button>

Ionic Angular Animations

The [@state] is bound to a particular animation that we are going to define in TypeScript. When the button is clicked, the boolean value is changed, which alters the state of the animation. The TypeScript that pairs with the UI component might look like the following:

import { Component, trigger, state, transition, animate, style } from "@angular/core";
import { NavController } from "ionic-angular";

@Component({
    selector: "page-home",
    templateUrl: "home.html",
    animations: [
        trigger("state", [
            state("inactive", style({ "background-color": "red" })),
            state("active", style({ "background-color": "green" })),
            transition("inactive => active", [ animate("600ms ease-out") ]),
            transition("active => inactive", [ animate("600ms ease-out") ]),
        ])
    ]
})
export class HomePage {

    public currentState: boolean;

    constructor(public navCtrl: NavController) {
        this.currentState = false;
    }

    toggleState() {
        this.currentState = !this.currentState;
    }

}

The animation has an inactive and active state which we pair to the boolean variable currentState. When the state changes, the animations are set to happen for 600ms each.

Animate NativeScript Components via Angular

Let's try to perform the same Angular animation that we saw with Ionic, but this time in NativeScript. Take the following button for example:

<Button
    text="Change Color"
    [@state]="currentState ? 'active' : 'inactive' "
    (tap)="toggleState()">
</Button>

NativeScript Angular Animations

When the button is pressed, the toggleState method that we define via TypeScript will change the currentState boolean. This boolean is hooked up to the defined animation state as seen in the following TypeScript:

import { Component, trigger, state, transition, animate, style } from "@angular/core";

@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
    animations: [
        trigger("state", [
            state("inactive", style({ "background-color": "red" })),
            state("active", style({ "background-color": "green" })),
            transition("inactive => active", [ animate("600ms ease-out") ]),
            transition("active => inactive", [ animate("600ms ease-out") ]),
        ])
    ]
})
export class AppComponent {

    public currentState: boolean;

    constructor() {
        this.currentState = false;
    }

    toggleState() {
        this.currentState = !this.currentState;
    }

}

Looks pretty familiar to what we saw in Ionic, right?

The big difference is with NativeScript you can push the boundaries of what is possible a lot further because the animations are occurring in bytecode—not on the DOM in a web view.


Getting RESTful with Backends

There is a web service for everything in this modern era of technology. These services can range anywhere from getting you information about Pokemon to managing your WordPress blog. The possibilities are limitless and because these services exist, it minimizes a lot of would-be development to accomplish the same task.

Boosting the features of your mobile application by making use of a web service is quite easy with Ionic and NativeScript. In fact, they both use the same Angular components.

Consuming RESTful Data with Ionic

Let's see what it takes to consume Pokemon data from a very popular, and free to use, web service in Ionic. To keep things simple we're going to work off a fresh project. Once created, open the project's app/pages/home/home.ts file and include the following code:

import { Component } from "@angular/core";
import { NavController } from "ionic-angular";
import { Http } from "@angular/http";
import "rxjs/Rx";

@Component({
    templateUrl: "build/pages/home/home.html"
})
export class HomePage {

    constructor(public navCtrl: NavController, private http: Http) {
        this.http.get("http://pokeapi.co/api/v2/pokemon/25/")
            .map(result => result.json())
            .subscribe(result => {
                // console.log(result);
            }, error => {
                // console.error(error);
            });
    }

}

In the above code we've imported the Http component as well as RxJS and injected the Http component in the constructor method. Within the constructor method we make a GET request to an endpoint defined in the API documentation. Using RxJS, which is a part of Angular, we transform the results using the map function into JSON data. Calling subscribe will execute the request, at which point we'll either have a JSON error or a JSON result.

Consuming RESTful Data with NativeScript

Things are near identical with NativeScript. We're going to go off the same Pokemon example that we used in the Ionic project. Assuming a NativeScript project was already created, open the project's app/app.component.ts file and include the following TypeScript code:

import { Component } from "@angular/core";
import { Http } from "@angular/http";
import "rxjs/Rx";

@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
})
export class AppComponent {

    public counter: number = 16;

    public constructor(private http: Http) {
        this.http.get("http://pokeapi.co/api/v2/pokemon/25/")
            .map(result => result.json())
            .subscribe(result => {
                // console.log(result);
            }, error => {
                // console.error(error);
            });
    }

    public get message(): string {
        if (this.counter > 0) {
            return this.counter + " taps left";
        } else {
            return "Hoorraaay! \nYou are ready to start building!";
        }
    }

    public onTap() {
        this.counter--;
    }

}

All of the project template code was left in the above. What we added was the Http import as well as the RxJS import. In the constructor method, the Http component is injected and the request against the API is made.

In both Ionic and NativeScript, the Angular Http component is used. More information on what it offers can be found in the Angular documentation.

Next let’s look at what happens when you need to access native platform features that don't ship with the default Ionic and NativeScript project templates.


Using Native Plugins and Features

When it comes to building apps with either Ionic or NativeScript, not all platform features are easily accessible via a default project template. The same can be said when building applications with Java and Objective-C. This is where the use of plugins comes into play. Plugins allow you to extend your application to use native device features that might not be deployed with the framework, or might not be easily accessible via the framework.

Using Native Features with Ionic

While Ionic is a hybrid web framework, it is still built on top of Apache Cordova which not only allows hybrid web applications to be bundled and deployed on the app store, but also allows them to access native device features. Ionic applications can access these native device features through the use of Apache Cordova plugins.

Let's take the example of adding support for the native device clipboard to our Ionic application. This will allow us to copy and paste data to and from our application.

Create a new Ionic project from the command line with the platforms you wish to support. Then execute the following command from the command line:

ionic plugin add https://github.com/VersoSolutions/CordovaClipboard.git

The above command will add the clipboard plugin to the project for any platforms you wish to support.

Because Apache Cordova is not framework specific, many of the plugins don't always play nice out of the box with Angular, a critical component in Ionic applications. This is where Ionic Native comes into play. Ionic Native was designed to make it easier to use certain Apache Cordova plugins. There are hundreds of Apache Cordova plugins available, and while they will work with some fiddling, only a few are currently available as part of Ionic Native.

With the plugin added, open the project's app/pages/home/home.ts file and add the following:

import { Component } from "@angular/core";
import { NavController } from "ionic-angular";
import { Clipboard } from "ionic-native";

@Component({
    templateUrl: "build/pages/home/home.html"
})
export class HomePage {

    constructor(public navCtrl: NavController) { }

    public copy(value: string) {
        Clipboard.copy(value);
    }

}

Notice in the above the Clipboard component was imported from Ionic Native. A copy method was created for adding values to the clipboard, maybe on a button press or some other operation. Nothing particularly complicated was involved in using this native feature with Ionic Native.

Let's take it to the next level and say we want camera support in our application. First let's add the Apache Cordova plugin for camera support:

ionic plugin add cordova-plugin-camera

To use the camera with Ionic Native, import Camera similarly to how we did it for Clipboard. To use it we might have a method that looks like the following:

public capture() {
    Camera.getPicture({}).then((imageData) => {
        // Do something with the image data
    });
}

Pretty easy, right?

Now let's see what it would take to get this same clipboard example working in a NativeScript application.

Using Native Features with NativeScript

While NativeScript supports plugins, they are not Apache Cordova plugins. They can, however, be used in a similar fashion since many of the Apache Cordova plugin developers also develop plugins for NativeScript with the same APIs.

Let's take the clipboard plugin for example. Assuming you've got a NativeScript project created with the platforms you wish to support, execute the following command:

tns plugin add nativescript-clipboard

The above command will install the clipboard plugin for the added project platforms. With the plugin added, open the project's app/app.component.ts file and include the following code:

import { Component } from "@angular/core";
import * as Clipboard from "nativescript-clipboard";

@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
})
export class AppComponent {

    constructor() {}

    public copy(value: string) {
        Clipboard.setText(value)
    }

}

Much of the default template code was removed, but what matters is how the clipboard component is being imported. It is similar to how the import was done in Ionic. Then there is a copy method where the clipboard text is set.

Not too difficult or much different than Ionic, right?

Time to kick it up a notch with camera support in the NativeScript application.

Like with the clipboard plugin, the camera plugin needs to be imported. Within the TypeScript file, add the following line:

import * as Camera from "camera";

Hold on a minute! Why didn't we need to install the plugin like we did with the clipboard plugin or like we did with Ionic's camera plugin?

NativeScript handles native code differently than Apache Cordova, something we'll explore later in this guide. With that said, many features that you'd obtain via an Apache Cordova plugin are already available in NativeScript without having to download anything.

With the camera component imported in the NativeScript application, it can be used as follows:

public capture() {
    Camera.takePicture().then(function(picture) {
        // Do something with the picture
    });
}

Just like with Ionic, nothing was particularly difficult in getting support for native platform features. In fact, much of the process was the same. A current list of available NativeScript plugins can be found on NativeScript.rocks.

Now what happens if you want to store data on the device? We're going to take a look at data persistence technologies for both frameworks.


Data Persistence with Different Technologies

Data is a big thing in any application, web or mobile. There are many different ways to store data in an Ionic and NativeScript application. You can use a NoSQL approach to data storage with technologies like Couchbase and Firebase, you can use key-value storage with technologies like application storage and local storage, or you can use relational storage with SQLite.

There is no best solution to storage, it comes down to what your goals are. For this example we're going to focus on relational storage using SQLite.

Saving Data in Ionic

To use SQLite in Ionic Framework we're going to use an Apache Cordova plugin with Ionic Native. The goal here is to see how to read and write data that persists even when the application is closed.

To make things easy to follow, let's start with a fresh Ionic project. As a refresher, create a new project with both Android and iOS build platforms with these commands:

ionic start IonicProject blank
cd IonicProject
ionic platform add ios
ionic platform add android

The SQLite plugin for Ionic can be installed by executing the following in your Command Prompt or Terminal:

ionic plugin add cordova-sqlite-storage

With the plugin installed, open the project's app/pages/home/home.ts file and include the following code:

import { Component } from "@angular/core";
import { NavController } from "ionic-angular";
import { SQLite } from "ionic-native";

@Component({
    templateUrl: "build/pages/home/home.html"
})
export class HomePage {

    private db: SQLite;

    constructor(public navCtrl: NavController) {
        this.db = new SQLite();
        this.db.openDatabase({name: "data.db", location: "default"}).then(() => {
            this.db.executeSql("CREATE TABLE IF NOT EXISTS people (id INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT)", {})
                .then(this.load).then(result => {
                    // Do something with query results
                });
        });
    }

    private load() {
        return new Promise((resolve, reject) => {
            this.db.executeSql("SELECT * FROM people", []).then(data => {
                let people = [];
                if(data.rows.length > 0) {
                    for(var i = 0; i < data.rows.length; i++) {
                        people.push({firstname: data.rows.item(i).firstname, lastname: data.rows.item(i).lastname});
                    }
                }
                resolve(people);
            }, error => {
                reject(error);
            });
        });
    }

    public add(firstname: string, lastname: string) {
        this.db.executeSql("INSERT INTO people (firstname, lastname) VALUES (?, ?)", [firstname, lastname]);
    }

}

Let's break down the above code since it’s a lot to take in. We are importing SQLite from Ionic Native and creating a database if it doesn't already exist within the constructor method. When the database has been opened, we are creating a new database table only if it doesn't already exist. We are chaining promises here, but after the table has been created all data is loaded, at which point we can determine what to do with the data.

The load method is where we construct our SQL query for fetching all the available data. This is done asynchronously and a promise is returned as part of our chain. The add method will insert data into the database, in this case first name and last name data.

The differences between using SQLite in Ionic and NativeScript are minimal.

Saving Data in NativeScript

To use SQLite in a NativeScript application, we can use the SQLite plugin created by Nathanael Anderson. We are going to maintain the same goal that we did with Ionic.

Just like with Ionic we'll create a fresh project to work with. As a refresher, a NativeScript project with both iOS and Android platforms included, can be created like the following:

tns create NativeScriptProject --ng
cd NativeScriptProject
tns platform add ios
tns platform add android

The NativeScript SQLite plugin can be installed by executing the following from the Terminal or Command Prompt:

tns plugin add nativescript-sqlite

With the plugin installed, open the project's app/app.component.ts file and include the following TypeScript code:

import { Component } from "@angular/core";
import * as SQLite from "nativescript-sqlite";

@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
})
export class AppComponent {

    private db: any;

    constructor() {
        (new SQLite("my.db")).then(db => {
            this.db = db;
            this.db.execSQL("CREATE TABLE IF NOT EXISTS people (id INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT)")
                .then(this.load).then(result => {
                    // Do something with the data
                });
        });
    }

    private load() {
        return new Promise((resolve, reject) => {
            this.db.all("SELECT * FROM people", []).then(data => {
                let people = [];
                for(let row in data) {
                    people.push({firstname: data[row].firstname, lastname: data[row].lastname});
                }
                resolve(people);
            }, error => {
                reject(error);
            });
        });
    }

    public add(firstname: string, lastname: string) {
        this.db.execSql("INSERT INTO people (firstname, lastname) VALUES (?, ?)", [firstname, lastname]);
    }

}

There is a lot going on in the above NativeScript code, so it is best to break it down.

First we're importing the SQLite plugin that was installed. This is similar to what we saw in the Ionic Framework version. Inside the constructor method we open the database and create a new table if it doesn't already exist. Like with Ionic we are chaining promises, loading the data after the table creation completes. The way to query for data using the NativeScript plugin is a bit different than with Ionic Framework. You have to remember that these are two different plugin authors and we're looking at two different frameworks. However, the idea behind the query is pretty much the same.

Finally, we have an add method as previously seen for Ionic.

Using SQLite in Ionic and NativeScript is quite similar. Neither of the frameworks are easier or more difficult than the other when it comes to using a relational database.

What if we want to access native platform code that isn't part of a plugin? For example, what if there is a really awesome database library for Android and iOS, but there is no Apache Cordova or NativeScript plugin available? We're going to take a look at accessing native platform code.


Accessing Native Platform Code

Previously in the guide you saw how easy it was to access native platform features for both Ionic and NativeScript using plugins. What happens when faced with the scenario where the native features you need to use don't exist as part of the base framework or within a plugin?

Accessing Native Code in Ionic

Apache Cordova, by its nature, is unable to access native platform code without the use of a plugin. This means that Ionic is unable to access native platform code without an Apache Cordova plugin in the middle. We're going to work with what we've got and design an Apache Cordova plugin so we can use our own custom native code.

Max Lynch, CEO of Ionic, wrote a great tutorial for developing Apache Cordova plugins on Medium. It is going to be the basis behind this section of the guide.

It is best to start plugin creation with the base Apache Cordova plugin template. It can be obtained by cloning the repository using Git like follows:

git clone https://github.com/driftyco/cordova-plugin-template

It can also be downloaded directly.

The most important files are the plugin.xml and everything found in the www and src directories. Without going into detail on how Apache Cordova plugins work, we're going to create one, emphasizing the code involved.

We're going to leave everything as the default in the plugin.xml file. This means that our plugin will be able to be accessed as window.MyCordovaPlugin inside an actual application. The native Android source code will be found at src/android/com/example/MyCordovaPlugin.java and the native iOS source code will be found at src/ios/MyCordovaPlugin.m and src/ios/MyCordovaPlugin.h. These files and locations are per the default settings of this Apache Cordova template.

Open the plugin's src/android/com/example/MyCordovaPlugin.java file as we're going to make some changes. Drop into the execute method and add the following native Java code:

public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
    if(action.equals("getVersion")) {
        PackageManager packageManager = this.cordova.getActivity().getPackageManager();
        PackageInfo packageInfo = packageManager.getPackageInfo(this.cordova.getActivity().getPackageName(), packageManager.GET_META_DATA);
        callbackContext.success(packageInfo.versionCode);
    }
    return true;
}

Essentially we're saying we want to return the application version in Android. Much of the above code was taken from the Android documentation.

When it comes to iOS we need to visit two places since that is the Objective-C way of doing things. Let's start with the header file. Open the plugin's src/ios/MyCordovaPlugin.h file and include the following:

#import <Cordova/CDVPlugin.h>

@interface MyCordovaPlugin : CDVPlugin

- (void)getVersion:(CDVInvokedUrlCommand*)command;

@end

The above Objective-C header says we are going to have a getVersion function in the next file. Open the plugin's src/ios/MyCordovaPlugin.m file and include the following function:

- (void)getVersionCode:(CDVInvokedUrlCommand*)command
{
    NSString* version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:version];
    [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

We're not done yet. We need to declare in our plugin how to access these native iOS and Android functions from JavaScript.

The following needs to be added to the plugin's www/plugin.js file in the MyCordovaPlugin object:

getVersion: function(cb) {
    exec(cb, null, PLUGIN_NAME, "getVersion", []);
}

The plugin, as basic as it is, can be added to any Apache Cordova or Ionic Framework project through the following command:

cordova plugin add --link ~/path/to/plugin/cordova-plugin-template

Although the native code in the plugin will work fine for Ionic, it has not been optimized with an Angular wrapper like what Ionic Native does.

Want to save yourself some time when finding the application version in Apache Cordova? This plugin code was inspired by the already existing AppVersion plugin.

That was a lot to take in when it comes to checking the application version using the few lines of Android and iOS code in an Ionic application. So what would you do if you wanted to get the application version in a NativeScript application?

Accessing Native Code in NativeScript

As mentioned earlier, the NativeScript runtime which operates off V8 and JavaScriptCore injects everything Android and iOS for use within an application. What this means is you can use a variation of native Java and Objective-C in any TypeScript file of your application. By variation, NativeScript will JavaScript-ify the Objective-C to not use brackets or the other notations that aren't typically found in JavaScript.

Take a fresh NativeScript project for example. Open the project's app/app.component.ts file and include the following:

import { Component } from "@angular/core";
import * as Application from "application";
import * as Platform from "platform";

declare var android: any;
declare var java: any;
declare var NSBundle: any;

@Component({
    selector: "my-app",
    templateUrl: "app.component.html",
})
export class AppComponent {

    constructor() {
        console.log(this.getApplicationVersion());
    }

    private getApplicationVersion(): string {
        if(Platform.isAndroid) {
            let PackageManager = android.content.pm.PackageManager;
            let pkg = Application.android.context.getPackageManager().getPackageInfo(Application.android.context.getPackageName(), PackageManager.GET_META_DATA);
            return java.lang.Integer.toString(pkg.versionCode);
        } else {
            let version = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString");
            return version;
        }
    }

}

By importing application we can get the Android application context and by importing platform we can determine whether or not we're using Android or iOS. We have to declare a few things as any because both the Android and iOS native APIs don't have type definitions for TypeScript. Not a big deal as we're basically telling TypeScript to ignore the type. Finally we can use the native platform code to get the application version right in our Angular component.

Convenient being able to access native platform code from any file, right?

To be fair with the Apache Cordova equivalent, let's convert this native platform code into a NativeScript plugin. TJ VanToll wrote a great article on creating NativeScript plugins as did Nic Raboy on The Polyglot Developer. Create a new directory to represent our plugin somewhere on your computer and add the following files:

appversion.android.ts
appversion.ios.ts
appversion.d.ts
package.json

These TypeScript files will get compiled down to JavaScript, but NativeScript knows to look for Android code in *.android.js files and iOS code in *.ios.ts files. The *.d.ts file represents our TypeScript type definitions, and the package.json has the plugin name and any dependency information.

In the appversion.android.ts file add the following code:

import * as application from "application";

declare var android: any;
declare var java: any;

export class AppVersion {

    public constructor() { }

    public getApplicationVersion(): string {
        let PackageManager = android.content.pm.PackageManager;
        let pkg = application.android.context.getPackageManager().getPackageInfo(application.android.context.getPackageName(), PackageManager.GET_META_DATA);
        return java.lang.Integer.toString(pkg.versionCode);
    }

}

The above code looks pretty familiar, right? Can you guess what the iOS file will look like?

Open the plugin's appversion.ios.ts file and include the following code:

declare var NSBundle: any;

export class AppVersion {

    public constructor() { }

    public getApplicationVersion(): string {
        let version = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString");
        return version;
    }

}

This code was taken almost directly from the app/app.component.ts file that was shown earlier. We just split it into two files so NativeScript can choose for us.

To make this plugin TypeScript friendly, add the following type definitions to the plugin's appversion.d.ts file:

declare module "appversion" {

    export class AppVersion {
        getApplicationVersion(): string;
    }

}

To let NativeScript know information about plugin version information or naming, the package.json file should contain the following, or similar:

{
    "name": "nativescript-appversion",
    "version": "1.0.0",
    "description": "",
    "main": "appversion.js",
    "nativescript": {
        "platforms": {
            "android": "2.0.0",
            "ios": "2.0.0"
        }
    },
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "author": "Nic Raboy",
    "license": "ISC",
    "dependencies": {
        "typescript": "^1.8.10"
    }
}

The plugin, at this point, can be added to your project like follows:

tns plugin add /path/to/plugin/appversion

Much of this NativeScript plugin code was taken directly from Nic Raboy's Polyglot Developer article on the subject.

It doesn't really matter how you choose to access native platform code within your application as both will work fine. Because NativeScript JavaScript-ifies iOS, the need to understand Objective-C headers and syntax is eliminated. As long as you know the APIs you wish to use for either Android or iOS, you'll do fine. There is no need to create any plugin wrappers like with Apache Cordova.

What if you've introduced an error into your application at some point along the way in this guide. We're going to take a look at troubleshooting an Ionic and NativeScript application through the CLI.


Troubleshooting a Native and Hybrid App

The development process for mobile applications, hybrid or not, isn't always smooth. Everyone will encounter bugs at some point, but how does one troubleshoot those bugs?

Both Ionic and NativeScript provide CLI functionality for finding errors during application runtime. To top this off, both Apple and Google provide ways to find such logs.

Debugging with the Ionic CLI

Ionic developers tend to test their applications the most using Ionic's live-reload features. As a refresher, create a new Ionic project with the following:

ionic start IonicProject blank
cd IonicProject
ionic platform add ios
ionic platform add android

With the fresh project created, execute the following:

ionic emulate [platform] --livereload --consolelogs --serverlogs

Remember to swap out the [platform] with the platform you wish to use. The caveat here is that some extra things must be done to live-reload with iOS.

Let's assume iOS for this particular example.

Starting with iOS 9, Apple introduced developers to what is called App Transport Security. This was an effort to protect the end user from malicious attacks. By default, iOS blocks all server requests. Since Ionic live-reload is local server based, it is blocked. To fix this, change the project's platforms/ios/[ProjectName]/[ProjectName]-Info.plist. Of course [ProjectName] needs to be swapped with the actual name of the project. Open this file and include the following lines:

<key>NSAppTransportSecurity</key>  
<dict>  
    <key>NSAllowsArbitraryLoads</key>
    <true />  
</dict>

The next thing that must be done is alter the project's config.xml file. Include the following lines of markup:

<access origin="*" />
<allow-intent href="*" />
<allow-navigation href="*" />

Whew, that was a lot just to get live-reload working for the iOS platform.

Start the live-reload process and the application should work fine since no errors have been introduced. This is where we introduce some errors to be picked up by the Command Prompt or Terminal to help us troubleshoot.

Let's try to use Ionic Native's camera component, but without first installing the required Apache Cordova camera plugin. Remember, Ionic Native doesn't do anything native, it only wraps Apache Cordova plugins that must be downloaded separately.

Open the project's app/pages/home/home.ts file and include the following import:

import { Camera } from "ionic-native";

Inside the constructor method let's try to use the camera. Add the following chunk of code, and yes I realize that it is a bit incomplete, but since it won't run regardless, it doesn't matter:

Camera.getPicture({}).then((result) => {
    console.log(result);
});

An error should appear in the Terminal or Command Prompt that looks like the following:

2     597343   warn     Native: tried calling Camera.getPicture, but the Camera plugin is not installed.
3     597344   warn     Install the Camera plugin: 'ionic plugin add cordova-plugin-camera'
4     597347   group    EXCEPTION: Error: Uncaught (in promise): plugin_not_installed
5     597348   error    EXCEPTION: Error: Uncaught (in promise): plugin_not_installed
6     597348   error    STACKTRACE:
7     597349   error    resolvePromise@http://localhost:8100/build/js/zone.js:538:41
resolvePromise@http://localhost:8100/build/js/zone.js:523:32
http://localhost:8100/build/js/zone.js:571:32
invokeTask@http://localhost:8100/build/js/zone.js:356:43
onInvokeTask@http://localhost:8100/build/js/app.bundle.js:37126:51
invokeTask@http://localhost:8100/build/js/zone.js:355:55
runTask@http://localhost:8100/build/js/zone.js:256:58
drainMicroTaskQueue@http://localhost:8100/build/js/zone.js:474:43
invoke@http://localhost:8100/build/js/zone.js:426:41
dispatchEvent@[native code]
touchEnd@http://localhost:8100/build/js/app.bundle.js:72686:44
[native code]

That information is useful to use when narrowing down the source of our problem. Maybe you didn't realize you need to install the Apache Cordova plugin.

If you don't want to use live-reload, you could also use Xcode or Android's ADB for finding the error logs to help you troubleshoot.

Debugging with the NativeScript CLI

Like Ionic developers, NativeScript developers also tend to use the live-reload features when testing their application. Create a fresh NativeScript Angular project and start it with the following:

tns livesync [platform] --emulator --watch

Remember to swap out [platform] with the platform you wish to build for. The huge difference here is that you don't need to go through the extra steps for iOS done in Ionic Framework to get your application working. This is a huge time saver.

Because everything about NativeScript is native, we won't run into the same issues when using the platform camera. Since we're using TypeScript, we cannot introduce the simple errors that we can introduce into vanilla JavaScript. This holds true for Ionic as well. So instead of forcing the code to blow up, we're going to add some troubleshooting output.

Inside the fresh project's app/app.component.ts file there should be an onTap method. Make it look like the following:

public onTap() {
    console.log("MADE IT");
    this.counter--;
}

Notice the print statement. As of right now the project is live-syncing so when a save is done, the NativeScript CLI will redeploy the application. Tap the button on the screen and look at the Command Prompt or Terminal. The output text should be added to the console.

NativeScript Terminal Troubleshooting

While the process of finding the application logs is the same between the two frameworks, the configuration between the two is more time consuming in Ionic.

There are other debugging features available when it comes to NativeScript development. For example, the NativeScript debugger, allows you to set breakpoints to inspect your code more thoroughly. There is also a NativeScript extension for Visual Studio Code to help make debugging easier.


Unit Testing an Angular Application

When it comes to testing an application, doing so against Angular code was never particularly difficult. With testing frameworks like Jasmine, Mocha, and QUnit available, there are a lot of options when it comes to testing.

Adding Tests to an Ionic Application

While Ionic can make use of Jasmine, Mocha, and QUnit, the bootstrapping and setup process takes some time. Since Ionic doesn't have testing built into the CLI, the process is more manual. There is no best testing framework, more a matter of preference. For this section we're going to be using Jasmine.

Assuming you've already created an Ionic project, include the following dependencies by executing:

npm install karma browserify browserify-istanbul karma-browserify isparta jasmine-core --save-dev

To use Karma, a configuration file must be created. A wizard for creating a Karma configuration can be accessed by executing:

karma init karma.conf.js

Choose Jasmine as the testing framework and make sure app/**/*.spec.ts is the pattern for files to be considered test scenarios.

A few more things must be done before tests can be executed. Open the karma.config.js file that was created by the Karma CLI and make sure it looks similar to the following:

module.exports = function(config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine', 'browserify'],
        files: [
            'node_modules/es6-shim/es6-shim.js',
            'node_modules/reflect-metadata/Reflect.js',
            'node_modules/zone.js/dist/zone.js',
            'node_modules/zone.js/dist/jasmine-patch.js',
            'node_modules/zone.js/dist/async-test.js',
            'node_modules/zone.js/dist/fake-async-test.js',
            'app/**/*.spec.ts'
        ],
        exclude: [],
        preprocessors: {
            '**/*.ts': ['browserify']
        },
        browserify: {
            debug: true,
            transform: [
                ['browserify-istanbul', {
                    instrumenter: require('isparta'),
                    ignore: ['**/*.spec.ts','**/*.d.ts'],
                }]
            ],
            plugin: [
                ['tsify']
            ]
        },
        reporters: ['progress'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        browsers: ['Chrome'],
        singleRun: true,
        concurrency: Infinity
    })
}

Finally Karma needs a way to run. Inside the project's package.json file and include:

"scripts": {
    "test": "karma start karma.conf.js"
}

The same bootstrapping needs to be done for every project that you create.

For actual testing, create a file in the Ionic project at app/pages/home/home.spec.ts since it will be responsible for testing the project's app/pages/home/home.ts file. In that file include:

import { describe, it, expect, beforeEachProviders, inject } from "@angular/core/testing";
import { HomePage } from "./home";

describe("HomePage tests", () => {
    it("Test the message", () => {
        let page = new HomePage(null);
        expect(page.message).toBe("This is a test");
    });
});

The above test references things that don't yet exist in the HomePage class. Let's go ahead and create something in the HomePage class to make it consistent to what we'll see in NativeScript. Open the project's app/pages/home/home.ts file and include the following within the HomePage class:

public message: string = "This is a test";

To run the test, execute npm run test from the Command Prompt or Terminal.

Much, not all, of the Ionic unit testing setup and code was taken from Josh Morony's unit testing article. Josh is a reputable developer in the Ionic space.

Adding Tests to a NativeScript Application

The NativeScript CLI makes testing an Angular mobile application significantly easier.

Provided you've already created an Angular project for NativeScript, execute the following from the Command Prompt or Terminal:

tns test init

The above command will ask you which testing framework you want to use.

Add NativeScript Test Framework

Again, there is no best framework, it is more a matter of preference, but I'll be referring to Jasmine. After selecting Jasmine, the CLI will create app/tests/example.js in your NativeScript project.

The example.js file is nothing more than an example. In fact, ignore it and create app/tests/app.component.js. As you can probably guess, this test file will contain the tests for the project's app/app.component.ts file. You can however, name the test files whatever you want.

You'll notice the use of JavaScript for the tests rather than TypeScript. You can use TypeScript, but as of right now, the NativeScript CLI will generate JavaScript.

The base NativeScript template has an AppComponent class with a public numeric variable that is defaulted to 16. There is also a getter method for providing a message with the current count included. Based on this information, check out the following Jasmine test:

var reflect = require("reflect-metadata");
var component = require("../app.component");

describe("Tests for app/app.component.ts", function() {
    it("Verify default message", function() {
        var appComponent = new component.AppComponent();
        expect(appComponent.message).toBe("16 taps left");
    });
});

To test an Angular component that contains Angular decorators, the reflect-metadata library must be included. There is only one test in this file, but in this case the component is initialized and the default message value is compared against an expected message value. While not the most complex test in the world, it is still valid.

To run all tests found in the app/tests directory, execute:

tns test [platform]

Substitute [platform] with the platform you wish to test for. Everything should pass in this simple example.

NativeScript Execute iOS Tests

Try changing the expected message and run it again. The test should fail and give you information about why it failed.

More information on testing a NativeScript application can be found in the NativeScript documentation.

It should be pretty obvious how much easier NativeScript made it to create unit tests for an iOS and Android application.


Appendix

Where to Find Help

There are many ways to get help with NativeScript development. You can reach out to @NativeScript on Twitter or you can join the community Slack channel. However, the best way to get help is through Stack Overflow.

There are plenty of resources for getting started with NativeScript, particularly designed by community members. Examples of these resources include:

Meet the Author and Editors

I develop mobile applications, web applications, and games and am an advocate for modern development technologies and frameworks. I do not work for Progress Software, the parent company of Telerik, nor do I work for any of the other companies mentioned in this guide that produce cross platform mobile application frameworks.

Nic Raboy (@nraboy) - August 2016

We work at Progress Software as developer advocates and we edited this guide. At Progress Software we have a long history of building tooling for both hybrid and native applications. Both approaches have their place in the development world, but as projects like NativeScript and React Native have matured, we’ve seen a growing trend in developers looking to make the switch from hybrid to more performant native solutions. We hope this guide helps developers considering that migration.

Burke Holland (@burkeholland) & TJ VanToll (@tjvantoll) - September 2016