Organizations and developers struggle with an interesting challenge. We no longer live in a single-platform, single-device, single-vendor world. We have multiple mobile platforms and we have multiple desktop platforms. To be relevant in today’s market, you need to target all of them when you’re building an app. Unfortunately, each platform has its own nuances. There are languages and frameworks to learn that are unique and specific to each platform. Even within a single vendor, targeting a phone operating system can be quite a different challenge than targeting desktops.
For the very first time in the history of computing, we can say that "write once, run everywhere" actually works. Yes, we heard that about Java, but we all know how that went. It was write once, test everywhere, and deliver poor experience everywhere.
Given this bad historical taste, I’m not surprised that many developers seem to discount Apache Cordova because it isn’t "native". That, I feel, is a serious mistake. Cordova is incredibly powerful, more powerful than native apps. Am I saying that Cordova is suitable for 100% scenarios and native is completely useless? Absolutely not. What I’m saying is that 98% of the apps you see and use every day could very well have been written using Cordova instead of native. In fact, you’d be surprised to know how many apps in app stores are written using Cordova. There are some very good reasons for this:
- Cordova apps can also selectively show parts of their UX using native code. For instance, a single drop-down could be native whereas other portions of the app could use HTML.
- Cordova apps can be updated on the fly without going through app stores. This is true as long as your app’s permissions don’t change. Now take this with a small grain of salt. If you’re targeting the public app store on iOS, you may run into some approval challenges if you abuse this power. However for enterprise deployment on iOS and Android in general, this is a very powerful technique. It’s especially powerful considering that enterprises desire control, 100% control, and they rarely have the patience to wait for Apple to take four weeks to approve or reject their changes.
- There are far more Web technology developers out there than there are C#, Swift, or Java developers. Let’s face it, the best platform is the one you actually know. And if you know Web-based technologies, you’re ready to target all major mobile platforms using Cordova.
In short, Cordova allows you to target multiple platforms with a single code base. It does allow you to use your existing Web technology skills and target mobile platforms, but there are a few things to learn before you can call yourself an expert Cordova developer.
- You have to learn Cordova basics, which I will cover in this article.
- Like any other Web-based project, you have to pick a good framework. I feel that Angular and TypeScript are very good choices. Because Cordova apps are built and deployed on a mobile device, certain things become more difficult. For instance, live-reload doesn’t work like you’d expect it to in a browser. You also have to do some additional things to make sure that TypeScript debugging works, along with sourcemaps. This will be the main focus of this article.
- Writing good UX using Cordova is a skill on its own. There are frameworks, such as Ionic, to help you here, but sometimes you may choose to write your own UX. Let me be honest, downloading the next-best Bootstrap theme and using it in a Cordova app isn’t the best idea. Frameworks such as Bootstrap can be too heavy for mobile UX. This topic won’t be a focus in this article.
Let’s get started. In the rest of this article, I’ll walk you through three main topics:
- Setting up your dev environment and what you need to follow this article.
- Writing a very simple Cordova app.
- Writing a Cordova app using TypeScript and Angular 2.
Setting Up Your Dev Environment
Because Cordova is cross-platform, you can use almost any operating system to follow this article. I prefer to use a Mac for one simple reason: On a Mac, I can target every platform, even Windows. In order to target iOS, I have to use a Mac. Yes, I know I am coloring between the lines here; there are some cloud-based services that allow you to get around this limitation. But if you’re going to do serious mobile development in today’s world, get yourself a Mac, and an Android, and an iOS physical device.
You can follow this article using a Linux or Windows computer as well. Just make sure that you have Node.js installed, and if targeting Android, go ahead and install Android Studio. Once you’ve installed Android Studio, create a virtual device using the emulator.
Writing a Cordova App
Writing a Cordova app is really simple. Assuming that you have Node.js installed, run the following:
sudo npm install -g cordova
Note that "sudo" is Mac’s equivalent of "Run as administrator".
Once you’ve installed Cordova, create a new Cordova project using this command:
cordova create myApp
Creating this project creates a folder like that shown in Figure 1.
Cordova allows Web-based code to interplay with native code. Native code sits under the plugins directory, and there are plugins available for most of the common stuff you may need to do. Your custom code resides under the www folder.
Let’s enhance the code a bit. The aim of this application is to write out the details of the device the application runs under. This is achieved by adding a plugin called Cordova-plugin-device. In order to add this plugin, open terminal (or command prompt) in the directory of your project, and issue the following command:
cordova plugin add cordova-device-plugin
You could test the application in a browser. The advantage of testing in a browser is that you get functionality such as live-reload, but the disadvantage is that you get fake native device capabilities. In order to run and test the application in a browser, issue the following commands:
cordova platform add browser cordova run browser
The UX of this application is very simple, so I’ll skip over running in the browser. I’ll add support for two other platforms, as shown here:
cordova platform add android cordova platform add ios
If you’re not on a Mac, skip iOS, or instead, add Windows.
It’s worth mentioning that your development environment needs to be already set up to use Cordova. For instance, if you wish to target Android systems, you need to have Android Studio installed. If you wish to target iOS, go ahead and install XCode and run it once. And if you wish to target Windows, your Windows computer should have Visual Studio installed with all the necessary pieces in place to create universal Windows apps.
Next, add some code. The purpose of this application is quite simple: It allows the user to press a button and get information about the device or emulator the application is running under. There are two parts to this code change. You need to change the UX to add a button, which is shown in Listing 1. This is just a button; as long as the button works, you can add it anywhere; I‘ve added this code under the default "device is ready" message.
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
Now, back in index.js. Locate the "bindEvents" function, and in that function, add the following line:
document.getElementById("populateDetailsButton") .addEventListener( "click", this.populateDeviceDetails);
This line of code subscribes to the "click" event of the populateDetailsButton, and calls a function called "populateDeviceDetails". The populateDeviceDetails function can be seen in Listing 2.
As can be seen from Listing 2, you’re using a global variable called "device". This variable is available to you because you’ve added the Cordova-plugin-device plugin. At this point, you can run your application using the following commands:
cordova run android cordova run iOS
You should see the applications running, as shown in Figure 2 and Figure 3.
Using Angular and TypeScript to Write Cordova Apps
Using them in Cordova raises two interesting challenges:
- And this transpilation step must understand sourcemaps so debugging in the device works.
Debugging in the device is especially interesting because file paths among iOS, Android, and Windows are different. So a hard-coded sourcemaps location doesn’t work.
Let’s address these challenges. The final code for this section can be found at https://github.com/maliksahil/cordovangts, or you can follow this article to build it step-by-step.
The first step is to create a Cordova project, which I assume you already know how to do.
Next, add a package.json. In this package.json, add the various dependencies for Angular2. Also add devdependencies for TypeScript and typings. You still need typings so that ES6 features, such as Promises, don’t throw TypeScript errors.
On your computer, go ahead and install JSPM globally using this command:
sudo npm install –g JSPM
Next, JSPM walks your code, looks at all the import statements, and bundles things appropriately. You can ask it to bundle sourcemaps also. And you can ask it to create a bundle that has no dependency on SystemJs at runtime. That, frankly, is quite incredible.
Between bundling and AOT, it’s not uncommon for your application to start up 10-15 times faster and run two-to-five times faster. Imagine the impact of that on mobile applications.
You need to give JSPM some hints. These go in both package.json and JSPM.config.js.
My package.json excerpt can be seen in Listing 3.
The JSPM.config.js file instructs JSPM, where in the node modules are the interesting bits. For instance, let’s consider zone.js. Zone.js is used by Angular, and its node_modules folder contains a bunch of files, such as the TypeScript source code and the source map. You don’t really care about that at runtime.
Also, you’re not going to bundle all of that with your application. For one, the application would be huge; the bigger issue perhaps, is that these node_modules prevent the Android build from succeeding.
You need to provide a list of packages and a map to JSPM. Providing the list of packages and map to JSPM is done via JSPM.config.js. This is exactly the same purpose as systemjs.config.js has at runtime. This is why JSPM and SystemJS go hand-in-hand.
If you were using Webpack, this section would be entirely different. That’s beyond the scope of this article, so I’ll leave it for a future article.
I suggest that you give the JSPM.config.js a look in the GitHub repo because it’s far too wordy to write out the whole thing here.
With these things in place, you can now write the Angular app that makes use of the Cordova plugin. So just like before, add the Cordova-plugin device.
Writing the Actual App
My app is quite simple. It has three components:
- The appComponent, which holds a router outlet and router links to About and Device.
- The About component, which acts as the home page of the app and informs the user what this app is all about.
- The Device component, which holds the logic of querying the device and writing out the details of the hardware you’re running the app on.
Besides that, there’s an AppModule.ts that’s the app’s module, and there‘s an app.routing.ts that sets up routing for the application. Because the focus of this article isn’t Angular, but rather, using Angular and TypeScript with Cordova, I won’t explain the basics of the application and will dive straight into the interesting bit, which is the device.component.ts file. This file can be seen in Listing 4.
Another handy advantage is Help and IntelliSense and refactoring over the project. You can get Cordova IntelliSense in TypeScript projects too. Here is how.
First, install typings globally.
sudo npm install –g typings
Next, search for typings for Cordova.
typings search cordova
Once you find the appropriate typing, install it.
typings install dt~cordova --global --save
Note that by doing so, you’re editing the typings.json file. Your package.json file was rigged to contain a "postinstall" action, which was the install typings. The next time you do npm install, it will update typings. This means that in a folder called typings typings puts the appropriate .d.ts files into the appropriate folder, and because they’re a part of your TypeScript project, and thanks to the jsconfig.json file, they’ll be picked up for IntelliSense purposes.
With all of this in place, you’ll now see full IntelliSense and Help for even Cordova plugins, as can be seen in Figure 4.
Now go ahead and run the application using npm start, which:
- Runs the Typescript transpiler.
- Does the bundling.
- Launches the application.
You should see the application running, as shown in Figure 5.
This is great! But you’ve seen the application running before. What’s different this time is that you can actually debug it in TypeScript, while it runs on a device or emulator. You can do so directly from Chrome when running Android, but let me show you the steps to debug directly from VSCode.
First, install the Cordova Tools extensions from the extension gallery.
Next, if you’re targeting iOS, install ios-webkit-debug-proxy. The easiest way to do this is using Homebrew.
brew install ideviceinstaller ios-webkit-debug-proxy
Once you’ve done this, go ahead and set a breakpoint in your code, and in VSCode, on the left hand side, look for the debug pane, the one that looks like a bug with an X through it. Launch either iOS or Android (remember to add iOS as a platform in your project first).
You’ll note that you’re able to debug TypeScript code running on an iPhone via VSCode. This is shown in Figure 6. Here’s the best part: Until very recently, Safari didn’t understand sourcemaps. Via VSCode, you could debug an iOS Cordova app and debug directly in TypeScript. Amazing! The newest version of Safari does understand sourcemaps.
I could hardly contain my excitement writing this article. A few years ago, when all my friends were writing iOS and Android apps, and had so many opportunities thrown at them, I was dangling around Silverlight. With Cordova, I feel so empowered, and I can use TypeScript and Angular and debug with the ease of well-put-together products.