Going Mobile: Wrapping an existing web application in Cordova/Phonegap

Going Mobile: Wrapping an existing web application in Cordova/Phonegap

There are many choices to build mobile applications, from native to the growing popularity of hybrid development, with everything in between. In all of these cases, as a developer you would be writing a brand new mobile application from scratch with the ultimate goal of deploying to one the app stores. Let’s say that a company has an existing web application and for reasons we will not debate here, management has decided that this web application is to be packaged and delivered as a mobile app available from the main app stores. How would you go about it from a development perspective and what would be some of the pitfalls and difficulties to take into account when engaged in this endeavor? This article will attempt to cover these and offer some solutions based on actual experience.

Design

The first and most crucial problem that you will have to resolve before doing anything is ensuring that:

  • Your web app is responsive
  • Your web app uses mobile friendly form controls

The second point is actually quite important if you’re using custom controls that will not be rendered natively by the mobile browser (I am looking at you Bootstrap dropdowns). You will have to scour your application on a mobile screen to ensure that the experience is consistent and frustration free for your target users. I emphasized target here because if your goal is to keep your application available through the web but still be available as a mobile application, I can almost guarantee that you will run into design issues where one platform will have to be favored over the other for optimal design so be prepared to make these choices when they come up.

Assuming that your design is now mobile ready. How do you go about wrapping into something that will feel native to your users?

Phonegap/Cordova and the InAppBrowser

Adobe PhoneGap is a distribution of Apache Cordova and Cordova is an open-source mobile development framework that allows you to use standard web technologies — HTML5, CSS3, and JavaScript for cross-platform mobile development. In simple words use standard web development tools to create and deploy mobile applications from a single codebase across different platforms. Phonegap offers some additional enterprise services for the development and deployment of your Cordova application but in essence you will always be working with Cordova under the hood.

What’s interesting about Cordova is that it can be extended using plugins which allow your app to use native device capabilities beyond what is available to pure web apps. That is how your standard HTML, CSS and Javascript codebase can hook into the phone’s camera or microphone to perform native functions, reinforcing the illusion of native to your app’s user. One of these plugins that can be made use of to wrap an existing application into a native app is the InAppBrowser who started as an independently developed plugin before being incorporated as a core Cordova plugin. It’s basically a child web browser that allows you to access third party web content. Wrapping an existing web application as a native application comes down at this point to loading said web application in an InAppBrowser window and managing the whole mobile app experience.

In practice, the loading is the easy part, with the managing of the mobile experience come the hard parts.

Cross Window Communication

Outside of just authentication, there will be many instances where you will find yourself wanting to communicate values between the wrapper app and the wrapped app in the inAppBrowser window. This great article by TJ VanToll lays the foundation for how to to just that even though it dates from 2013. Basically by making use of the inAppBrowser executeScript function, and using localStorage, you can exchange values between both windows. It’s not a standard way of operating but while waiting for Cordova to implement the window.postMessage specification in the InAppBrowser, this remains the most effective way of going about cross window communication.

Authentication and Session Management

If your application only consist off content that does not require authentication, you’re home free. Skip the rest of the article and enjoy yourself a cold drink. For the majority of cases the existing application is without a doubt functional only after authentication. With authentication also comes session management not only in the context of your application but also in the context of your user being on a mobile phone with the expected corresponding behavior. The following questions need to be answered

  • How long does the session last once the user is authenticated? In web applications, users are used to re-authenticating every time their session expire but on mobile, the expectation is to authenticate once and forget it, or use other less intrusive mechanisms like fingerprint reading or pin entering when security must be enforced. The takeaway is that you should not make the user have to enter a username and password every time they open the app if they really don’t have to.
  • Is your Forgot Password application flow mobile friendly? Does it require users to perform steps (Captcha) that are easy to do on a computer but would be more challenging on a mobile device? If not, you will have to adjust your flow to use more mobile friendly techniques (verification codes sent by SMS or Email).
  • What happens to your application when the user navigates away from it? Do not assume that your application will always be front and center, what happens when a call comes in and last longer than your session duration? What happens if the user sends the application to the background and then comes back to it a couple of days later? How would you know the state of the application at that time?

