Next article: Friday Q&A 2009-12-11: A GCD Case Study: Building an HTTP Server
Previous article: Friday Q&A 2009-11-27: Using Accessors in Init and Dealloc
Tags: html iphone javascript web
Welcome to another edition of Friday Q&A. This week I'm going to talk about building standalone iPhone web apps, web apps that have an icon on the home screen, and which start a separate program when tapped, just like native apps, a topic suggested by Mike Shields.
iPhone web apps have been in the news a fair bit lately as a way to bypass Apple's troublesome review process. While web apps aren't as capable as native apps, and almost certainly never will be, they're still interesting to work with simply because they're so much simpler to develop and deploy.
Neven Mrgan's Pie Guy is perhaps the most prominent example. It's a complete Pac-Man look-alike built as a standalone web app, albeit one which, because it's all HTML and JavaScript, requires a 3GS to run smoothly.
While the ability to build apps like this is well known, I haven't seen anything that gathers all the requisite parts in one place and walks through how to build one, so that's my intent today. I cribbed much of this information from dissecting how Pie Guy does it, and don't think for a moment that I discovered any of this stuff myself.
Getting Started
In this post I'll walk through the process of creating a basic standalone web app. My example app simply queries the JavaScript location object and displays your latitude and longitude. You can try the completed app, or get its source with Subversion:
svn co http://mikeash.com/svn/PhoneWebAppThe scope of this post is simply building the parts that are special to iPhone standalone web apps. The actual HTML and JavaScript for the app functionality itself is beyond the scope of the discussion, but there are of course a wide range of resources out there for them.
Separate Program
The first thing that you want in a standalone web app is for it to start as its own program, rather than loading into Safari, when the user taps your icon on the home screen. To make this happen, you simply add a tag to your
setting
apple-mobile-web-app-capable
to yes
, like so:
<meta name="apple-mobile-web-app-capable" content="yes">
tag:
<meta name="viewport"
content="width=device-width; height=device-height; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;">
Icons and Startup Images
Another thing that's a must for real iPhone apps is to have an actual icon and a startup image that's displayed while the app is loading. You can specify an icon by referencing it in a tag with
apple-touch-icon-precomposed
set as the relationship, and you can specify a startup image with apple-touch-startup-image
:
<link rel="apple-touch-icon-precomposed" href="icon.png">
<link rel="apple-touch-startup-image" href="default.png">
Putting the above together, and with the page title and link to the app's JavaScript code, the entire tag looks like this:
<head>
<title>PhoneWebApp</title>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width; height=device-height; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;">
<link rel="apple-touch-icon-precomposed" href="icon.png">
<link rel="apple-touch-startup-image" href="default.png">
<script src="main.js" type="text/javascript" />
</head>
Body
The body of the page is, of course, where you put all the stuff for your web app to interact with the user. Beyond the basic functionality of your app, you'll also want some iPhone-specific sections. You'll want to display a different page to users who view the app on a non-iPhone browser, to tell them to reload it with an iPhone. You'll also want to display a different page to users who view the app on an iPhone, but who have not yet installed it, to tell them how to install it.
For this particular app, I also have three more pages. One is a "loading" page, which is visible on initial load and gives the computer something to display until the JavaScript kicks in. One is a page to display in case navigation services aren't available for some reason. And finally, I hae the real page that displays the navigation data.
Each conceptual "page" is actually a With all of that, here's what the Scripting Caching Even this problem can be solved, though. Specify a manifest file in the With the manifest in place, the iPhone will cache all resources locally when the user adds the bookmark, and the app will launch and function even when no data access is available. (Try enabling Airplane Mode to test it.) While developing the app, the cache manifest can be really annoying, so it's a good idea to remove it from the Conclusion For further reading, Apple's Safari Web Content Guide documents all of the special tags I used here, and has a lot of other useful information as well. Also, I linked to this at the beginning but it merits a second mention, Neven Mrgan's Pie Guy is a great example of these tehcniques that you can examine to see how it ticks on the inside. That's it for this week. Come back in another seven days for the next scarifying edition. Friday Q&A is driven by user ideas, so if you have an idea you'd like to see covered here, send them in!
Add your thoughts, post a comment:
Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.
tag looks like:
<body onload="load()">
<div id="loading" style="position: absolute; left: 0; top: 0;">
Loading....
</div>
<div id="noiphone" style="visibility: hidden; position: absolute; left: 0; top: 0;">
This is an iPhone web app. Load this page on an iPhone!
</div>
<div id="notinstalled" style="visibility: hidden; position: absolute; left: 0; top: 0;">
To install this web app, tap the + button at the bottom of the page, then select "Add to Home Screen".
</div>
<div id="nonavigation" style="visibility: hidden; position: absolute; left: 0; top: 0;">
Location services aren't available for some reason.
</div>
<div id="navigation" style="visibility: hidden; position: absolute; left: 0; top: 0;">
Longitude: <span id="longitude">N/A</span>
<br>
Latitude: <span id="latitude">N/A</span>
</div>
</body>
With the HTML out of the way, it's time to start scripting. The tag references a
load()
function which will get called once the page is loaded. The first thing it does is hide the "loading" page:
function load()
{
document.getElementById("loading").style.visibility = "hidden";
// no iPhone
if(navigator.appVersion.indexOf('iPhone OS ') < 0)
{
document.getElementById("noiphone").style.visibility = "visible";
}
else if(!window.navigator.standalone)
{
document.getElementById("notinstalled").style.visibility = "visible";
}
else if(!hasNavigation())
{
document.getElementById("nonavigation").style.visibility = "visible";
}
else
{
// we're on a phone, and installed standalone
// "real" app init code goes here
document.getElementById("navigation").style.visibility = "visible";
navigator.geolocation.watchPosition(positionWatcher);
}
}
function hasNavigation()
{
return (typeof navigator.geolocation != "undefined");
}
function positionWatcher(location)
{
document.getElementById("longitude").textContent = location.coords.longitude;
document.getElementById("latitude").textContent = location.coords.latitude;
}
The above code will work great to build a simple little web app that acts like a native app, with one significant exception: native apps work even when you have no data service, and this does not. tag at the top of the HTML file:
<html manifest="cache.manifest">
CACHE MANIFEST
, and then subsequent lines refer to the files that the app needs to access, one per line. The manifest file for this app looks like this:
CACHE MANIFEST
index.html
main.js
default.png
icon.png
text/cache-manifest
, otherwise iPhone Safari won't recognize it. The Subversion repository includes a .htaccess
file which declares that MIME type, but beware that if you're trying to test it with OS X's personal web sharing, you have to do some configuration hacking to enable .htaccess
support, which is beyond the scope of this post.
tag while you're working on it. Apple says that the browser will reload everything if there are any changes in the manifest file (like adding a blank line), but while working on this example I found this to be frustratingly unreliable.
iPhone web apps don't behave as well as native apps, and probably never will, but with these few simple tips you can bridge the gap much more closely than with just a simple web page, and create an app that completely bypass the App Store. Building an interesting and useful app with HTML and JavaScript once you're in is, of course, up to you!
Comments:
It's pretty boring, just a mortgage calculator, but viewing the source will show a few techniques that are improvements on Pie Guy.
E.g., there's no need to use JavaScript to detect things like whether the user is on an iPhone/iPod Touch, nor which orientation the phone is being held in. These can both be done using CSS @media queries. (I do use JavaScript to detect standalone mode so I can show an alert to users to nudge them towards installing the app as a proper icon on their home screen.)
I have a couple of games in development as well that use hardware accelerated animations as well. These should do more to show of what WebKit is capable of.
Anyway, great article! I'm glad to see these techniques gain a higher profile. Back in the day, when Apple declared that web apps were the iPhone SDK, developers groaned, but WebKit is finally becoming a viable platform for a lot of apps. The last pieces of the puzzle are going to be tighter integration with device APIs such as accelerometer, camera, compass, etc. Once Safari exposes that functionality, there will be even less need to build apps using Cocoa Touch.
> Once Safari exposes that functionality, there will be even less need to build apps using Cocoa Touch.
I had thought this was an open question, not a certainty. Has that changed of late?
I believe the < script src type / > is xhtml, right?
Sorry about the extra spaces in the tags - I added them to make sure that the tags weren't being interpreted.
/Mogens
Comments RSS feed for this page
http://www.phonegap.com/
A pretty neat piece of technology; their philosophy is to take advantage of the common web languages that most smart phones can interpret through their web browsers in order to make device agnostic, cross compatible apps.