Choosing A Mobile App Strategy That’s Right For You

by Marshall Levin, Technology Solutions Senior Manager at NavigationArts, now an EPAM company

When it comes to choosing an approach for your next mobile app, three obvious options come to mind. But there’s a fourth you may want to consider. I’ll explain the pros and cons of each and describe some of the things to consider before going down one of these paths.


Option 1: Native application

This option is pretty obvious. You write one or more versions of your app depending on the platforms you want to support. Objective C or Swift for iOS. Java for Android. .NET for Windows Phone. And so on.

For all but the largest budgets, this is often a non-starter. Sometimes you can achieve a large degree of code reuse using a bytecode-compiled approach like Xamarin that has runtimes for each platform. Sometimes you have a lot of legacy code written in C and you can leverage this by building native libraries and thin platform-specific wrappers. Sometimes you’re building a game and can take advantage of something like Unity, a cross-platform tool that compiles to native code for each platform. But for the most part, multiple platforms mean multiple code-bases in multiple languages, so plan on doubling (or tripling) your development and QA budget, and plan on spending more to find developers with highly specific and highly sought-after skills.

Our clients are most often interested in dynamic, interactive, content-driven applications and want to get their apps to market as quickly as possible. These kinds of apps do not require a high framerate and tend not to leverage very hardware-intensive device functionality. Native application development is generally overkill and represents an unnecessary expense.


Option 2: Responsive web-based app

Another way to go is simply to build a responsive web-based app that can serve your (or your client’s) desktop, tablet and handheld needs. This has some clear advantages. First, you can leverage your existing talent. You most likely already have front-end developers fluent in HTML5, CSS and JavaScript. Of course, there’s more to it than just breakpoints and stacking elements. You need to consider gestures and touches, and most of all you need to consider performance. Users of mobile devices aren’t simply using tiny web browsers. They may have limited bandwidth and inconsistent connectivity, and their mini-computers, powerful as they are, still can’t keep up with a desktop in terms of rendering speed, especially for complex pages, so you will definitely need to keep your pages light and your code clean and efficient.

The biggest downside of this approach, however, is that clients often want a presence in the app stores. While it is possible to create home screen shortcuts to launch your mobile website, there are definite advantages to being in the app store. Your users can rate your app, you can be featured in the app store, and of course you can sell your app easily.

In addition, while HTML5 has made it possible to access many of the device’s hardware features, such as a user’s location or the ability to take pictures with the built-in camera, there are still OS-level features, like push notifications, that require some native code. If you build a responsive web-based app, you won’t be able to provide some of the most compelling functionality that users have come to expect.


Option 3: Cordova/PhoneGap app

Building your mobile app with Apache Cordova, or its Adobe-backed, value-added cousin, PhoneGap, allows you to target multiple mobile platforms with a “true” downloadable, app-store-publishable app that still runs your HTML5-based code.

At its simplest, Cordova provides a thin native application wrapper that simply shows a splash screen and then displays a full-screen “webview” component. You can think of the webview simply as a browser window with no buttons, address bar, or other user controls. It’s simply a stripped-down, bare-bones area for displaying an HTML page.

There’s a bit of a hitch, though. Cordova uses the OS’s built-in webview component to render its HTML5 content. This is, unfortunately, not the same thing as the rendering engine that is part of the Chrome or Safari browser on your mobile device. The webview component tends to lag a few versions behind its full-browser cousin, so just because the latest and greatest HTML5 feature or API is supported by your device’s stand-alone browser doesn’t mean that your Cordova-embedded webview will support it. Check carefully before embarking on a project that depends heavily on an API that could make or break your app.

There are frameworks that help address this issue. Crosswalk, for example, embeds a much more up-to-date rendering engine into your app and is still compatible with Cordova. Some of these solutions are still in their early phases, though, and tend not to have the user base that pure Cordova/PhoneGap have. They are good options if you’re running into issues with unsupported APIs, but if you get stuck, it may be that much harder to find someone else who has the same issue.

