Mobile applications continue to be the hottest topic in software development. If you're a Web dev you've probably noticed that there's an over-emphasis these days on building native mobile applications, even when you have the ability to build rich Web applications that work great in mobile browsers. Browser technology has improved steadily to make it quite possible to build rich mobile Web applications relatively easily even though there are some limitations when it comes to device integration. Yet the reality is that even if you can build a rich Web application, mobile users - especially the non-technical ones - tend to look for apps first and expect applications to come from an app store.
Building native mobile applications can be very painful as you have to support multiple mobile platforms. At the very least, mobile apps have to support the iOS and Android platforms and perhaps also Windows Phone as a distant third platform. The problem with native development is that you have to build for each platform specifically using the respective SDKs and languages for each platform. In essence, it means two or three or more applications have to be built and maintained. When I talk to developers that aren't doing mobile yet, the biggest concern and confusion I hear is addressing this multi-platform world.
Say Hello to Apache Cordova
Apache Cordova, formerly known as PhoneGap (see Cordova and PhoneGap Sidebar), offers cross-over technology that lets you use standard Web technologies to build mobile apps. Rather than creating native applications that are targeted specifically for each mobile device platform, Cordova lets you use HTML5, CSS, and JavaScript to build mobile apps that can be targeted at all device platforms simultaneously and can be deployed into mobile app stores.
Microsoft recently released a preview release of the Visual Studio Tools for Apache Cordova that provides excellent integration for building Cordova applications. In this article, I'll give an overview of Cordova and the Visual Studio integration, as well as providing an example to walk you through the creation of a Cordova app by turning an existing mobile aware Web browser application into a Cordova app.
Cordova lets you use HTML5, CSS, and JavaScript to build mobile apps that can be deployed in mobile app stores.
How Cordova Works
Cordova works by providing a native device application that hosts a full-screen Web browser control. As a developer, you implement the Web interface using the same Web technologies you use for Web development: HTML5, CSS, and JavaScript, as well as any support libraries and frameworks you might be using. Your Web application then runs inside of this WebView
control on the mobile device, which effectively lets you build HTML5 based mobile applications.
The Cordova host container provides the resources required for the host OS to load and bring up the app on the device. This includes core settings like the app name and operating modes supported (orientation, sizing, etc.) as well resources like the splash screen and home screen icons. These settings and resources along with your application's HTML, scripts, images, and other resources are packaged into a single executable package that can be deployed to an app store.
When the app is started, this shell initially handles interaction with the mobile OS to display the splash screen and startup of the host container. Control is then transferred into the WebView that hosts your HTML-based application.
Cordova Web apps are plain HTML5 applications that typically launch an index.html
page. The only difference is that you also load a generated cordova.js
script file on startup. This file is dynamically created for each device platform by Cordova when it builds your project and is injected at runtime. Cordova.js
provides the interface to internal features of Cordova, the most important of which is the Cordova plug-in manager.
Plug-ins let you extend your Cordova apps beyond the very limited and inconsistent HTML5 mobile features, so you can properly integrate with mobile device features and APIs. Plug-ins access native APIs and expose native functionality as JavaScript APIs that can then be accessed by your application. Hundreds of plug-ins are available to access many native features of devices. For example, there are common plug-ins for accessing hardware features like the camera, camera roll, the microphone, the accelerometer, and so on. But there are also plug-ins to access software APIs like the Contacts list or sending SMS messages. Most plug-ins support multiple platforms and provide a single API that works on all of them, but there are also some plug-ins that are device-specific because they address a device-specific need or because the author didn't implement for other devices.
A large open repository of plug-ins allows Cordova to access native mobile device features and APIs.
Figure 1 shows how Cordova's platform integrates HTML5 Web applications with the mobile device's platform.
One of the advantages of Cordova is that you can have a single codebase that targets all mobile platforms
Visual Studio and Cordova
Microsoft recently released a preview of the Visual Studio Tools for Apache Cordova, which is an add-in package for Visual Studio 2013 (or it comes built-in with Visual Studio 2015 Preview) that allows you build Cordova applications right out of the familiar Visual Studio environment. Microsoft packages the Cordova tools along with a single point installer that installs all of the required platform SDKs for Android, and the various Windows Platforms as well as the tools required for iOS development on the Mac.
Although Microsoft is not the first to provide this sort of integration tooling, it provides a very comprehensive toolset and a pretty amazing development and debugging experience. For me, the highlight of the tooling is the ability to remotely debug a running device, including support of debugging Apple devices via an attached Mac for iOS. You can debug right inside of Visual Studio with access to the JavaScript console and debugger to step through code.
I've played around with Cordova a bit in the past, but I've never really felt the time was right to jump in, primarily because it was incredibly difficult to debug running applications on a device or even on an emulator. With these tools, Visual Studio makes it much easier to see what's going on in a running application, so that you can productively work in a live environment.
The Visual Studio Tools for Apache Cordova provide the following:
- Installation of all necessary SDKs for Windows Phone and Android on Windows
- An iOS build and debugging agent that interfaces with XCode's command line tools
- (a Mac is required to build and debug)
- A startup template that sets up a multi-platform project (iOS/Android/Windows Phone)
- Customized platform configuration integration
- A host of Emulators you can run on Windows
- Full DOM and CSS Viewer for live applications both in emulators and on devices
- Full JavaScript Console and Debugger using the Visual Studio debugger UI
This is a sophisticated set of tools that provides a top-notch developer experience that integrates the entire development workflow from writing HTML, CSS, and JavaScript code, to testing and debugging executing code on the device. And - maybe somewhat surprisingly for Microsoft - the cross-platform aspect of these tools is excellent, with nice support for iOS and Android devices that ironically work better in this Preview CTP than the Windows Phone support.
You can pick up the Visual Studio Tools for Apache Cordova either by downloading an add-in package for Visual Studio 2013, or by installing the Visual Studio 2015 Preview, which includes support for these tools natively. Go to: http://tinyurl.com/ptgkz6k for more info and download links. (Editor's Note: This link now points to the latest version of Visual Studio.)
The Visual Studio Cordova Tools provide a comprehensive toolset and an excellent development and debugging experience.
Getting Started
After you've installed the Cordova tools, you get a few new options in the Visual Studio environment to build a new application. To get started, create a new Solution or Add New Project to an existing solution. Figure 2 shows the Add New Project dialog in Visual Studio.
When Visual Studio creates a new Cordova project, it creates a standard Cordova project template. Figure 3 shows the result from the New Project template. There are a lot of components in a Cordova project and the project template puts them all into the right places. All you have to do is fill in your custom HTML content for your application.
The template includes index.html plus a few standard static folders where you can store application resources like css, images, and so on. You can create your own folders as well, but Cordova actually flattens these when it builds your project.
The project also includes a number of subfolders that contain Cordova-specific content and resources, such as icons, splash screen images, and configuration settings for each of the different platforms. These files need to be changed to your application images/resources for each of the platforms, which is quite a chore as there are a lot of combinations of image sizes.
When you create a new project, you should just be able to hit the Run button from the Visual Studio shell and run the project in a few of the emulators without any configuration whatsoever. The Ripple Emulator is a browser plug-in that simulates screen dimensions of a host of mobile devices and provides basic access to the Cordova plug-in and system functions so your application can just run in the browser using your standard developer tools to debug the application. Besides dimensioning the browser window and showing a device mock-up, Ripple also better emulates touch operations and provides a host of device simulations like shaking, tilting, network and battery status settings and presetting geo-location values simulating a GPS. It's a quick way to test your application without running through an actual device or emulator that works great if you just need to work out your application layout or non-device-specific code issues.
In Figure 4, I show a target platform selection of iOS and all the device options available. If I switch to Android, I get a different set of devices and emulators. Note the local device and remote device options that are used to run on the actual device. For iOS, remote devices run on an attached device on a Mac. Local Device can be used when you're running on a Mac in Parallels or other VM.
As you can see, there are a host of different emulators, simulators, and live devices. Some of these require configuration and I'll come back to that later. For now, choose any of the Ripple emulators to fire up the blank project and just see the application come up running under Cordova.
Building a Cordova Application
A Cordova application is essentially a Web application, which means that you have an index.html
page that kicks off the HTML rendering and you then reference additional resources and scripts from that page. Your application logic is implemented using JavaScript and, for the most part, a Cordova application behaves very similar to the way a Single Page Application (SPA) works on the Web.
The big difference between a SPA and Codova is that in Cordova, you should try to keep as much of the related resources for the application in local folders so that the application can run locally without requiring an Internet connection.
The big difference is that you should try to keep as much of the related resources for the application in local folders so that the application can run locally without requiring an Internet connection. Of course, if your application requires data from the Web, you'll need to hook up your AJAX calls for remote access at some point, but as far as “pages” of your application go, the idea is to build a local application that runs locally from the device.
For a quick test to see Cordova working I'll create a HelloWorld application/page first, so you can see what's involved in getting an app up and running and onto a phone to execute. Then I'll look at a little more full-featured Album Viewer sample to demonstrate a small Angular SPA application that can run locally on the phone and demonstrate some of the things you need to watch out for when taking an existing Web application and moving it to a Cordova phone app. (Hint: Not much!)
Let's start with Index.html
, which is the application's start page. It's just an HTML page, but it has to contain a couple of script references to cordova.js
and platformoverrides.js
. The rest of the page is merely your initial page HTML layout plus whatever script references to bootstrap your typical HTML application. For example, for an Angular SPA, you'd add the Angular references and links to all of your app.js, controllers, and services, etc.
For now, let's create a dead-simple HelloWorld page that lets you type your name into a textbox and have it immediately echoed back in the Message area below it:
<body>
<h1>Hello Cordova</h1>
<input id="Name"/>
<div id="Message"></div>
<script src="cordova.js"></script>
<script src="scripts/platformOverrides.js"></script>
<script src="scripts/index.js"></script>
</body>
Cordova.js
and platformOverrides.js
are “injected” into the project when it's built and they contain platform-specific interfaces to interact with the WebView interface and provide the services to expose plug-ins. Most integration in Cordova is provided via the plug-in interface rather than directly into either of these files, but they provide core bindings to the WebView and underlying mobile platform. The plug-ins handle the actual interactions with the hosting platform. If your application is a plain HTML application, you can ignore all the platform-specific stuff and it will all just work. Only when you need special integration with device-specific components do you need to access and program against plug-ins.
The index.js
page provides a few event handlers for custom events set up by Cordova to handle application startup, pause, and resume events. Listing 1 shows what index.js
looks like.
Listing 1: The index.js handles Cordova startup events and startup code.
(function () {
"use strict";
document.addEventListener( 'deviceready', onDeviceReady.bind( this ), false );
function onDeviceReady() {
document.addEventListener( 'pause', onPause.bind( this ), false );
document.addEventListener( 'resume', onResume.bind( this ), false );
console.log("Device ready...");
start(); // User Code
};
function onPause() {
console.log("Application paused...");
};
function onResume() {
console.log("application resumed...");
};
function start() {
var name = document.getElementById("Name");
var message = document.getElementById("Message");
name.addEventListener("keyup", function() {
message.innerText = name.value;
});
}
} )();
I've hooked up a start()
function that hooks my initial application logic, which is merely hooking up an event handler to list to key up events in the name input box and echoing it back to screen as the text is typed. Note that you can add custom libraries to index.html, so if you wanted to use jQuery, or Angular, or whatever, you can do that and it will just work. You're not limited to raw HTML and JavaScript or, for that matter, hooking into onDeviceReady()
as I've done in Listing 1. An Angular application, for example, would internally manage loading the code in app.js
based on the ng-app
attribute in the HTML markup.
In short, it's all just HTML and works as you would expect HTML-based apps to behave in a typical Web application.
The debugging experience is awesome. You can debug a live app using a DOM inspector, Console and JavaScript debugger right inside of Visual Studio
Use the Ripple HTML-based Debugger
Now that the app is ready, let's run it. Select a Ripple browser from the Attach button dropdown and then click Attach to run the application.
This starts the Ripple browser. At the same time Visual Studio goes into debug mode and displays a DOM and CSS viewer, a JavaScript console and a JavaScript debugger that works in JavaScript code editor just like other project debuggers. Figure 5 shows all the pieces except the code debugger.
While the app is running, you can select elements in the DOM viewer and change values editor and cause the changes to appear in the emulator or an attached device. The DOM and CSS views are live and you can make changes in real time. Visual Studio uses the Internet Explorer Debugging Tools interface, yet I'm not running in Internet Explorer; I'm running Ripple in Chrome. Likewise, when I debug on a live device like iOS attached to a Mac, I can use these same developer tools, which is a pretty cool feature, again with a live connection to the running device's DOM.
You also have access to the JavaScript console that displays console output and lets you run console commands against the live page. The JavaScript debugger uses the standard Visual Studio debugging interface and you can set breakpoints in the Visual Studio JavaScript editor using the standard F9
key (or margin click). Visual Studio then breaks into the debugger when the code is hit. It's a really nice and natural debugging experience consistent with how debugging works for other project types. And now you can debug remote devices directly from Visual Studio. This is huge!
Although running on live devices and emulators can be enticing, I think you'll find that most work gets done using either the Ripple browser or even a straight Web browser running with a local Web Server (see the sidebar Use a local Web Server for Cordova Apps). Why? Using a browser is a lot faster than deploying to a device or a full emulator. Unless you need to fix device-specific issues or layout, running in a browser to test and build your code will save you a lot of time.
Running on Live Devices
In the past, when I've looked at Cordova, I always got turned off by the crappy integration and bad documentation when trying to get each device configured for development and attached to the computer. Although this process is still not quite automatic (except for Windows Phone, unsurprisingly), these tools install everything you need on Windows as part of the installer. This includes the Android and Windows Phone SDKs. For iOS devices, there's a Mac build agent that integrates with Visual Studio and allows you to build, deploy, and debug on an iOS device directly from Visual Studio. The process for the Mac configuration is a bit more manual and involves a bunch of command-line commands, but the documentation is clear enough to get the tools running even for a Mac-phobe like myself.
The Visual Studio Tools for Apache Cordova do a pretty good job of getting your environment mostly set up, but there is still a little more tweaking involved for each platform. Typically you have to enable a developer account, or put phones into developer mode so you can deploy your development builds for testing. See the Device Configuration sidebar for more information for each platform's configuration.
To build an app for iOS, a network-connected Mac is required.
Building the App for an iOS Device
For this article, I'm going to use iOS as my example platform and actual device deployment. I always think of iOS of the worst-case scenario for Windows development since you need to have a Mac and configure a Mac to provide the build process. It also doesn't hurt that my primary devices are iOS devices so I prefer building for those first.
You Need a Mac to Build
If you want to build apps for iOS and you want to test either on a live iOS device or even one of the real Apple simulators, you need a Mac as well as an Apple Developer account. You need to create a provisioning profile and the certificates to publish your app to your phone for development or to a live store. This process is a lot easier than it used to be and you can do most of it directly from within XCode (see the Device Configuration sidebar).
The good news is that you just need to configure the Mac once. After that you can mostly forget about it and just use Visual Studio to build and deploy your app to an iOS device connected to the Mac or the iOS simulator over a network.
Install Applications
The first thing you have to do is install the tooling on the Mac, which involves manual installation of a few applications and some command-line installations via NPM. The base documentation and links for this can be found here: http://tinyurl.com/q7rkuae.
Here's what you need to install to get started:
- XCode 6
- NodeJS (so that NPM is available)
- An active iOS Developer Program Account
The base documentation and links for this can be found here: http://msdn.microsoft.com/en-us/library/dn77155
Install the Build Agent and XCode Command Line Tools
The install instructions are pretty simple, actually. Open a command prompt on the Mac and run:
cd /usr/local
sudo npm install -g vs-mda-remote --user=Username
The installation uses NPM to install HomeBrew and the XCode 6 Command Line Tools first before installing the Visual Studio Remote Build Tools. Unfortunately for me, these simple instructions did not work; I saw failures trying to install HomeBrew and I had to go and manually install it (http://brew.sh). Once HomeBrew was installed, I re-ran the NPM install and was able to get the remainder of the tools installed.
You also need to make sure that XCode has a linked developer account, which you can do by starting XCode and then going to the XCode menu | Preferences | Accounts and linking a developer account. Follow the prompts to add your Apple developer account to the list of identities.
Once the tools are installed and configured, run the following from the Mac Terminal to start the build agent:
vs-mda-remote --secure false
Configure Visual Studio for the Build Agent
Now go into Visual Studio and enable the remote build tools from Tools | Options | Tools for Apache Cordova, as shown in Figure 6. You have to specify an IP Address or Machine name for the Mac (use iconfig from the Mac terminal to find it) as well as an optional PIN. Unless network security is an issue for you, I recommend that you don't use a PIN, as I found that I had to frequently reset it, which turned into a real pain. If you do want to use a PIN, you can run vs-mda-remote generateClientCert to generate a new PIN, which is displayed in the terminal window. When you press Done and you don't get an error, the connection was made. Otherwise, follow the directions in the error prompt to fix any connection issues. You may have to modify your firewall settings to allow access to port 3000.
Ready, Set, Run
In Visual Studio, set the platform dropdown to iOS, use the Attach dropdown, and select Remote Device. Now you're ready to run the app by clicking on the Attach
button to build, deploy, and debug to the iOS device. Make sure that the Mac is awake and running the remote agent from the terminal, and make sure that the iOS device (an iPhone 6 in my case) is connected via USB to the Mac. Also make sure that the iOS device is active and the lock screen is unlocked.
Auto-building an XCode Project
If you can see your Mac's screen, the terminal should show the build agent receiving a connection, copying files and building the project using the XCode command line tools. Behind the scenes, Visual Studio creates an XCode compliant project that's copied to the Mac where it's built and deployed to the device. The project lives in the USER/remote-builds folder and you can drill down and actually open the iOS project in XCode to fine-tune the project settings and behaviors. They will be overridden each time you build, but when you do your final builds for deployment, you can tweak the final build parameters more easily in XCode.
Opening the project in XCode can also be useful if, for whatever reason, you can't run the project on your device or the emulator when there's a build or deployment error on the Mac end. XCode will give you a little more information about build errors, so it's a little easier and much faster to troubleshoot from the Mac locally. Hopefully, this won't be necessary but it's a good option to have.
Running on the Device
If everything went well, the CordovaHelloWorld
app should now pop up on the device and it should work the same as it did in any of the other emulators. Figure 7 shows the app running on my iPhone.
If you look at Visual Studio, you should again see the developer tools popping up with the DOM and CSS editors and the JavaScript Console visible. Go into the live DOM editor and select the Message element and edit it by adding some styling:
<div id="Message" style="color: darkred"></div>
This change is immediately reflected in the running app on the phone.
Now stop the app and add the second and third lines below in the index.js
page in the .start()
function:
name.addEventListener("keyup", function () {
var ua = navigator.userAgent;
console.log(ua);
message.innerText = name.value;
});
This is the code snippet that handles the keyup
event and I'm going to set a break point on the first line by pressing F9
on the line or checking the breakpoint in the editor margin. Then, I'll restart the app again by clicking on Attach
. Figure 8 shows Visual Studio breaking into the debugger from the code on the iOS device.
The debugger stops and you get standard .NET debugger features, including hovering over variables to see values, and the watch window to inspect values. And look closely; notice the user agent. It's showing the iOS device.
In addition to the debugger, you can also use the console on the right to evaluate expressions and values and to see those displayed in the console output window. So you can do something like console.log(name.value) and see the text that the user entered, for example.
There are a few gotchas that you have to watch out for with the debugger. I hooked my debugger breakpoint to demonstrate to the keyup
event, rather than the startup code, which might have been more obvious. That's because the debugger doesn't immediately attach when the app starts and so a breakpoint in the deviceReady
event won't break in the debugger. There's some delay before the debugger connects and so it only works during later events. If you need to debug device startup code, you can trick the debugger by re-running the startup page (the one that loads cordova.js
). Simply put a link onto the page that can re-navigate to the index.htm page like this:
<a href="index.html">Reload Page</a>
Click that link after the page has loaded and now the deviceReady
event fires and the debugger stops on startup code.
This index page is obviously a silly demo example, but it demonstrates the basics of getting an application to run on an actual device from Visual Studio and being able to debug it live, which is impressive. Now let's look at a more functional example.
You can debug iOS apps running on a mobile device right from within Visual Studio
A More Involved Example
I don't have enough space to cover all aspects of mobile device application design, but I'd like to demonstrate moving an existing mobile-aware Web application to run on a mobile device. In the January/February 2015 issue, I wrote a small AlbumViewer sample application for the “A First Look at ASP.NET vNext” article. This application is a small Angular front-end talking to a vNext back-end to pull data.
For this Cordova example, I took this application and ported it over to run as a mobile device application, making a few changes to access the data locally rather than from a service. Although you can certainly access data from a service, it's easier for you to play with this application if the data is local to the device, rather than setting up a separate application and service to connect to.
I also made a few minor UI tweaks to optimize for pure mobile device usage. This involved removing menus and the default bootstrap navbar which, while usable, is not very mobile-friendly, and adding some additional top spacing to allow iOS devices to place the content below the device's status bar. Other than that, there were very few changes to this application to get it to run inside of Cordova.
In the following section, I'll go over the process to get this application onto the device. You can check it out as part of the sample solution at: https://github.com/RickStrahl/CordovaAlbumViewer. Figure 9 and Figure 10 show a few screen shots from the application on a live iPhone 6 and an iPad Mini 2 respectively to demonstrate the adaptive UI that changes based on the form factor used.
One of the nice aspects about mobile Web applications is that responsive Web design allows your app to run more easily in different orientations and form factors. Since the Web app was already responsive, this fluid layout carries over to the phone and tablet and the UI needed very little adjustment to support both phone and tablet, which demonstrates one of the big advantages of Web-based mobile apps. If you look at the Albums view in Figures 9 and 10, for example, you see the single-column layout on a phone and two or three-column layout on a tablet. Likewise, the Album detail is single column on the phone with a large picture, and on the tablet, the picture is presented as a sidebar column, giving the content some room to breathe. The layout just works, assuming you use some responsive design principles or a framework like Bootstrap that provides the basics for managing resizing layouts for you.
One of the strengths of Web technology with responsive design is that it can work easily for different device dimensions from one code-base
Switching to Local Data
The original application used data coming from a local Web Service where the app was running. Since this is supposed to be a mobile demo, I decided that I wanted to make it work without a connection or service that has to run, so I created a secondary Angular service that uses locally stored data from a pair of files: albums.js
and artists.js
and browser LocalStorage
. I made this change to the Web app before I copied the files into Cordova, because although working with Cordova is reasonably easy once you're set up, it's still easier to work with standard browsers and Web tools.
If possible, making data local rather than always pulling data from services is a good idea. Whether you use data stored on disk (for read only data), or use data that's stored in LocalStorage, local data works when no connection is available and provides a faster experience even when you do have a data connection. Some applications work very well with local offline data, rather than data coming from the cloud. Since this is a demo, I wanted to ensure that the application can run without external dependencies on a service that has to be provided. Local data lets you run the application on the device without having to set up additional components or applications.
To make this app work with local data instead of a Web service, I added a new Angular factory that uses local data and duplicates the interface of the original factory that retrieved the data from a service. By adding a flag into the application's startup, I could then specify which factory to use:
var app = angular
.module('app')
.controller('albumsController', albumsController);
if (!app.configuration.useLocalData)
albumsController.$inject = ['$scope','albumService'];
else
albumsController.$inject = ['$scope','albumServiceLocal'];
function albumsController($scope, albumService)
The configuration is set in the app.js
startup code where I can now easily switch the app between local data and Web data with a single value:
app.configuration = { useLocalData: true };
Note that remote XHR calls and access to external resources on the Internet is available to Cordova applications, so if you do need to, you can access a service that's fully supported. By default, Cordova apps have access to all domains (*) but you can also specify exactly what domains are allowed. These settings are configured via the property settings in the config.xml dialog in the Visual Studio Tools for Cordova or you can edit the config.xml
manually.
Moving the Application
Next is the obvious step of moving the Web application files into a new Cordova project. I started by first creating a new Cordova project and copying the existing html files from the Web project. Because Cordova Mobile apps are essentially Web applications, you can simply copy your existing folder structure as is. Related resources like images, css, script files, etc., are still found by a Cordova application in the same relative path structure, although the Cordova build process actually rearranges the file locations for you after the fact.
The main change you have to make is that your index.html
has to load cordova.js
explicitly and, optionally, index.js
, if you want to handle the deviceReady
, devicePause
and deviceResume
events.
Figure 1 shows the project structure of the app after the AlbumViewer application has been added to the Cordova project. The structure of the project looks pretty much like a standard Web project. Since this application is an Angular app, there wasn't even any startup code to hook up to index.js
. Angular manages its own startup bootstrapping based on the ng-app
directive and app.js
loading when the page loads into the browser, so there are no code changes required to hookup any sort of startup code for the application. It should just work!
Next, I simply tried running the application and deploying it directly to the iPhone and...it worked on the first try. The app came up and ran and even looked pretty decent. Not unexpected, of course, since the app was mobile-aware before, but still it's exciting to see the app just work on the first try!
On iOS especially, it's nice to see the app behave like an app rather than the terrible pinned Web browser experience that iOS provides, where pinned Web pages lose their state after you navigate away. A Cordova app behaves like a mobile app should, and leaves the last screen intact when you switch to another app and come back. Nice!
Adjusting the Layout for Mobile
The original app was designed with Bootstrap and meant to work with mobile first/responsive design to support desktop browsers down to mobile phones. It did so reasonably well, but once you are specifically focusing on mobile devices - and especially phones - you can optimize the UI a bit more.
Keep in mind that mobile device UX isn't just about small size. It's also about making sure that the UI is designed for and responds well to touch interactions and can manage to convey useful content in a small content area. Mobile design is an art and this sample application is by no means a great example of it, even though it's functional and works reasonably well for the mobile form factor.
When converting from the original Web app, the first thing that doesn't look right is the default Bootstrap navbar
with its terrible collapsing button UI. I threw out the button and replaced the top menu bar with plain buttons that use text and icons for larger resolutions and show only the icons on a phone. Although that's still not quite optimal, it looks and behaves a lot better, especially on phones, by not requiring a collapsing menu to open. Menus on mobile devices should be avoided like the plague. Instead use icons, or if more content is needed, slide-in panels that provide more options.
There are a number of other small tweaks I made specifically for mobile and phone operation that I didn't pay close attention to originally when the app was meant for a browser-based app. Once you know that people will use this app on mobile devices, your usage perspective changes slightly and so I ended up tweaking a few things like margins and font sizes to look more appropriate, especially for the phone form factor. I'm no UX designer, but even these small adjustments can have a big effect on appearance and usability. For professional mobile apps, it's a good idea to invest in having a professional Web designer go over your UI and clean up artifacts and usability issues or have it completely redesigned specifically for mobile. But if you don't have access or a budget for a good designer, using a UI framework like Bootstrap or a full mobile framework like Ionic or KendoUI goes a long way toward providing a decent looking and usable interface that works well with a mobile app.
Mobile UI Challenges
Building good HTML mobile UIs that feel like native interfaces is not a trivial task. There are many gotchas that you often have to fight with. The next few sections list a few things that I struggled with on this sample.
Form Inputs
Form input invariably feels very choppy with HTML compared to native form implementations. Part of this has to do with the funky resizing that occurs when the on-screen keyboard pops up, leaving a very limited amount of screen real estate for your input content. While orientation changes work reasonably quickly on most phones and can be handled with plain resize events in the browser, pop-up keyboards tend to have extended delays before the resize events fire in the browser resulting in a laggy UI that bounces around a lot before you can start typing. This is especially noticeable if you have fixed headers and footers that don't replace immediately and seem to bounce around the screen a bit before settling. In some cases the resize events don't fire at all and the UI appears stuck until the phone is flipped and flipped back again.
Form control input focus is also funky for HTML forms. The interface between the keyboard and input form controls seems laggy and there's a noticeable lag when navigating to new fields. Additionally, lack of auto-selection for fields in input forms can result in unnatural cursor behavior.
CSS Animation
CSS animations are nice and, especially in mobile applications, can add a touch of native feel to an application. However, I've found it really difficult to get transitions and transforms to work consistently, especially if you need to have multiple transitions or transformations interact with each other. Because HTML is dynamic and the transitions themselves affect the HTML content, it can be really difficult to predict when transitions end and a new one starts, which can result in transitions interacting badly with each other. For example, a slide out/slide in transition can go really wrong if the content sliding out takes up little space and leaves the viewport mostly empty while a new screen slides in. There are lots of edge cases to check for. Decide carefully whether you want to deal with this pain; transitions can add a nice touch, but they often also add a lot of extra complexity and require fine-tuning to get to work right. In the end, I decided to remove the screen transitions because when they didn't work, they really broke the flow and polish of the application.
Fix Mobile Click Delay: FastClick.js
For some mobile browsers - iOS in particular - there's a 300ms delay before touch events are registered as clicks, which has the effect of a very annoying delayed reaction that can make apps feel terribly sluggish. The problem is that mobile browsers distinguish between click and touch events, waiting for other gestures that might occur when you touch an element, such as a swipe or drag operation that can't be discerned until some time has passed. The latest versions of Android and also Windows Phone have gotten smarter and better at handling touch vs. clicks, but iOS still behaves terribly with clicks in browsers.
There are a number of ways around this, but Angular has an ng-touch module that helps for some things. I found it to be pretty unreliable; although it worked well for buttons and links, it didn't work well for clicks on other elements. For example in the app clicking on an album always required either a really long hold/click or two clicks before the album opened.
Another solution that's a bit more reliable is FastClick.js
library, which shortcuts all click handlers and fires them immediately as touch events. It assumes that every touch is a click, and lets you explicitly exclude items using a .needsclick CSS
class definition on the element. Using this library worked for both standard button/link clicks as well as non-button element clicks like the album selection, but you have to be careful: It can interfere with other controls that rely on click- or touch-mapping, like auto-complete controls or carousels.
The iOS StatusBar
When I first fired up the iOS application on my phone, I noticed that the phone's status bar (the carrier, signal, and battery indicator on top) overlaid my content. That's because iOS treats the status bar as part of the content area. Additionally, the default colors for the status bar are a dark color expecting a light background, instead of my blackish header bar.
The first issue of the status bar overlaying content is easy to fix with some additional CSS that extends the header slightly with some extra top padding to force the content down. It's an easy fix.
The second issue is trickier as the status bar isn't controlled directly by Cordova. Instead, you have to add a plug-in. There's a custom Cordova Statusbar plug-in that surfaces basic status bar display customization via a few configuration settings in config.xml
.
The config.xml is the project's configuration and when you double-click, you get access to various configuration property windows. In Figure 12, you can see the Plug-ins view, which allows you to install a few common plug-ins like the statusbar plug-in.
To actually change the status bar behavior, you now need to add some settings to the actual config.xml
file. To do this, select the file in the project, then right�click, select Open With…, and choose XML Editor. Then add the following preference keys:
<preference name="StatusBarOverlaysWebView" value="true" />
<preference name="StatusBarBackgroundColor" value="#535353" />
<preference name="StatusBarStyle" value="lightcontent" />
The config.xml
file holds a host of configuration options for Cordova itself as well as for plug-ins.
Set Up Splash Screens and Icons
Another important task is to set up Splash screens and icons for your applications. Each mobile platform has multiple icons and splash screens that need to be created for various resolutions. For iOS, there are 14 icon and seven Splash screen combinations. Yikes! That's time-consuming but an important part of the process. You want to make sure that users see your icon and/or splash screen properly and they need to match each size exactly.
In this app, the icon is simple line art. I used a large 512x512 image and then resized for each image size in Paint.NET, saving the image on top of the old file. Then undo, resize again, and save to a new file name. Even so, this process took a while to work through for 21 iOS images. Rinse and repeat for Android and Windows Phone and whatever other platform you choose to support.
Dealing with iOS Build Problems
The Visual Studio and Build Agent build process combined essentially create a new Cordova project on the Mac. The build agent then compiles the final build using the Cordova and XCode command-line tools and deploys it to the device. One side-effect of this process is that the build files are temporarily stored on the Mac and you can open the intermediary project in XCode.
If you run into problems with builds on iOS, you can open the project on the Mac using XCode. The location for these files is in the User/remote-builds folder. Drill down into the actual builds identified by a build number and the iOS platform folder, and there you can find the .xcodeProj
file that you can open in XCode. This gives you the option of building the project from within XCode, which gives you some additional information on errors and allows you to fix the problem more quickly from within XCode. If you're getting ready for a final build to deploy to the Apple App Store, you might also want to copy this project to a new location so that you have a permanent record of the final project. Note that you can modify the HTML and other assets in XCode as well, just make sure that you sync the changes back to your Visual Studio project.
Figure 12 shows XCode's configuration dialog. As you can see, there are many, many tunable options that you can set more easily from within XCode than the configuration files, so if you need to fine tune, XCode is the way to go.
Other Platforms
Cordova supports many mobile platforms but the Visual Studio Tools for Apache Cordova supports only Android and Windows Phone in addition to the iOS, as I've shown. You can also build Universal Windows applications that run on both Windows Phone and as Windows Store apps on the desktop. The tools promise the same experience I showed for iOS, but unfortunately, I was unable to test the debugging features for either my older Android 4.0 device (only Android 4.4 or later are supported for debugging) or my Nokia Windows Phone for which debugging is not yet supported in these CTP releases, per documentation. Ironically, iOS offered the best developer experience, although you'd probably have better luck with a new Android device.
I was, however, able to deploy both to my older Android device and the Windows Phone without a problem and the application ran without any special setup on either of the devices. On Windows Phone, there were a few hiccups related to links. I was also able to run and debug on a newer cheap Samsung Galaxy Pad 7. The Visual Studio Cordova Tools also install a new fast Hyper-V-based Android Emulator provided by Microsoft that works very well to test Android applications and that is considerably faster than the stock emulator shipped in the Android SDK. If you don't have an Android device to test on that's the emulator to use.
As has always been the case if you plan for supporting multiple platforms, nothing beats having a few devices to test on and it can be tricky to configure all of them and get your apps to run on them. My suggestion is focus on one platform at a time to avoid driving yourself crazy with gadget configuration hell.
For features and UI, each of the platforms requires a slight bit of tweaking. Neither Windows Phone nor Android needed the statusbar plug-in nor the extra header spacing, for example.
Windows Phone and Angular Routing
Windows Phone required some special configuration to deal with IE mobile's link fixups. Apparently Windows Phone browser considers hash-bang URLs with multiple segments suspect and marks them as unsecure:ms-appx:#/artist/2. This causes problems for Angular's navigation and a pop up dialog that tries to download an application to handle this URL moniker. There's a fix for this using a configuration override in app.js
:
app.config([
'$routeProvider',
'$compileProvider',
function($routeProvider,$compileProvider) {
... // route configurations
$compileProvider.aHrefSanitizationWhitelist(
/^\s*(https?|ftp|file|ms-appx):/);
}
It's interesting to see that of all the supported mobile platforms in the Visual Studio Cordovoa Tools, iOS seems to be the one that's working the best in this CTP release. But I expect the experience for Windows Phone and Android to improve, as newer versions of the tooling becomes available.
Summary
The Visual Studio Tools for Apache Cordova are a surprisingly well-implemented set of tools for building cross-platform mobile applications using pure Web technologies. Microsoft has done an excellent job integrating Cordova's features into Visual Studio. Visual Studio already provides a powerful and user-friendly HTML5 development and debugging environment and the Cordova Tools extend these features to mobile device development in a way that feels very natural to Visual Studio developers. The Cordova Tools are going to be an awesome toolset for building rich mobile applications using the Web technologies you already know and without leaving the familiar confines of Visual Studio. The developer experience for these tools is top notch.
There are still rough edges with some missing platform features in this CTP, but you can definitely see where Microsoft is heading with this technology. It looks like they've created a kick-ass tool to build Cordova applications with Visual Studio. If nothing else, it's a great way to get a Cordova project started, especially on a new device that doesn't already have any of the support frameworks installed.
It's ironic that iOS currently has the best integration of all the platforms supported and I salute Microsoft for making the process of building and debugging for iOS devices so smooth, even if you've had very limited exposure to Macs as I do. You can't get around having a Mac available on the network, but you can at least continue to write your code and debug right inside of Visual Studio.
It's exciting to see Microsoft supporting cross-platform development, using non-Microsoft platforms as equals next to Windows Phone and Windows Universal apps. It's a much needed kick in the ass that the Microsoft platform needs to become at least a contender in the mobile space again.
The Visual Studio Tools for Apache Cordova are currently in Preview CTP state with an expected release timed to the release of Visual Studio 2015 later this year. You can install these tools either as a Visual Studio add-in package for Visual Studio 2013 or by downloading the Visual Studio 2015 Preview. Go to http://tinyurl.com/ptgkz6k to download the tools and start building mobile apps. (Editor's Note, this link now points to the latest version of Visual Studio.)