When I was trying to find information about how to get started with mobile app development with PhoneGap and Angular, I had difficulties on finding a good step by step tutorial. This is the tutorial I wish I had:

You will learn how to:

Set up the development environment in OS X for development for iOS and Android.

Installing PhoneGap and emulators

To develop for iOS you just need Xcode installed (download from the App Store). If you didn’t have the command line tools you will also need them (xcode-select --install)

To develop for Android, you need to install Android SDK. It depends on Ant, so let’s install that first:

brew install ant
brew install android-sdk

You might need to add export ANDROID_HOME=/usr/local/opt/android-sdk to your ~/.bash_profile and run source ~/.bash_profile.

Now we can open the SDK with this command:

android

By default it will have selected the most necessary packages, so just go ahead and click the Install 12 packages… button. When finished installing it should look like this:

Now we have the necessary tools to create our first Android Virtual Device (AVD). I’m going to create the first one from command line, and later on I’ll create a second one with the help of the android app.

To create from command line, we need to know the target (Android version), and the available ABIs (hardware/CPU architecture). To do that run:

You will get a list of targets. Find the one with the latest Android version. Your target will be the id. Have a look to the related ABIs. Apparently most phones out there use the ARM architecture, so choose default/armeabi-v7a. Now you can run:

If you see the DEVICE IS READY text, means that everything is working so far.

Now let’s do the same for iOS. We need to install this two npms:

sudo npm install -g ios-deploy
sudo npm install -g ios-sim

And now let’s run the app in the emulator:

phonegap run ios

Testing in different devices

You probably want to test your app in different devices. To do that for iOS, you just need to go to the iOS Simulator > Hardware > Device and select your device.

If you need to test in different iOS versions, you need to download them from Xcode > Preferences > Downloads, and you will see the option when selecting your device.

For Android, you need to create new AVDs. Let’s do it this time from the android interface, which makes easier to select options:

android avd

Click New and fill up the details. Example:

Click OK.

Now if we want to use this AVD, we need to have it open.

Back in the list, elect the desired device and click Start.

You can close the avd window, but leave open the emulator. When you run

phonegap run android

The app should be deployed to the opened emulator

Testing in real devices

At some point you will also want to test your app in a real device.

To install your app in an iOS device, you need to be member of the Apple’s iOS Developer Program, which costs $99/year, so for now we will restrict our real life testing in an Android. To do that you just need to connect your device via USB, and enable USB debugging under Options > Developer Options. On Android 4.2 and newer, Developer options is hidden by default. To make it available, go to Settings > About phone and tap Build number seven times. Return to the previous screen to find Developer options. Then make sure you don’t have open any emulator, and run phonegap run android. The app should install and open in your device.

Creating the Angular app

Now that we have PhoneGap working with emulators, let’s create our AngularJS app. We will use Yeoman to help with the scaffolding, Grunt to help with automated task, and Bower to take care of front-end packages.

sudo npm install -g yo bower grunt

To scaffold AngularJS apps, Yeoman has a generator that we need to install too

sudo npm install -g generator-angular

Now in the current directory (where PhoneGap is) we can simply run:

yo angular

Would you like to use Sass (with Compass)? Yes
Would you like to include Bootstrap? No
Which modules would you like to include? // Deselect all of them.

We can install the modules when we need them with bower install angular-<module>. Now we have the source files of our angular app inside the folder app.

We can open that app in a browser with grunt:

grunt serve

And a new browser window should open with a content like this:

It looks like is without styles, but that’s normal because we didn’t include Bootstrap, and Yeoman relies on it for this welcome screen.

Now let’s try that in our emulators. Grunt will build the app by default in a dist folder, but we need it to be built in the www folder from Phonegap. For that we need to change the Gruntfile.js: replace the line

dist: 'dist'

with

dist: 'www'

And to avoid this folder to be pushed in github, open .gitignore and replace dist with www.

We also need to protect the config.xml file inside the www folder, or it would be deleted in the build process. For that, we move it to the app folder, and then in the Gruntfile.js we add it to the copy task under dist: > files: > src:

'config.xml',

And we will also protect the res folder, where the icons and splash screens for the different platforms are. Also move that folder to app and add to the copy task src list:

'res/*'

We should clean up the res folder and the config.xml and leave only the platforms that we are going to build for. We could also automate the creation of the icons with different sizes based on the same SVG source file using grunt rasterize, but for now let’s move on.

Building the app

We are ready to build the app from the source code to the files that will serve as source for building the different platforms. Run:

grunt build

and now you should get the files compiled and compressed in the www folder. Let’s test how it looks in our emulator:

phonegap run ios

Great! we managed to get our app running in the device.

Adding PhoneGap Functionality

Now let’s add some PhoneGap functionality to see if works.

First we need to add phonegap.js to our index file. Add below

<!-- build:js scripts/vendor.js -->

<script src="phonegap.js"></script>

This file will be generated by PhoneGap when building the app, so if we open the app in the server, we will get an 404 error. To avoid this simply add an empty phonegap.js file to the app folder.

Now we need to bootstrap angular but only when the device is ready. To do that we need to remove the ng-app="myApp" attribute from the body of the index.html file, so Angular doesn’t get started automatically, and then run it manually when the ‘deviceready’ event gets fired. If the app is run in the browser, we want to bootstrap Angular anyway so we get to see our code (‘deviceready’ event doesn’t fire in the browser). To do this add this code to your app.js file, below angular.module('myApp', []);:

Now the page should still be loading as it did before, in browser of if we build and run into the emulator.

Adding the Ionic Framework

Lets add Ionic framework. At the time of writing, the bower repository didn’t have the latest version 1.0 as default (probably because it was still in beta), so we would need to specify the version manually:

bower install ionic#1.0.0-beta.1 --save

If it prompts to choose the Angular version, choose the latest.

Now to see if it is working, let’s add a ionic directive to views/main.html. Replace the content for:

Before Angular can interpret this new directive we need to include ‘ionic’ as dependency in app.js:

angular
.module('myApp', [
'ionic'
]);

If we run grunt serve, we will see that the browser is rendering a header with the ionic styles.

Notice that because we are using bowerInstall in our Gruntfile.js, when we run grunt serve, the ionic css and js files get included in index.html automatically. You can check the index.html source code after running grunt serve.

If we try this code it won’t work as expected, because the success callback happens within PhoneGap framework and outside Angular framework. For the scope to work, we need to force Angular to evaluate it. To do that, we need to inject $rootScope and call $apply:

Now the app in the browser should give us some information (if you are in Chrome, make sure you accept the browser notification to track location):

And if we build and run in the emulators it should work too:

In Android, the emulator don’t read GPS values, so we need to send them via command line. We need to start a telnet session in the port that the emulator is running (you can check the port in the emulator window title, the number at the beginning, in my case 5554). Find here the complete list of commands:

telnet localhost 5554

and then run the command:

geo fix -122.4 37.78

After that you can do:

phonegap run android

and the location should work:

If you close the app you need to re-send the geolocation, so if it doesn’t work, just run the geo fix command just after opening the app, before the timeout event fires.

What about other platforms

Since we are using PhoneGap, same code base should work for all the supported platforms with very little modifications. For some platforms you need a specific operating system. For example for iOS you need OS X, and for Windows Phone 8 you need Windows. In my case since I’m in OS X to build for Windows Phone 8 I’d need to do it through virtualisation with VirtualBox.

The other alternative is using PhoneGap Build, which compiles the app for you in the cloud. To do that you can just run:

Jesús Carrera

Hi Jesús, nice post! It is the exact same framework stack that I’m currently using with some minor adjustments:

– I’m using the cordova tooling instead of the phonegap tooling. Phonegap brings you the extra mile using their build services, but it is slow compared to building on Mac/Windows, and it only supports a white-list of supported plugins. The list of plugins is growing, so this is good, but still… – I’m using Typescript (http://www.typescriptlang.org/) as programming language instead of plain Javascript. It is very close to Javascript (any Javascript IS Typescript) but it provides type safety and a lot of checks which prevents a lot of common errors. It compiles to plain Javascript that looks almost the same as your Typescript.

During development I work with a native Android device (I bought a Nexus 10 and upgraded it to Kitkat 4.4.2 – essential, I come to that later) because it is so much faster than the emulator interpreting arm code on an intel processor. You can improve speed by adding an x86 based emulator (also through AVD) and using Intel HAXM use latest version if working on Windows 8.1, otherwise your computer freezes from time to time). Another great (and fast) emulator is GenyMotion (http://www.genymotion.com/), but in the end nothing beats a native device.

Another thing we did was creating a bootstrapper.html page that is the entry point for the cordova/phonegap app, this bootstrapper unzip a http://www.zip file from the apk or other host application (app for iOS, xap for WP8) to another place on disk, and run the index.html as real starting point of the app from there. This allows for two things: 1. it is now possible tp check from the bootstrapper if there is a newer version of the app.zip file, and if there is one expand this package over our old code, in that way updating the actual application independent from the app store. This is similar to “hydration” in Phonegap build (http://docs.build.phonegap.com/en_US/tools_hydration.md.html#Hydration) or “liveUpdate” in the Intel XDK (http://docs.appmobi.com/index.php/live-update/), or “reload” provided by trigger.io (https://trigger.io/docs/current/recipes/reload.html). 2. It is now possible to have a kind of “live reload” of your current “web page” in your app on your device on saving code during development. I extended my grunt build script to support this. The latest version of the Intel XDK (http://xdk-software.intel.com/) does support this feature as well. Really nice, but the problem is that with Intel XDK you are stuck with their set of included plugins… From the grunt script we copy changed files (after all the required compilation for the Typescript code) using “adb push” to the device, and trigger a reload of the current page.

Last thing: Webstorm 8 (http://www.jetbrains.com/webstorm/whatsnew/) is a great tool for edting your application code. Great support for AngualrJS, Typescript Intellisense (although I do the compilation of the Typescript with grunt), and grunt support.

@jesus – Thanks for posting this great article. I really needed the angular bootstrap solution you presented. Works great!

@serge – Thanks for posting all the great information. I was unaware of the remote chrome debugging capability. That is so awesome. The other topics you mentioned are very interesting as well. Thanks again!

If you get stuck at the “device is ready” point and it just wont show, try changing your phonegap.js to cordova.js in your www/index.html worked for me, although I am still trying to figure out why, since I only installed phonegap, not cordova (also trying to figure out the difference nowadays since apache split them up and made what looks like an extended cordova by releasing phonegap)

Jesús Carrera

Hi! Thank you for the excellent tutorial. I´m having problems including Ionic in the app, could you explain a little more how this works -> “Notice that because we are using bowerInstall in our Gruntfile.js”? It is not working for me, the app doesn’t find ionic module.

Jesús Carrera

Magnus

I changed the dist: from ‘dist’ to ‘www’ at line 21 inside var appConfig={…} Is that correct? I also added ‘config.xml’ and ‘res/*’ to copy:dist:files:src: at line 338. But, “We also need to protect the config.xml file inside the www folder, or it would be deleted in the build process. For that, we move it to the app folder,…” My config file is located at the same folder level as the www folder, is that the correct one?

When I run grunt build, it runs two of the tasks but it does not go anywhere. I look in the www folder and its empty. Any idea?

Rutger

Thanks for this great tutorial, I’m having trouble after I install Ionic though… I can run the ‘grunt serve’ task without errors, but I notice that my ‘main.html’ view is not loading, the console shows no errors.

When I uninstall Ionic and remove the dependencies, ‘main.html’ loads again. What could be wrong?

ivo

I am following it using Cordova. One thing I found was that the cordova.js script include on the HTML page needs to go *before* the vendor.js tag.

If you place it after the vendor tag like you indicate the js minification step will remove the tag as it attempts to compress it into the final single js file. The cordova.js file though is not present during this minification step, it is supplied by the runtime environment so the tag needs to be present in the final html file.