These questions show that even though the goal is to simply wrap an application in an hybrid app, to ensure an optimal mobile experience you will sometimes have to write additional code in the hybrid app to handle these different situations. For authentication and session management, the general idea is for also the hybrid wrapper to be aware of the authenticated and session state of your application.

Cookie Authentication

If your application uses cookies for authentication and session management and you don’t need to add an extra layer of security or convenience like fingerprint authentication, you will need to make sure that when opening the InAppBrowser window, you set the proper option for the window to remember cookies, otherwise if you kill the app and re-open it, your session cookies will be gone and the user will need to re-authenticate:

var ref = (url, target, options);

From the documentation:

options: Options for the InAppBrowser. Optional, defaulting to: location=yes. (String)

The options string must not contain any blank space, and each feature’s name/value pairs must be separated by a comma. Feature names are case insensitive. All platforms support the value below:

location: Set to yes or no to turn the InAppBrowser‘s location bar on or off.

clearcache: set to yes to have the browser’s cookie cache cleared before the new window is opened

clearsessioncache: set to yes to have the session cookie cache cleared before the new window is opened

The clearcache and clearsessioncache options are available on both Android and iOS and for clearsessioncache it should be set to no to ensure that your session cookies are remembered.

Token Authentication

If you’re using a token based authentication strategy and want to add fingerprint or pin-based authentication, you will need add extra code to the hybrid app to check for the state of the user when the app opens and present them with either a login screen or a fingerprint/pin reader. Refer to the Cross Window Communication section on how to communicate this state between the wrapper and wrapped apps.

External Links

A (nasty) little gotcha from using the inAppBrowser is that clicking on a link to external content, while you are in a inAppBrowser window will load that content in the inAppBrowser window you are using which leads to the following scenario:

  • User is enjoying your app thinking it’s native as your design job is excellent.
  • User sees a link to let’s say a Twitter page and “clicks” on it
  • The Twitter page loads in the InAppBrowser window, replacing your app with the Twitter page where your app screen used to be.
  • Extreme confusion ensues as the task manager shows that the user is indeed using your app, but they have no way to get back to your app content on iOS, or have to use the Back button on Android, and at this point the magic of native feel is broken.

The expected behavior would have been for the link click to open in a new OS Browser window, keeping the application intact and allowing the user to get back to it once they are done.

The solution in general is to track every external link with a script and open them using the window.open(url, '_system') call as illustrated here. Remember that this script will have to run in your wrapped app to work otherwise you will need to communicate the link url back to the wrapper app and issue the window.open call.

Android Only: Back Button

On Android you will have to be careful and plan out what happens when the user clicks on the Back button whether it’s a hardware or software button. If your wrapped app is a SPA, this can leads to some weird situations with your app state.

First thing to know is that there is a configuration for it in the InAppBrowser launch options:

hardwareback: set to yes to use the hardware back button to navigate backwards through the InAppBrowser‘s history. If there is no previous page, the InAppBrowser will close. The default value is yes, so you must set it to no if you want the back button to simply close the InAppBrowser.

If it is set to yesand your SPA handles back history fine, if the user gets back to the first page in your history and touches the Back button again, they will be taken back to the entry page of your wrapper app! Again not optimal and it breaks the experience of native and leaves the user confused again. One way to handle it would be to listen to the exit event on the InAppBrowser window so that when it is closed, the wrapper itself also is closed. Check this StackOverflow issue for alternatives.

Angular Only: ngZone for event handlers

After working with Cordova/Phonegap for a while and installing plugins, you’ll become familiar with how Cordova exposes functionality through events and event callbacks or promises. One gotcha if you’re working with Angular (>v4) is that these are events that are happening outside of Angular and if you remember even from AngularJS, any event happening outside of AngularJS needs to be synced with the AngularJS digest cycle using the $scope.apply() function. It’s no different in Angular expect now that Angular run in zones and uses the ngZone.run() function to re-enter the Angular zone. If you have Cordova plugin events firing and you are not able to tap into them in Angular, make sure to capture the event within a run function call as illustrated here in this example service for the Back button event being fired.

import { Injectable, NgZone } from '@angular/core';
@Injectable()
export class BackButtonService {
constructor(private logger: DebugLogService,
private ngZone: NgZone) {
document.addEventListener('backbutton', () => {
this.ngZone.run(() => {
console.log('Back button has been pressed')
});
}, false);
}
}

Summary

Although the initial goal of wrapping an existing web app into a native mobile app might look simple enough with Cordova/Phonegap, in practice there a quite a few pitfalls in making sure that the experience is optimal for users and fulfills their expectations for what a native app experience is. Behind the scene there will be quite a few orchestrations to put in place to maintain the illusion of native. I would then only recommend this approach as a stop gap measure while either a more appropriate native or hybrid app is being developed. Even they don’t work on Safari yet thus making them a more limited choice at this point, Progressive Web Applications are also a very viable option when targeting mobile applications.

Developing a mobile app with Phonegap, AngularJS and ExpressJS Part I

As a side project, I have embarked lately on the task of developing a mobile application with what ended up using Phonegap, AngularJS and ExpressJS. The gist of the application is to allow users to sign up and be able to share financial tips. I will document my journey along for others who are thinking of travelling down the same road, starting with my choice of technologies, this being my first attempt at building up the whole architecture from the ground up and since I am a front end developer and I wanted to leverage that experience to build a mobile app instead of going native.

Why PhoneGap and AngularJS and not Sencha Touch or Titanium?

When deciding on the initial approach to building the app, I initially thought about using Sencha Touch. I had tried Titanium before but had found it unwieldy although I like the whole integrated IDE to production setup. I work with ExtJS on a daily basis so I figured Sencha Touch would be a natural complement but after working with AngularJS, I really did want to keep getting better at it and not have to duplicate my learning by both doing Angular and Sencha Touch at the same time so i settled on PhoneGap after reading this tutorial (must read, you need to also read this here ). My time outside of work is very limited so I have little patience for frameworks with a high learning curve. I love working with AngularJS and adding Yeoman to the mix just makes it a breeze to work with so I was set on my front end tools.

Why ExpressJS?

When deciding of what framework to use for my API I initially tried Laravel, a PHP Web framework I had heard good things about it and it did not dissappoint. With the right instructions, the set up was a breeze and I had a basic API setup quickly. I liked the overall organization of the framework but at this point, I spend 90% of my time writing Javascript, so again, I wanted to stay within a single frame of mind for this project. I had always looked forward to finally trying out NodeJS , so server side Javascript it was. Given its nature, NodeJS can be unsettling because you are basically writing your own webserver, and coming from a background where you had to deals with the Apaches and Tomcats, it takes a little while to accept that new paradigm. Freedom can be overwhelming and I needed some structure to this whole new tool so I dug up ExpressJS, which is a web application framework for Node. I also decided to use MongoDB for my database, so I needed to find out how to make ExpressJS and MongoDB work nicely together, and Moongoose provided a good abstraction for interaction with my MongoDB database. Overall understanding the ExpressJS architecture, the concept of routes and middleware was the most challenging part of this part of the journey. I ran into Locomotive and I am not so far along that I can’t refactor using it as it adds convention over configuration.

API, API, API…

This is my first time building out a RESTful API as opposed to just using and it brought up considerations that I briefly wondered about before, the first one being how do I protect my API since this will be a closed system, I just want my mobile app to talk to my API. As a front end developer, those are considerations I never thought that deep on. I had to consider each strategy (Basic and Digest Authentication, API Key, OAuth, OAuth2), and how it would affect my implementation in AngularJS as well. This is the part that to a degree I am still pondering about but I am not letting me stop from moving forward.

Yeoman

Starting up was easy following Brian’s Ford instructions, I actually use Eclipse as an IDE where both my PhoneGap project and ExpressJS applications reside. There is a NodeJS plugin that exists for Eclipse that I use to run my API server initially but then I found Nodemon, which monitors my app for changes and restarts the server automatically. All of my Angular code resides in the assets folder of my PhoneGap project where I instantiated my Angular project using the yo angular generator. If you have not used Yeoman before to manage your web development projects, I strongly recommend it.

I create all my controllers, directives and services through the yo command as it also creates the proper accompanying unit tests. One small tip here, you might be wondering where yo gets the name of your app from when creating the angular module for your application and how to modify it so that you don’t have to keep changing the app name every time you create a new file? Change the “name” property in your bower.json file and yo will use a concatenation of that name and the “App” suffix to name your Angular modules.

AngularJS & Mobile Development Libraries

