Article – Using Best Practices to Create appMobi Applications
v1.05 : 05.10.2011
Although websites and appMobi applications are both written using the same technologies, there are significant differences between the two projects. Writing native applications using HTML5 and Javascript requires a different mindset from making an HTML5 website. This document is aimed at illustrating appMobi's "best practices" for web developers creating applications using the XDK development tool.
Download speed is no longer an issue since all the files necessary for the application to run are located within the application itself. HTML display and Javascript execution speed becomes important instead. Perhaps the biggest roadblock to speed is loading HTML files. The very first thing an appMobi application does is load the root index.html file. If there is too much HTML and Javascript loading in that page, it could delay the application up to several seconds before the user sees anything. Furthermore, the user might see HTML elements as they load dynamically onto the page which can be confusing.
Developers should try to delay as much loading of HTML and Javascript until the first index.html file is loaded. One of the most efficient ways to do this is to load big java-script libraries asynchronously after the app is already open. This lets the user get to the content faster which is an essential part of a good user experience. Make sure that any Javascript libraries that are required at startup are still loaded in the HTML. The remainder could be loaded with a function similar to the one below:
/* Global Variables */
var asyncSetupCounter=0;
var asyncAddScripts=0;
var asyncScripts=[];
/* Load all the Javascript libraries this way */
asyncScripts.push("test3.js");
asyncScripts.push("test2.js");
asyncScripts.push("test1.js");
/* This function will queue the Javascript libraries up for loading */
function addAsync(scripts,callback)
{
asyncAddScripts=scripts.length;
for(var i=0;i<scripts.length;i++)
{
loadAsyncScript(scripts[i],callback);
}
}
/* Start loading the Javascript libraries asynchronously once the appMobi library has been successfully loaded */
document.addEventListener("appMobi.device.ready", function(){ addAsync(asyncScripts,setupAsyncProgress); }, false);
/* This function loads the Javascript libraries into memory */
function loadAsyncScript(src,callback)
{
var xhrObj =new XMLHttpRequest();
xhrObj.onreadystatechange = function() { if ( xhrObj.readyState != 4 ) {return; }
window.eval(xhrObj.responseText);if(callback)callback();};
xhrObj.open('GET', src, true);
xhrObj.send('');
}
/* Once all the scripts are loaded asynchronously, call any functions depending on those libraries here */
function setupAsyncProgress()
{
asyncSetupCounter++;
if(asyncSetupCounter>=asyncAddScripts)
{
/* any functions that depend on the just loaded libraries should be fired here */
}
}
Each time a new HTML page is loaded, the user will see a flash as the new page is drawn on the screen. Developers can prevent this interruption by building their application using a single HTML file. Use CSS rules such as display:none and visibility:hidden in order to produce different UI views while staying on the same HTML page. Animate between those views using Javascript to change the CSS rules accordingly.
Mobile applications simply don't have the same space that the World Wide Web does. Rather than cramming too much into a single view, developers should try to divide an application into "bite-sized" chunks. Mobile browsers don't leave a lot of room for doing too many things at once. For example, long and complicated forms will frustrate even the most dedicated mobile users. Long lists of items are also very unwieldy. Try to create "tunnels" that will lead users down a path of entering data step by step instead. Be sure to remind users which step they are currently viewing, and how many steps remain in the "tunnel". Developers must try not to do too much on each page. Stick to the old adage, "less is more". Consider linking to another view rather than cramming it all onto the same user interface. Navigation should be as simple as possible. The best mobile applications have a single "main access" point that users can always return to rather than taking up real estate on the screen to allow the users to go anywhere in the application immediately.
Although mobile screens are smaller, ironically the user interface must be larger in order to accommodate selection using screen touches. The selection space of a fingertip is approximately 44 pixels by 44 pixels in size so it becomes important to give your customers a larger target to shoot at. There are no hard and fast rules, but buttons, edit fields, and graphical elements should all be at least 70 pixels square to accommodate comfortable touch access on smart phones.
It goes without saying that today's smart phones can't detect where your customer is looking the way it can identify that by detecting where the mouse cursor is. Don't use hover states or mouse over events.
Try to use touch start, touch move, and touch end events as much as possible. Anchor tags and click Javascript events common to mobile websites don't respond as quickly as the events of native applications. These Javascript events require the user to first touch the selected element, and then release the touch in a particular period of time before firing the event. Use touchstart HTML5 Javascript events on your elements as much as you can to accelerate the user experience.
Android and iOS devices support cutting edge HTML5 technologies. Video tags, audio tags, and client-side storage are just a few advances that developers can use. Take advantage of these new capabilities by starting the application's index.html file with the HTML5 docutype declaration. The HTML5 doctype declaration looks like this:
<!DOCTYPE html>
Developers should also consider using CSS3 to round corners, drop shadows, and gradients to your graphics and text. Here's an example of how to round corners, text shadow, and gradients:
element {
-webkit-border-radius: 5px 5px 5px 5px;
padding: 6px;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #999999),color-stop(1, #ffffff));
text-shadow: 2px 2px 2px #000;
text-align:center;l
}
HTML takes its time loading images and data. Developers can make their appMobi application respond quicker by preloading images before using them. Here is some example Javascript to load and use images.
//image array
var arrImages=new Array();
var arrImageFiles=new Array();
arrImageFiles[0]="image0.png";
arrImageFiles[1]="image1.png";
arrImageFiles[2]="image2.png";
arrImageFiles[3]="image3.png";
window.document.addEventListener('onload', function(){
for (var x=0;x<arrImageFiles.length;x++)
{
arrImages[x]=new Image();
arrImages[x].src=arrImageFiles[x];
}
}, false);
Filenames are case sensitive on iOS and Android devices. So if you use an image tag to reference an image file such as:
<img src="MYIMAGE.JPG" />
But the filename is cased to be myImage.jpg, the image will not display on the device. This can be particularly frustrating since the XDK ignores this issue. Developer just need to be aware of this disparity as they develop applications.
On iOS devices, touching and dragging will result in a section of text highlighted for a copy/paste operation. Although that makes sense on a web page, it can be counter-intuitive for an application. Get rid of this annoying "feature" by adding the following style changes.
/*** Prevent copy paste for all elements except text fields ***/
* { -webkit-user-select: none; }
input, textarea { -webkit-user-select:text; }
On Android devices, selected HTML elements are highlighted in big orange boxes. It is possible to hide this highlighting in an application using a simple style sheet change.
/*** Prevent Android element highlighting ***/
* {
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
The default browser window is set to scroll in response to a touch and drag event. However, mobile applications should not necessarily react to someone dragging their finger across the screen. Simply intercept and turn off the touchMove event to prevent this behavior. Here's some sample Javascript code to demonstrate how to do it.
/* This code prevents the webview from moving on a swipe */
preventDefaultScroll = function(evt) {
evt.preventDefault();
window.scroll(0,0);
return false;
};
window.document.addEventListener('touchmove', preventDefaultScroll, false);
The appMobi splash screen an application is given when it is created is shown as the application is started up. This splash screen will display either for fifteen seconds as the application loads, or until the AppMobi.device.hideSplashScreen command is executed. While the application is running behind the splash screen, take the opportunity to pre-load and size images and HTML elements.
Just as the splash screen hides processing while the application loads, developers might consider "drawing the curtain" over the application using an overlaid element or web view while animating from view to view. That way, the user can't repeatedly touch a button and stack up the list of functions the application is required to do unnecessarily.
Developers should consider building applications with the ability to track their customers' interactions with it. Analytics provides an accurate representation of what is working in an application and what isn't. The appMobi platform includes the statMobi Analytics Cloud Service. Using this service, applications can be built that automatically report back how users interact with the application. Since statMobi Analytics is hooked directly into the application, developers get information to track just how often their users start their applications and how long they use them. If developers see that customers are gravitating to a certain portion of the application, or using the application in surprising or unexpected ways, analytics give developers the ability to recognize these trends and make changes to the application accordingly.
Every statMobi enabled application will record the following data points automatically:
The power of statMobi lies in being able to track any sort of data point in your application that you might want, though. A good way start adding custom data points is to create a single function to call that will serve as a clearinghouse for all your analytics requests. Here is some example code you might call to save analytics data. This sample function records an event ID, as well as an optional set of name/value pairs as a query string to the statMobi Analytics logs.
/* ANALYTICS EXAMPLE CODE */
//this function sends an event ID, to statMobi analytics
//as well as an optional set of name/value pairs as a query string
function addAnalyticsDataPoint(eventID,queryString)
{
try
{
if (queryString==null) { queryString = ""; }
AppMobi.analytics.logPageEvent("/application/"+eventID+".event", queryString,"","",0,"index.html");
//view the sampled results of your analytics from the XDK
}
catch(e) {}
}
Drop this Javascript function into the <head> element of your application's index.html page and call it everywhere you want to record an analytics event. It takes two parameters. The first is an event identifier string and the second is an optional key/value query string parameter
User interface animations are achieved by changing an element's style rules over a period of time. There are several methods for making these animations work. Javascript libraries such as jqMobi give you access to functions that allow developers to animate HTML elements. Perhaps the most basic and quickest method of element animation is using the webkit translation commands. They are specifically written to take advantage of the device's animation hardware, and only require a tiny block of code. An example is included below.
//These global variables must be set in order to
var translateOpen = window.WebKitCSSMatrix && 'm11' in new WebKitCSSMatrix() ? "3d(" : "(";
var translateClose = window.WebKitCSSMatrix && 'm11' in new WebKitCSSMatrix() ? ",0)" : ")";
//This function will animate elements from one position to another
function move3d(elname, x, y, time, fromPreviousPosition) {
var el = document.getElementById(elname);
x = parseInt(x);
y = parseInt(y);
if (fromPreviousPosition) {
try {
x += parseInt(new WebKitCSSMatrix(window.getComputedStyle(el, null).webkitTransform).e);
y += parseInt(new WebKitCSSMatrix(window.getComputedStyle(el, null).webkitTransform).f);
}
catch (e) { }
}
try {
el.style.webkitTransform = "translate" + translateOpen + x + "px," + y + "px" + translateClose;
el.style.webkitTransitionDuration = time + "ms";
el.style['-webkit-backface-visibility'] = 'hidden';
}
catch (e) { console.log("Error moving " + elname + " " + e) }
}
While developing an application, developers should keep in mind which device or devices the application will ultimately run on. For example, if the application is meant to run on an iPad it should be created so that it is sized to 1024x768 pixels - the native display for that device. Otherwise, the application will look "fuzzy" or "pixilated" as it is sized up to match the size of the larger display.
For more information about sizing an application for a larger device, or sizing an application for multiple devices, download the article "Developing appMobi Applications for Multiple Devices" here
Application developers should think about positioning their UI elements absolutely using the CSS rule position:absolute. This allows developers to position controls in terms of pixels from the edges of the application, rather than relying on HTML's static positioning. Unless the application includes a lot of text that is meant to flow across the screen, absolute positioning takes the guesswork out of where controls will lay out for the developer and make the application experience that much better for the end user.
Although appMobi developers must consider turning off scrolling for the entire application, oftentimes there will be a need to scroll a certain element. Rather than trying to rely on the device's scrolling capabilities by dropping in an iframe element or navigating to a new HTML document, consider using a Javascript scrolling library such as iScroll4 (http://cubiq.org/iscroll-4) to handle in-application scrolling capabilities.
Do not rely only on the XDK for development. Developers should test applications on actual devices early and often. In an ideal situation, developers should have all the actual devices targeted by the application. Oftentimes, testing on devices will expose any performance issues or device quirks that could become a real problem later on in the development process. Bypass the headaches by using appMobi's "test anywhere" feature to load the application onto a live device.
The appMobi framework features a cloud service that allows developers to update applications even once they have been submitted to the application stores. Even if they never expect to need a Live Update, developers should make sure that their application will react appropriately to an update. That way, if the application requires an unforeseen change of some sort the option to post a Live Update is available to them. A simple test is really quick, and if the application doesn't require any input from the final user at all it will require no code changes.
For more information on Live Updates, download the article "Why Use Live Updates?" here: