Web Apps

So web apps are pretty awesome. Of course they have some limitations like access to device hardware (camera), and they can take a little extra time to load depending on how they’re built, but overall, the time it takes to put together a convincing prototype web app is small and the results can be impressive. And then there are tools to wrap these builds in enough native code that it can really become a native app: PhoneGap, Appcelerator, etc

I’ve been in a few prototype coding competitions recently, and have found myself adopting web app solutions more and more. I also receive a lot of questions about this at work, and find myself pointing people to the same resources over and over, so I thought a post here might benefit someone else. Please do find a number of helpful links throughout the post.

The gist of it (and I should preface that I’m biased towards iOS, but many of these elements port to Android just fine) is that you want to take a web page and modify it sufficiently so that it mimics a native application experience. This entails things like: removing the browser elements, adding an icon, adding a launch/splash screen, handling orientation changes, and so on.

Let’s jump into it…here is some of the header from a typical app I build:

<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="apple-touch-icon" sizes="57x57" href="icon/icon57.png" />
<link rel="apple-touch-icon" sizes="72x72" href="icon/icon72.png" />
<link rel="apple-touch-icon" sizes="114x114" href="icon/icon114.png" />
<link rel="apple-touch-icon" sizes="144x144" href="icon/icon144.png" />
<link rel="apple-touch-startup-image" media="screen and (max-device-width: 320px)" href="images/iphone_splash.png"/>
<link rel="apple-touch-startup-image" media="(max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2)" href="images/iphone_splash_hires.png" />
<link rel="apple-touch-startup-image" href="icon/ipad_landscape.png" media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:landscape)" />
<link rel="apple-touch-startup-image" href="icon/ipad_portrait.png" media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:portrait)" />
<link rel="apple-touch-startup-image" href="icon/ipad_landscape_hires.png" media="screen and (min-device-width: 481px) and (max-device-width: 1536px) and (orientation:landscape) and (-webkit-device-pixel-ratio: 2)" />
<link rel="apple-touch-startup-image" href="icon/ipad_portrait_hires.png" media="screen and (min-device-width: 481px) and (max-device-width: 1536px) and (orientation:portrait) and (-webkit-device-pixel-ratio: 2)" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />

Going line by line: “viewport” tells the device how wide the viewable area should be…it’s important because an iPhone is 320 pixels wide (or 640 points in a retina display) but if you don’t explicitly set the viewport, it acts as if it’s 960 pixels wide (so if your content is 320, you’re getting a zoomed out version).

The “apple-touch-icon” links are icons for: iphone, ipad, iphone retina, ipad retina.
icon sizes

In my sample I made unique icons for each just to highlight that they were different, but really, I’d build one at 144×144, then scale it down and save it accordingly…unless I had some graphical element that fell apart in the smaller icons, then I’d tweak those versions accordingly. Here are some metrics around icon images: HIG

The “apple-touch-startup-image” links are for the splash screen. This is the image that pops up while the app loads. There are some intricacies around this, mostly with iPad. iPhone is straightforward: portrait, 320×460 for iPhone, 640×920 (don’t forget the media portion) for iPhone retina…but iPad is a little odd in that they accept a landscape version as well as portrait, but the images it accepts must always be taller than they are wide, so when you’re making the landscape images, you need to rotate the CONTENT, not the image. Not a big deal, but a critical distinction to make.

apple-mobile-web-app-capable” tells the phone that this page can be installed as an application (using the icon and splash screens we’ve defined)

apple-mobile-web-app-status-bar-style” can set the status bar style. There are a few options (default, black, black transparent) but I usually omit it if I want the default, or go with black if the style of the app is dark.

The stage is set, but we have some steps to get through before showing real content, so in order to let the user know we’re working on it, I like to start my apps with a little loader graphic that is then replaced when the real load is complete:

<body>
<img src="loader_graphic.gif"/>
</body>

So now the framework is there, but there are a few more subtle details before you get to the content. First, in order for anyone to see the icon and startup image, they have to install it. A user does this by tapping the arrow at the bottom of safari (on an iPhone, it’s at the top on iPad), and then ‘Add to Home Screen.’ So you’ll want to keep an eye out for users that can do so, and give them some guidance (more or less politely depending on the purpose…I’ve had builds where if it was not installed, you would only see an animation of how to do so):

installation guidance screen