I am using Angular 1.1.5  as it adds a angular-mobile resource which adds better support for click events on mobile devices.  I initially thought of using Jquery Mobile even though I had a lot of apprehension about its speed but my app won’t be doing anything fancy UI wise, just presenting and collecting information. There is a plugin called the JQuery Mobile Angular Adapter which allows the two libraries to work nicely together but I could not get it going from the routing point of view, I was trying to do my application routing through AngularJS and it just was not playing nice, maybe due to my ignorance so I gave up on it and decided to stay entirely in AngularJS . I decided though to make use of the Angular Mobile Nav plug-in for my main screen and it worked as expected right out of the box with little configuration. There is though a promising project integrating JQuery Mobile and Angular called Angular JQM which instead of using adapters for JQuery mobile provides native Angular directives for JQuery Mobile, removing the need for the 3rd party libraries. As far as development itself it’s your typical Angular app so nothing new there.

NB: Doing bower install angular will get you the latest stable branch, to get the unstable branch you need to point Bower configuration for Angular dependencies to Pattern Consulting bower repo. This is what my bower.json looks like:

{
 "name": "appMobile",
 "version": "0.0.0",
 "dependencies": {
 "angular": "PatternConsulting/bower-angular#1.1.x",
 "json3": "~3.2.4",
 "jquery": "~1.9.1",
 "bootstrap-sass": "~2.3.1",
 "es5-shim": "~2.0.8",
 "angular-resource": "PatternConsulting/bower-angular#1.1.x",
 "angular-cookies": "PatternConsulting/bower-angular#1.1.x",
 "angular-sanitize": "PatternConsulting/bower-angular#1.1.x",
 "angular-mobile": "PatternConsulting/bower-angular#1.1.x",
 "angular-bootstrap": "~0.4.0",
 "angular-ui": "~0.4.0"
 },
 "devDependencies": {
 "angular-mocks": "PatternConsulting/bower-angular#1.1.x",
 "angular-scenario": "PatternConsulting/bower-angular#1.1.x"
 }
}

Another gotcha is that when trying to deploy your PhoneGap app to your phone or emulator for testing in Eclipse, you might run into an AAPT error such as:

"[2013-07-16 01:20:35 - appmobile] /projects/appmobile/assets/app/bower_components/angular-cookies/docs/partials/api/ng.directive:a.html: error: Invalid filename.  Unable to add."

This is because of the dot in the file name. Simple solution is to delete the “docs” folder in all your angular-* folders under bower_components.

Design

I have absolutely zero design skill so of course I am using Twitter Bootstrap, and the plan is to have a designer spruce it up once development is complete. One thing I have noticed is that I find the height of text inputs to be too small. Holly Schinsky in her excellent introductory article mentioned Topcoat which I had never heard of before, and it looks good so who knows, I might give it a run on a feature branch.

Summary

The process has been interesting to say the least, and it leads sometimes to second guess myself but that is the process of learning. I will keep up updated as of my progress, issues and findings in subsequent posts.

My first mobile Hackathon experience

On Saturday October 15th 2011 I attended the ATT Mobile App Hackathon held at the AOL headquarters in Dulles Virginia. It was quite a drive for me to get there, on top on waking up early on a Saturday morning but I figured it was worth it, I was already on a roll attending the UM Technology Startup Bootcamp the day before, which was informative in its own right. So i got to the AOL building a little after 10:00 enjoying 45 minutes or so of good reggae in my car and when I got into the building I was surprised by the “hipness” of it all. The event was held on the fifth floor and getting to the actual conference room just confirmed what I suspected at that point: AOL means business now. I met TeamAOL’s main man Christopher Gibson who gave me a card right away and let me know AOL was hiring mobile developers (A good man, if you are looking for a solid mobile dev gig, email Christopher.Gibson@teamaol.com ). After a healthy fruit based breakfast, I milled around, networking with the other developers and met others who like me were there for the first time.

