24 min read

In this article by Andy Matthews, author of the book Creating Mobile Apps with jQuery Mobile, Second Edition, we will see a website where listeners will be greeted with music from local, independent bands across several genres and geographic regions. Building this will take many of the skills, and we’ll pepper in some new techniques that can be used in this new service. Let’s see what technology and techniques we could bring to bear on this venture.

In this article, we will cover:

  • A taste of Balsamiq
  • Organizing your code
  • An introduction to the Web Audio API
  • Prompting the user to install your app
  • New device-level hardware access
  • To app or not to app
  • Three good reasons for compiling an app

(For more resources related to this topic, see here.)

A taste of Balsamiq

Balsamiq (http://www.balsamiq.com/) is a very popular User Experience (UX) tool for rapid prototyping. It is perfect for creating and sharing interactive mockups:Creating Mobile Apps with jQuery Mobile - Second Edition

When I say very popular, I mean lots of major names that you’re used to seeing. Over 80,000 companies create their software with the help of Balsamiq Mockups.

So, let’s take a look at what the creators of a community radio station might have in mind. They might start with a screen which looks like this; a pretty standard implementation. It features an icon toolbar at the bottom and a listview element in the content:Creating Mobile Apps with jQuery Mobile - Second Edition

Ideally, we’d like to keep this particular implementation as pure HTML/JavaScript/CSS. That way, we could compile it into a native app at some point, using PhoneGap. However, we’d like to stay true to the Don’t Repeat Yourself (DRY) principle. That means, that we’re going to want to inject this footer onto every page without using a server-side process. To that end, let’s set up a hidden part of our app to contain all the global elements that we may want:

<div id="globalComponents">
   <div data-role="navbar" class="bottomNavBar">
       <ul>
           <li><a data-icon="music" href="#stations_by_region" data-transition="slideup">stations</a></li>
         <li><a data-icon="search" href="#search_by_artist" data-transition="slideup">discover</a></li>
           <li><a data-icon="calendar" href="#events_by_location" data-transition="slideup">events</a></li>
           <li><a data-icon="gear" href="#settings" data-transition="slideup">settings</a></li>
       </ul>
   </div>
</div>

We’ll keep this code at the bottom of the page and hide it with a simple CSS rule in the stylesheet, #globalComponents{display:none;}.

Now, we’ll insert this global footer into each page, just before they are created. Using the clone() method (shown in the next code snippet) ensures that not only are we pulling over a copy of the footer, but also any data attached with it. In this way, each page is built with the exact same footer, just like it is in a server-side include. When the page goes through its normal initialization process, the footer will receive the same markup treatment as the rest of the page:

/************************
* The App
************************/
var radioApp = {
universalPageBeforeCreate:function(){
   var $page = $(this);
   if($page.find(".bottomNavBar").length == 0){
     $page.append($("#globalComponents .bottomNavBar").clone());
   }
}
}
/************************
* The Events
************************/
//Interface Events
$(document).on("pagebeforecreate", "[data- "role="page"]",radioApp.universalPageBeforeCreate);

Look at what we’ve done here in this piece of JavaScript code. We’re actually organizing our code a little more effectively.

Organizing your code

I believe in a very pragmatic approach to coding, which leads me to use more simple structures and a bare minimum of libraries. However, there are values and lessons to be learned out there.

MVC, MVVM, MV*

For the last couple of years, serious JavaScript developers have been bringing backend development structures to the web, as the size and scope of their project demanded a more regimented approach. For highly ambitious, long-lasting, in-browser apps, this kind of structured approach can help. This is even truer if you’re on a larger team.

MVC stands for Model-View-Controller ( see http://en.wikipedia.org/wiki/Model–view–controller), MVVM is for Model View ViewModel (see http://en.wikipedia.org/wiki/Model_View_ViewModel), and MV* is shorthand for Model View Whatever and is the general term used to sum up this entire movement of bringing these kinds of structures to the frontend.

Some of the more popular libraries include:

A very nice comparison of these, and more, is at http://readwrite.com/2014/02/06/angular-backbone-ember-best-javascript-framework-for-you.

MV* and jQuery Mobile

Yes, you can do it!! You can add any one of these MV* frameworks to jQuery Mobile and make as complex an app as you like. Of them all, I lean toward the Ember platform for desktop and Angular for jQuery Mobile. However, I’d like to propose another alternative.

I’m not going to go in-depth into the concepts behind MVC frameworks. Ember, Angular, and Backbone, all help you to separate the concerns of your application into manageable pieces, offering small building blocks with which to create your application. But, we don’t need yet another library/framework to do this. It is simple enough to write code in a more organized fashion. Let’s create a structure similar to what I’ve started before:

//JavaScript Document
/*******************
* The Application
*******************/
/*******************
* The Events
*******************/
/*******************
* The Model
*******************/

The application

Under the application section, let’s fill in some of our app code and give it a namespace. Essentially, namespacing is taking your application-specific code and putting it into its own named object, so that the functions and variables won’t collide with other potential global variables and functions. It keeps you from polluting the global space and helps preserve your code from those who are ignorant regarding your work. Granted, this is JavaScript and people can override anything they wish.

However, this also makes it a whole lot more intentional to override something like the radioApp.getStarted function, than simply creating your own function called getStarted. Nobody is going to accidentally override a namespaced function.

/*******************
* The application
*******************/
var radioApp = {
settings:{
   initialized:false,
   geolocation:{
     latitude:null,
     longitude:null,
   },
   regionalChoice:null,
   lastStation:null
},
getStarted:function(){
   location.replace("#initialize");
},
fireCustomEvent:function(){
   var $clicked = $(this);
   var eventValue = $clicked.attr("data-appEventValue");
   var event = new jQuery.Event($(this).attr("data-appEvent"));
   if(eventValue){ event.val = eventValue; }
   $(window).trigger(event);
},
otherMethodsBlahBlahBlah:function(){}
}

Pay attention, in particular, to the fireCustomEvent. function With that, we can now set up an event management system. At its core, the idea is pretty simple. We’d like to be able to simply put tag attributes on our clickable objects and have them fire events, such as all the MV* systems. This fits the bill perfectly.

It would be quite common to set up a click event handler on a link, or something, to catch the activity. This is far simpler. Just an attribute here and there and you’re wired in. The HTML code becomes more readable too. It’s easy to see how declarative this makes your code:

<a href="javascript://" data-appEvent="playStation" data- appEventValue="country">Country</a>

The events

Now, instead of watching for clicks, we’re listening for events. You can have as many parts of your app as you like registering themselves to listen for the event, and then execute appropriately.

As we fill out more of our application, we’ll start collecting a lot of events. Instead of letting them get scattered throughout multiple nested callbacks and such, we’ll be keeping them all in one handy spot. In most JavaScript MV* frameworks, this part of the code is referred to as the Router. Hooked to each event, you will see nothing but namespaced application calls:

/*******************
* The events
*******************/
//Interface events
$(document).on("click", "[data-appEvent]",
radioApp.fireCustomEvent);"$(document).on("pagecontainerbeforeshow",
"[data-role="page"]",radioApp.universalPageBeforeShow);"
$(document).on("pagebeforecreate",
"[data-role="page"]",radioApp.universalPageBeforeCreate);"
$(document).on("pagecontainershow", "#initialize",
radioApp.getLocation);"
$(document).on("pagecontainerbeforeshow", "#welcome",
radioApp.initialize);
//Application events
$(window).on("getStarted",
radioApp.getStarted);
$(window).on("setHomeLocation",
radioApp.setHomeLocation);
$(window).on("setNotHomeLocation",
radioApp.setNotHomeLocation);
$(window).on("playStation",
radioApp.playStation);

Notice the separation of concerns into interface events and application events. We’re using this as a point of distinction between events that are fired as a result of natural jQuery Mobile events (interface events), and events that we have thrown (application events). This may be an arbitrary distinction, but for someone who comes along later to maintain your code, this could come in handy.

The model

The model section contains the data for your application. This is typically the kind of data that is pulled in from your backend APIs. It’s probably not as important here, but it never hurts to namespace what’s yours. Here, we have labeled our data as the modelData label. Any information we pull in from the APIs can be dumped right into this object, like we’ve done here with the station data:

/*******************
* The Model
*******************/
var modelData = {
station:{
   genres:[
       {
       display:"Seattle Grunge",
       genreId:12,
       genreParentId:1
       }
   ],
   metroIds[14,33,22,31],
   audioIds[55,43,26,23,11]
}
}

Pair this style of programming with client-side templating, and you’ll be looking at some highly maintainable, well-structured code. However, there are some features that are still missing. Typically, these frameworks will also provide bindings for your templates. This means that you only have to render the templates once. After that, simply updating your model object will be enough to cause the UI to update itself.

The problem with these bound templates, is that they update the HTML in a way that would be perfect for a desktop application. But remember, jQuery Mobile does a lot of DOM manipulation to make things happen.

In jQuery Mobile, a listview element starts like this:

<ul data-role="listview" data-inset="true">
<li><a href="#stations">Local Stations</a></li>
</ul>

After the normal DOM manipulation, you get this:

<ul data-role="listview" data-inset="true" data-theme="b" style="margin-top:0" class="ui-listview ui-listview-inset ui- corner-all ui-shadow">
<li data-corners="false" data-shadow="false" data- iconshadow="true" data-wrapperels="div" data-icon="arrow-r" data- iconpos="right" data-theme="b" class="ui-btn ui-btn-icon-right ui- li-has-arrow ui-li ui-corner-top ui-btn-up-b">
       <div class="ui-btn-inner ui-li ui-corner-top">
           <div class="ui-btn-text">
               <a href="#stations" class="ui-link-inherit">Local Stations</a>
           </div><span class="ui-icon ui-icon-arrow-r ui-icon- shadow">&nbsp;</span>
       </div>
   </li>
</ul>

And that’s just a single list item. You really don’t want to include all that junk in your templates; so what you need to do, is just add your usual items to the listview element and then call the .listview(“refresh”) function. Even if you’re using one of the MV* systems, you’ll still have to either find, or write, an adapter that will refresh the listviews when something is added or deleted. With any luck, these kinds of things will be solved at the platform level soon. Until then, using a real MV* system with jQM will be a pain in the posterior.

Introduction to the Web Audio API

The Web Audio API is a fairly new development and, at the time of writing this, only existed within the mobile space on Mobile Safari and Chrome for Android (http://caniuse.com/#feat=audio-api). The Web Audio API is available on the latest versions of desktop Chrome, Safari, and Firefox, so you can still do your initial test coding there. It’s only a matter of time before this is built into other major platforms.

Most of the code for this part of the project, and the full explanation of the API, can be found at http://tinyurl.com/webaudioapi2014.

Let’s use feature detection to branch our capabilities:

function init() {
if("webkitAudioContext" in window) {
   context = new webkitAudioContext();
   // an analyzer is used for the spectrum
   analyzer = context.createAnalyzer();
   analyzer.smoothingTimeConstant = 0.85;
   analyzer.connect(context.destination);
   fetchNextSong();
} else {
   //do the old stuff
}
}

The original code for this page was designed to kick off simultaneous downloads for every song in the queue. With a fat connection, this would probably be OK. Not so much on mobile. Because of the limited connectivity and bandwidth, it would be better to just chain downloads to ensure a better experience and a more respectful use of bandwidth:

function fetchNextSong() {
var request = new XMLHttpRequest();
var nextSong = songs.pop();
if(nextSong){
   request = new XMLHttpRequest();
   // the underscore prefix is a common naming convention
   // to remind us that the variable is developer-supplied
   request._soundName = nextSong;
   request.open("GET", PATH + request._soundName + ".mp3", " true);
   request.responseType = "arraybuffer";
   request.addEventListener("load", bufferSound, false);
   request.send();
}
}

Now, the bufferSound function just needs to call the fetchNextSong function after buffering, as shown in the following code snippet:

function bufferSound(event) {
   var request = event.target;
   context.decodeAudioData(request.response, function onSuccess(decodedBuffer) {
       myBuffers.push(decodedBuffer);
       fetchNextSong();
   }, function onFailure() {
       alert("Decoding the audio buffer failed");
   });
}

One last thing we need to change from the original, is telling the buffer to pull the songs in the order that they were inserted:

function playSound() {
   // create a new AudioBufferSourceNode
   var source = context.createBufferSource();
   source.buffer = myBuffers.shift();
  source.loop = false;
   source = routeSound(source);
   // play right now (0 seconds from now)
   // can also pass context.currentTime
   source.start(0);
 mySpectrum = setInterval(drawSpectrum, 30);
   mySource = source;
}

For anyone on iOS, this solution is pretty nice. There is a lot more to this API for those who want to dig in. With this out-of-the-box example, you get a nice canvas-based audio analyzer that gives you a very nice professional look, as the audio levels bounce to the music. Slider controls are used to change the volume, the left-right balance, and the high-pass filter. If you don’t know what a high-pass filter is, don’t worry; I think that filter’s usefulness went the way of the cassette deck. Regardless, it’s fun to play with:Creating Mobile Apps with jQuery Mobile - Second Edition

The Web Audio API is a very serious piece of business. This example was adapted from the example on Apple’s site. It only plays one sound. However, the Web Audio API was designed with the idea of making it possible to play multiple sounds, alter them in multiple ways, and even dynamically generate sounds using JavaScript.

In the meantime, if you want to see this proof of concept in jQuery Mobile, you will find it in the example source in the webaudioapi.html file. For an even deeper look at what is coming, you can check the docs at https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html.

Prompting the user to install your app

Now, let’s take a look at how we can prompt our users to download the Community Radio app to their home screens. It is very likely that you’ve seen it before; it’s the little bubble that pops up and instructs the user with the steps to install the app.

There are many different projects out there, but the best one that I have seen is a derivative of the one started by Google. Much thanks and respect to Okamototk on GitHub (https://github.com/okamototk) for taking and improving it. Okamototk evolved the bubble to include several versions of Android, legacy iOS, and even BlackBerry. You can find his original work at https://github.com/okamototk/jqm-mobile-bookmark-bubble. However, unless you can read Japanese, or enjoy translating it.

Don’t worry about annoying your customers too much. With this version, if they dismiss the bookmarking bubble three times, they won’t see it again. The count is stored in HTML5 LocalStorage; so, if they clear out the storage, they’ll see the bubble again. Thankfully, most people out there don’t even know that can be done, so it won’t happen very often. Usually, it’s geeks like us that clear things like LocalStorage and cookies, and we know what we’re getting into when we do it.

In my edition of the code, I’ve combined all the JavaScript into a single file meant to be placed between your import of jQuery and jQuery Mobile. At the top, the first non-commented line is:

page_popup_bubble="#welcome";

This is what you would change to be your own first page, or where you want the bubble to pop up.

In my version, I have hardcoded the font color and text shadow properties into the bubble. This was needed, because in jQM the font color and text shadow color vary, based on the theme you’re using. Consequently, in jQuery Mobile’s original default A theme (white text on a black background), the font was showing up as white with a dark shadow on top of a white bubble. With my modified version, for older jQuery Mobile versions, it will always look right.

We just need to be sure we’ve set up our page with the proper links in the head, and that our images are in place:

<link rel="apple-touch-icon-precomposed" sizes="144x144" href="images/album144.png">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="images/album114.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="images/album72.png">
<link rel="apple-touch-icon-precomposed" href="images/album57.png">
<link rel="shortcut icon" href="img/images/album144.png">

Note the Community Radio logo here. The logo is pulled from our link tags marked with rel=”apple-touch-icon-precomposed” and injected into the bubble. So, really, the only thing in the jqm_bookmark_bubble.js file that you would need to alter is the page_popup_bubble function.

New device-level hardware access

New kinds of hardware-level access are coming to our mobile browsers every year. Here is a look at some of what you can start doing now, and what’s on the horizon. Not all of these are applicable to every project, but if you think creatively, you can probably find innovative ways to use them.

Accelerometers

Accelerometers are the little doo-dads inside your phone that measure the phone’s orientation in space. To geek out on this, read http://en.wikipedia.org/wiki/Accelerometer.

This goes beyond the simple orientation we’ve been using. This is true access to the accelerometers, in detail. Think about the user being able to shake their device, or tilting it as a method of interaction with your app. Maybe, Community Radio is playing something they don’t like and we can give them a fun way to rage against the song. Something such as, shake a song to never hear it again. Here is a simple marble rolling game somebody made as a proof of concept. See http://menscher.com/teaching/woaa/examples/html5_accelerometer.html.

Camera

Apple’s iOS 8 and Android’s Lollipop can both access photos on their filesystems as well as the cameras. Granted, these are the latest and greatest versions of these two platforms. If you intend to support the many woefully out of date Android devices (2.3, 2.4) that are still being sold off the shelves as if brand new, then you’re going to want to go with a native compilation such as PhoneGap or Apache Cordova to get that capability:

<input type="file" accept="image/*">
<input type="file" accept="video/*">

The following screenshot has iOS to the left and Android to the right:Creating Mobile Apps with jQuery Mobile - Second Edition

APIs on the horizon

Mozilla is doing a lot to push the mobile web API envelope. You can check out what’s on the horizon here: https://wiki.mozilla.org/WebAPI.

To app or not to app, that is the question

Should you or should you not compile your project into a native app? Here are some things to consider.

Raining on the parade (take this seriously)

When you compile your first project into an app, there is a certain thrill that you get. You did it! You made a real app! It is at this point that we need to remember the words of Dr. Ian Malcolm from the movie Jurassic Park (Go watch it again. I’ll wait):Creating Mobile Apps with jQuery Mobile - Second Edition

“You stood on the shoulders of geniuses to accomplish something as fast as you could, and before you even knew what you had, you patented it, and packaged it, and slapped it on a plastic lunchbox, and now [bangs on the table] you’re selling it, you wanna sell it. Well… your scientists were so preoccupied with whether or not they could that they didn’t stop to think if they should.”

                                                                                                 – Dr. Ian Malcolm

These words are very close to prophetic for us. In the end, their own creation ate most of the guests for lunch.

According to this report from August 2012 http://www.webpronews.com/over-two-thirds-of-the-app-store-has-never-been-downloaded-2012-08 (and several others like it that I’ve seen before), over two-thirds of all apps on the app stores have never been downloaded. Not even once! So, realistically, app stores are where most projects go to die.

Even if your app is discovered, the likelihood that anyone will use it for any significant period of time is astonishingly small. According to this article in Forbes (http://tech.fortune.cnn.com/2009/02/20/the-half-life-of-an-iphone-app/), most apps are abandoned in the space of minutes and never opened again. Paid apps last about twice as long, before either being forgotten or removed. Games have some staying power, but let’s be honest, jQuery Mobile isn’t exactly a compelling gaming platform, is it??

The Android world is in terrible shape. Devices can still be purchased running ancient versions of the OS, and carriers and hardware partners are not providing updates to them in anything even resembling a timely fashion. If you want to monitor the trouble you could be bringing upon yourself by embracing a native strategy, look here:

http://developer.android.com/about/dashboards/index.html:Creating Mobile Apps with jQuery Mobile - Second Edition

You can see how fractured the Android landscape is, as well as how many older versions you’ll probably have to support.

On the flip side, if you’re publishing strictly to the web, then every time your users visit your site, they’ll be on the latest edition using the latest APIs, and you’ll never have to worry about somebody using some out-of-date version. Do you have a security patch you need to apply? You can do it in seconds. If you’re on the Apple app store, this patch could take days or even weeks.

Three good reasons for compiling an app

Yes, I know I just finished telling you about your slim chances of success and the fire and brimstone you will face for supporting apps. However, here are a few good reasons to make a real app. In fact, in my opinion, they’re the only acceptable reasons.

The project itself is the product

This is the first and only sure sign that you need to package your project as an app. I’m not talking about selling things through your project. I’m talking about the project itself. It should be made into an app. May the force be with you.

Access to native only hardware capabilities

GPS and camera are reliably available for the two major platforms in their latest editions. iOS even supports accelerometers. However, if you’re looking for more than this, you’ll need to compile down to an app to get access to these APIs.

Push notifications

Do you like them? I don’t know about you, but I get way too many push notifications; any app that gets too pushy either gets uninstalled or its notifications are completely turned off. I’m not alone in this. However, if you simply must have push notifications and can’t wait for the web-based implementation, you’ll have to compile an app.

Supporting current customers

Ok, this one is a stretch, but if you work in corporate America, you’re going to hear it. The idea is that you’re an established business and you want to give mobile support to your clients. You or someone above you has read a few whitepapers and/or case studies that show that almost 50 percent of people search in the app stores first.

Even if that were true (which I’m still not sold on), you’re talking to a businessperson. They understand money, expenses, and escalated maintenance. Once you explain to them the cost, complexity, and potential ongoing headaches of building and testing for all the platforms and their OS versions in the wild, it becomes a very appealing alternative to simply put out a marketing push to your current customers that you’re now supporting mobile, and all they have to do is go to your site on their mobile device. Marketing folks are always looking for reasons to toot their horns at customers anyway. Marketing might still prefer to have the company icon on the customer’s device to reinforce brand loyalty, but this is simply a matter of educating them that it can be done without an app.

You still may not be able to convince all the right people that apps are the wrong way to go when it comes to customer support. If you can’t do it on your own, slap them on their heads with a little Jakob Nielson. If they won’t listen to you, maybe they’ll listen to him. I would defy anyone who says that the Nielsen Norman Group doesn’t know what they’re saying. See http://www.nngroup.com/articles/mobile-sites-vs-apps-strategy-shift/ for the following quote:

“Summary: Mobile apps currently have better usability than mobile sites, but forthcoming changes will eventually make a mobile site the superior strategy.”

So the $64,000 question becomes: are we making something for right now or for the future? If we’re making it for right now, what are the criteria that should mark the retirement of the native strategy? Or do we intend to stay locked on it forever? Don’t go into that war without an exit strategy.

Summary

I don’t know about you, but I’m exhausted. I really don’t think there’s any more that can be said about jQuery Mobile, or its supporting technologies at this time. You’ve got examples on how to build things for a whole host of industries, and ways to deploy it through the web. At this point, you should be quoting Bob the Builder. Can we build it? Yes, we can!

I hope this article has assisted and/or inspired you to go and make something great. I hope you change the world and get filthy stinking rich doing it. I’d love to hear your success stories as you move forward. To let me know how you’re doing, or to let me know of any errata, or even if you just have some questions, please don’t hesitate to email us directly at http://[email protected]. Now, go be awesome!

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here