In any case, the webview, whichever one you settle on, is simply configured to start up and display a predetermined web page. Typically, this is a local HTML file (i.e., a URL beginning with “file://”). The installed app has your HTML file, along with all of the CSS, JavaScript, images, etc. bundled with it. They’re all just loaded locally. This is no different than loading and viewing an HTML page that is stored on your computer with your favorite browser. There’s no web server involved. You’re simply viewing an HTML page.

Of course, Cordova can do more than just display a web page. It provides, in the form of built-in or third-party plugins, special hooks that allow you, through special JavaScript APIs made available to the webview in which your JavaScript code runs, to access functionality normally only available to you through native code. This is because you are, essentially, running native code. The plugins are platform-specific (although for the most popular ones, there’s almost always both an Android and iOS version). They can do anything that native code can do, but they also expose a few functions to initiate execution and to pass data back and forth between the native realm and the webview realm. For example, you could execute some JavaScript like “cordova.exec(successCallback, failureCallback, “AcmeScannerPlugin”, “scan”)” to tell Cordova to invoke the AcmeScannerPlugin’s “scan” method. The “successCallback” JavaScript function would then receive the result of the scanned barcode. Cordova acts as a mediator and allows you, through its plugin interface, to harness the power of native code. It is also very easy, if you’re proficient with native code, to write your own Cordova plugins.


Option 4: The hybrid-of-hybrids

From the shadows emerges a fourth option, the hybrid-of-hybrids.

Let’s say your client wants to have its cake and eat it, too. Now they want both an installable Cordova/PhoneGap app and a responsive website.

You can certainly achieve a great deal of code reuse with the Cordova/PhoneGap option. You could take the same code running your responsive web-base app and embed a copy of that HTML5 code, probably with minor changes to make it work locally and to leverage Cordova APIs, directly into your Cordova app.

But then you have two copies of your front-end code. What if your back-end changes and you need to make a corresponding change to your front-end code? Well, depending on how you roll it out, you may have two different versions of the front-end (one being served as a web-based version and one baked into your mobile app). They may expect different versions of your back-end services. You would need to push an update to the app store. It may take a while for your app to be approved. Some people may never update the app. You may be stuck supporting legacy versions of your app indefinitely. Sure, you could version your back-end APIs (perhaps something like http://api.example.com/rest/v2/widget). But this can quickly get pretty hairy. You get the idea.

As you may recall, the Cordova webview simply loads up a URL when it starts. There’s no reason this has to be a local (file://) URL. It could just as easily be an http(s):// URL to fetch a responsive or mobile-optimized web page from your web server. Voilà! We’re done!

Wait, not so fast. This is not a task to be undertaken lightly. There are some serious implications you need to think about, not the least of which are performance and latency. Now, rather than loading a local HTML page, you’re loading a remote resource. Even if your local HTML code eventually loadeddata from a remote resource, at least the basic user interface could start immediately and show some kind of “loading” icon. But if you’re loading even that first resource remotely, your users will have to wait.

There are certainly ways to mitigate this. You could, for example, have a very basic local “bootstrap” HTML page (no, not the framework, but rather the “pull oneself up by one’s bootstraps” concept) that would tell the user to wait while it loaded the remainder of your application, perhaps via AJAX, or perhaps simply by updating the window.location variable. The bootstrap page would not need to rely on any custom API of your back-end, so it would be unlikely to become obsolete and require an update.

However, if you try this approach, you will quickly find a major flaw. If your Cordova-based application is loaded via HTTP and not from the mobile device’s local filesystem, the Cordova-specific JavaScript API is not available to you (at least not in any reliable way, as far as I’ve been able to tell). So while you could serve up a perfectly good HTML5-based application this way, you’d be unable to take advantage of any Cordova plugins and thus any native code.

But there’s still hope. One technique we’ve used successfully relies on the Window.postMessage API.

Create an HTML start page for your Cordova app. This start page will be embedded into the app (Cordova will be loading a file:// URL). On this start page, you’ll create an iframe and set it to 100% width and 100% height. In this iframe, you’ll load your responsive web-based app.

Using JavaScript that executes in the local HTML page (the one that contains the <iframe> tag), you can detect when the iframe content has fully loaded (essentially a “document ready” state). At this point, your local HTML (we’ll call it the “wrapper” page) can target the Window object of the iframe (via its “contentWindow” property) and use the postMessage API to send an data object of your own design to the iframe. This generates a JavaScript event that can be received by code in your iframe.

In your iframe, a receiveMessage function will receive the object from the wrapper page (encapsulated as the “data” property of an “event” object). Congratulations! Your iframe has received a message from the wrapper. It can then make note of the event’s “origin” property (to make sure it is a trusted sender) and the event’s “source” property (to keep a handle to the wrapper’s Window so that the iframe can target the wrapper with a postMessage call of its own).

Ideally, the first message the wrapper sends to the iframe would tell it “hey, lucky you, you’re running inside an iframe within a Cordova wrapper!” From this point forward, we can take advantage of the principles of progressive enhancement. But instead of using techniques like object detection or browser sniffing to determine whether our environment supports some specific functionality, we can simply use the fact that we’ve been notified that we’re running inside Cordova. When our code running inside the iframe would like to take advantage of a feature only available inside Cordova, it can use postMessage to call back to the wrapper. The wrapper, like the iframe code, also has a “receiveMessage” function. The wrapper code, because it is loaded from a file:// URL, has full access to the Cordova API and any native plugins that are bundled with your app. If the iframe asks the wrapper to invoke some native functionality by passing it a message and asking it nicely, the wrapper can invoke the Cordova plugin, get the result, and pass it back, again via postMessage, to the iframe. Your front-end code, which is loaded from your web server, can now do double-duty as your mobile app code, and it doesn’t have any Cordova-specific code getting in the way. It is abstracted thanks to the wrapper page. All your iframe code knows is that a certain feature is available and that Window to which it has a reference is capable of executing said feature when asked via postMessage.

That probably sounded confusing, so here’s some code for you. In your wrapper page, which hosts the iframe, you can do something like this:

// confirm that your iframe content is fully loaded, otherwise there will be nobody listening to receive our message
document.getElementById("myIframe").contentWindow.postMessage({ cordova: true, message: 'yay, you are in Cordova!' }, 'http://mysite.example.com');

 

Now, inside your iframe, you’d do something like this:

window.addEventListener("message", receiveMessage, false);
var wrapperWindow = null;
var isCordova = false;
function receiveMessage(event) {
     // you should probably check event.origin here to see if it is something legit
     wrapperWindow = event.source; // we're going to need this later
     if (event.data.cordova) {
      // we are in cordova! yay!
      isCordova = true;
    }
}

 

Now, when you want to do something fancy, in your iframe, you’d just do this:

if (isCordova) {
     // yay! we can do something fancy
    wrapperWindow.postMessage({ request: 'fancyFunction' }, 'http://mysite.example.com');
}

 

Meanwhile, back in wrapper-land:

window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
     // you should probably check event.origin here to see if it is something legit
    if (event.data.request == 'fancyFunction') {
       // invoke some fancy Cordova function
       // if the result is not returned immediately, the following line would be in the success callback
document.getElementById("myIframe").contentWindow.postMessage({ response: 42 }, 'http://mysite.example.com');
     }
}

 

And finally back in the iframe, add a clause to your existing receiveMessage function:

if (event.data.response) {
    alert('the fancy feature says: ' + event.data.response);
}


Again, while this approach can allow your HTML5 code to do double-duty and serve both your responsive web-based app as well as your Cordova-based mobile app and thus recognize significant code reuse and eliminate the headaches associated with divergent copies of your front-end code, you’ll still need to address the issues of latency and performance using some of the techniques described above.

Good luck!