At around 11:00am is my memory serves me right, presentations started and the first one was by AppMobi, who were introduced by Alex Donn of the  ATT Developer Program who did a good job introducing the platform. I got to play with their PhoneGap SDK offered as a Chrome plugin and was impressed by the whole package. Deploy to cloud options, emulators for different platforms including Iphone, Ipad and Droid among others. That presentation was followed by one from Apigee, which like AppMobi I had never heard of before and was actually impressed by their offering. OAuth seems to be giving everybody trouble coding and they offered a streamlined simplified API to work with, definitely something to consider for me in the future. There were other presentations from Pearsons who introduced their new API, and ViaFo , App47 and Sierra Wireless. To not also was the demo of the new Android app by the Winamp team of AOL Music and I have to say it was impressive software.

I got lunch (Thai s’il vous plait) and sat around, until Chris got me in touch with Omar and Amira who had ideas for apps but no technical know how, so together with Kyle, a developer from Fort Washington, and Rajish, we became Team Fresh and decided on a Recipe Finder app that would search for recipes entered by users and find a list of ingredients and nearby stores where they can be purchased. Since I was the de-facto tech lead, i set out to first find our data source and wound up on the PunchFork site which provides a recipe API with some paid extra features. I decided to go wit AppMobi as my dev platform and my first task was to get data from PunchFork. It turned out to take longer than I expected due to my inexperience with the AppMobi aUX API. Kyle left around 3:00PM followed by Omar and Amira around 5:00PM and I was left to figure things out with Rajish, who although being a backend .Net developer helped me along and was quite a good conversation partner. I went back and forth with the AppMobi support team (John and Tyler) who were gracious and very helpful. John ended sending me code for a sample app that was actually very similar in structure to what we were trying to achieve but by that time, I was already headachy and hacking the code would have taken too long to meet the 7:00PM deadline for app submission.

It was the end of the day for me, I was mad I could not finish, but the learning experience was invaluable. It made me happy to be around other developers and geek out to the fullest. It’s an environment where as a developer you are understood and it was welcomed for a change. AOL has some very fancy offices and breakout rooms, and as I told Chris Gibson and Chida Chidambaram who were our very gracious hosts, this event helped turned around my opinion of AOL. From the media coverage I assumed i would be going into old offices, populated by old people who were not in with what’s current, even dismissing calls from recruiters about possible opportunities at AOL (which is physically too far for me in any case), but it was quite the contrary. The dynamism I have seen from Chris Gibson and his team, their helpfulness, and the Winamp demo helped me change my mind. They might have taken a fall but they are definitely working their way back up and I am sure I will hear soon in the future about the things that will get done by their mobile development team. If you are counting AOL out, all I know is, as far as their mobile team, you might be surprised so hold judgement.

I left the venue a little after 7:00PM and went home happy with my day. It was productive and I learned a lot. I got some free tee shirts, something for the wife, some stickers and got to play with a Windows 7 phone, which I hadn’t had the opportunity to do yet. My advice for first timers:

  • Attend, don’t be afraid, there are other people like whose first time it is as well, so you won’t stand out, believe me.
  • BRING A LAPTOP, otherwise, you will be bored and are defeating the purpose.
  • Come prepared, if you are a developer, having an idea is good, to really make the most of your time, if you already have a time, plan it all out before hand and use the hackathon time as a collaborative development time to actually get it done.
  • Hackathon organizers: make it easier for devs/business people to create teams before hand so that time spent on design is reduced and we can get a finished product at the end of the day.
  • Fail if needed like I did, you will learn along the way. Nobody is expecting a shiny new app at the end of the day with all the bells and whistles. You’ll see that just having something that works is very satisfying.
  • Attend another one

That last point is well taken. I will be at the Education Hackday in Baltimore next month, and they have actually started building teams around ideas, so make sure to attend if you are in the area.

Android Development: PhoneGap’s “No such file or directory” Error

I was following the tutorial on the Phonegap wiki to set up the example project within the Eclipse SDK and ran into this issue where PhoneGap could not find the phonegap.jar under the framework directory:

`stat’: no such file or directory android-sdk-path.bat
D:/Development/Android/example_android/framework/phonegap.jar (Errno::ENOENT)

I had followed the previous steps and was puzzled by this error. It turns out the issue was the version of Ruby I was using was the issue. I was using 1.9.2 and scaling back to 1.9.1 got rid of the error and i am now able to build fine. I was getting also previous errors about the android-sdk-path.bat missing and those were due to the PhoneGap bin directory missing from my PATH variable so make sure to update your path correctly and you will be good to go!