But often you’ll just see a little tooltip box appear near the arrow as a gentle reminder. So how do we determine when do do any of this? We need to know it’s an eligible device, and that it’s not already installed. For the first portion of that I do the following:

var ipad=navigator.userAgent.match('iPad')?true:false;
var iphone=(navigator.userAgent.match('iPhone')||navigator.userAgent.match('iPod'))?true:false;

So now we know if it’s an iPad, or iPhone (I don’t distinguish between iPhone and iPod here). But is it installed? By testing the navigator.standalone property we can find out.

if((iphone||ipad)&&(!navigator.standalone)){
	//here is where you would handle install instructions like 
	//popping up an image about how to "Add to Home"
	str='please install me by pressing the arrow button at the ';
	str+=ipad?'top':'bottom';
	str+=' of the screen, then selecting \'Add to Homescreen\'';
	alert(str);		
}

So, we’re close, but not quite there because unlike a full computer, your device has an orientation. You’ll want to add a listener for orientationchange to the body, which can be as straightforward as:

<body onorientationchange="updateOrientation();">

in conjunction with a function like this:

function updateOrientation(){
	switch (window.orientation) {
	//while some things will be handled by the phone itself,
	//you may need to change some things manually (for instance,
	//you may have two elements stacked on top of each other,
	//and you'd like them to sit side by side in landscape mode
        case 0:
        alert ('Orientation: Portrait');
        break;
        case 90:
	alert ('Orientation: Landscape');
        break;
        case -90:
        alert ('Orientation: Landscape');
        break;
	case 180:
        alert ('Orientation: Portait');
        break;
    }
}

Obviously javascript is one approach, but some things can be handled with CSS Media Queries, so that’s a powerful tool to understand as well.

One detail about the various versions of iOS, is that CSS’ position:fixed isn’t supported before 5.0, so if I used that (and I do), I want to test for that as well:

if(iphone||ipad) { 
	if(/OS [2-4]_\d(_\d)? like Mac OS X/i.test(navigator.userAgent)) {  
        	alert('This is designed for iOS 5 and above, please update your device for proper functionality'); 
   	} else if(/CPU like Mac OS X/i.test(navigator.userAgent)) {
        	alert('This is designed for iOS 5 and above, please update your device for proper functionality');
    	} else {
		//iOS5+
	}
}

Okay. So the last piece is user interface. If you’re trying to mimic a native app, you’ll probably use elements like a navigation bar at the top, or tabs at the bottom, etc. Here is how I set up a starting point:

#nav {
width:100%;
height:40px;
position:fixed;
top:0;
text-align:center;
background-color:#29abe2;
color:#FFF;
font-size:150%;
padding-top:5px;
border-bottom:thin solid #444444; display:block;
font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
}
#main{ width:100%; margin-top:45px; }
#footer {
width:100%;
height:44px;
position:fixed;
bottom:0;
left:0;
text-align:center;
color:#FFF;
font-size:150%;
margin-top:5px;
border-top:thin solid #444444; 
background:#000000;
}

And when I’m ready to show the app (it’s installed, etc), I just:

document.body.innerHTML='<div id="nav"></div><div id="main"></div><div id="footer"></div>';

And then fill in my content to the main div (I have a function for each view that changes the .innerHTML, and that are linked to tabs that I build in the footer).

So if you’d like to give it a try, visit www.robotwoods.com/dev/web_app_demo from your mobile device. It should work in Android too, the way to install there is to bookmark it, then go to your bookmarks, and long press it, it’ll then give you some sort of Home Screen option. I also put the files in a zip here

Oh, you’ll see in the actual app that the footer has a gradient, I use this site for that: http://www.colorzilla.com/gradient-editor/ I think subtle gradients add a bit of warmth to a UI that may look dull with solid color blocks (see the icons as an example).

And, to minimize the size of your project (which enables faster download), you may want to minify your javascript. I think there are a variety of options for this, I usually just go with: http://jscompress.com/

Lastly, once everything is running as you like it, there’s one more way to improve performance (particularly in those applications that do not require a network connection, for instance, calling a web service for data): cache manifest

By crafting a particular file that outlines all of the files used in your project, the device will (with some limitations) store those files locally, so that it will run even when disconnected from the network. And, as the files are local, it loads much faster as well.

I have covered a number of the important areas of a web app, but there is plenty of other areas and approaches to learn about. What other tips and tricks do you use when building web apps?