del.icio.us .:. tweet

Backchannel: an Offline-Capable Web App for the iPhone .:. kentbrewster.com

In the past I've build some Web-based applications--iClipper and iClock, among others--that were meant to hack around the problem of storing applications on your (non-jailbroken) iPhone or iPod Touch. I did this through a completely unusable combination of bookmark-editing and base64-encoding that no sane user would ever actually endure. Recent releases of Mobile Safari have made creating, deploying, and updating offline-capable Web apps for the iPhone much easier.

Backchannel: Possibly the Smallest Useful Offline Application That Isn't a Flashlight

Backchannel is a re-implementation of @jerrymichalski's Red:Green Cards. It's intended for use during seminars and other meetings where the audience needs to communicate a limited set of one-way messages to the speaker. While running Backchannel, your phone's screen will turn red while vertical and green while horizontal. This could be used for feedback (faster/slower, go/stop) or voting (yes/no). There's no technical reason why the colors couldn't be different, or programmable, or a third color added to the plus-ninety-degrees orientation.

While I've seen several red:green versions, they suffer from a) having to be downloaded from the App Store or b) having to be downloaded live from a Web site, which may or may not be responding from your chair at SxSW.

Enter the offline version. To try it out, fire up your iPhone and go here:

http://kentbrewster.com/backchannel/bc.html

Once you're done flipping your device up and down to see the color change, try saving a local copy. To do this, touch the + button at the bottom of your browser and choose Add to Home Screen. The Add to Home panel should come up, with Backchannel's red-and-green icon and title. Touch Add and you should see it on your home screen.

To see if local storage worked, go to Settings, put your device into Airplane Mode, turn off your Wi-Fi connection, exit Settings, and try Backchannel again. If all is good, Backchannel should behave exactly the same as it did before, with the important difference that all browser chrome should be completely gone, leaving only the very top ten-pixel-thick connection/battery status bar.

Important Note from the Voice of Experience: if you forget to turn your network back on when you're done testing, you will be unable to tell that you have not broken your iPhone. If you take it back to the Genius Bar and ask for help, they will point at you and laugh.

How to Make This App

Besides the one that tells how everything works (which you are reading now) there are five files in this directory:

  • bc.html -- the HTML structure
  • presentation.css -- the CSS
  • behavior.js -- the JavaScript
  • icon.png -- the icon used to represent the application on the iPhone desktop
  • main.manifest -- the list of files to be stored locally

Structure

This is basic HTML with some custom links, attributes, and metas, as discussed below.

<!DOCTYPE html>
<html manifest="main.manifest">
   <head>
      <title>Backchannel</title>
      <meta name="viewport"
         content="width=device-width;
                  initial-scale=1.0;
                  maximum-scale=1.0;
                  minimum-scale=1.0;
                  user-scalable=0;"
      />
      <meta name="apple-mobile-web-app-capable"
         content="yes"
      />
      <meta names="apple-mobile-web-app-status-bar-style"
         content="black"
      />
      <link rel="apple-touch-icon" href="icon.png" />
      <link rel="stylesheet" type="text/css" href="presentation.css" media="screen" />
   </head>
   <body>
    <script src="behavior.js"></script>
   </body>
</html>

Those custom items:

  • manifest -- points at the list of files that we want to save locally.
  • viewport -- sets the initial width of the document to whatever the width of the device is, and tells it not to scale the size, ever. This plus preventing default on touchmove (in the behavior, below) causes the app to be rock-steady.
  • apple-mobile-web-app-capable -- lets the device know that this page can be saved as a mobile web app.
  • apple-mobile-web-app-status-bar-style -- hides the status bar
  • apple-touch-icon -- points to the icon we want to use on the home screen

Yep, that's the HTML5 doctype up there. Please promise me right here and now that you will never build anything for an iPhone that's not HTML5.

Presentation

Here we tell Safari what color to make each orientation of the screen:

.landscape {background-color:green;}
.portrait {background-color:red;}

All we're going to do to make this happen is to change the main body's CSS class when the device rotates.

These colors are, of course, trivially easy to change for color-blind speakers.

Behavior

Here we listen for the onorientationchange event and change the screen's CSS class name accordingly.

(function (w, d, k) {
   var $;
   $ = w[k] = {};
   $.w = w;
   $.d = d;
   $.f = (function () {
      return {
         init : function () {
            $.s = $.d.getElementsByTagName('BODY')[0];
            $.s.addEventListener("touchmove", function (e) { e.preventDefault(); }, false);
            $.w.setTimeout($.f.orient, 0);
            $.w.onorientationchange = function () {
               $.f.orient();
            };
         },
         orient : function () {
            if ($.w.orientation === 0) {
               $.s.className = 'portrait';
            } else {
               $.s.className = 'landscape';
            }
            $.w.setTimeout($.f.home, 0);
         },
         home : function () {
            $.w.scrollTo(0, 1);
         }
      };
   }());
   $.f.init();
}(window, document, 'backchannel'));

We're listening for touchmove in order to prevent the screen from bobbling about when the user touches and drags.

Icon

Here's our happy little icon, icon.png:

We could have also used a .jpg here, but it's significantly bigger and uglier.

Manifest

Here we tell the app what files we want it to save locally:

CACHE MANIFEST
# v20091210
presentation.css
behavior.js
icon.png

Our manifest starts with CACHE MANIFEST. This is required.

Next up is a comment. If your device is online when the user starts your app, it will ping the remote version of the manifest before loading, to make sure nothing needs to be updated. If I make a change to behavior.js, I'll also change something in my manifest--that version number, most likely--to let the client know it needs to pull fresh copies of the cached files.

Finally, list all files. We could be using relative path names here--caution: these will be relative to the manifest's path, not the path of the document referencing the manifest--but we're keeping things simple.

Our manifest does not include bc.html, since that's the file that points to main.manifest.

Important: Why This Didn't Work the First Time I Tried It

Unless you've been reading ahead, chances are excellent that your Web server won't send main.manifest with the proper content type. If your server sends your manifest as text/plain, it is guaranteed to silently fail. What you want is text/cache-manifest. To make it work, I added the following directive to my Apache .htaccess file:

AddType text/cache-manifest .manifest

To make sure it was working, I used Web-Sniffer to make sure my Content-Type was being set correctly:

http://web-sniffer.net/?url=http%3A%2F%2fkentbrewster.com%2Fbackchannel%2Fmain.manifest

... and it was. Huge thanks to @thecssninja for this tip, and much useful material about creating offline apps; please see How To Create Offline Web Apps On The iPhone for more. (The Ninja also has tips for those of you stuck with IIS and other inferior Web servers.)

Further Reading

As always, have fun with this, please let me know how it goes, and feel free to repurpose the code as long as you're within the limits of my Right and Permissions.

Comments from before Disqus:

Ryan .:. 2010-07-25 17:41:57
Nice idea to make it a webapp. just a thought you probably wouldn't need to use javascript at all.

You can do the orientation changes with CSS3 media queries e.g.

@media screen and (orientation:landscape) {
body { background: green; }
}

And to disable the user selecting the body so the copy action doesn't happen you could do this:

* {
-webkit-user-select: none;
-webkit-touch-callout: none;
-webkit-user-modify: none;
}

And since Android also support orientation media queries and it's webkit based it should work no problem on it either. Most likely blackberries new webkit browser will also work.
Mike Butcher .:. 2010-07-23 12:02:47
Thanks for sharing. Bill W. - I have simliar project. I download the customer info to the phone then allow the Technician to update the info and save it to a local sqlite database. Once the Technician is in a cell phone coverage area or wifi hotspot then they can call a xhr function to create a document on the Server.

I have even made a signature capture area so we can collect signatures and send them back as a jpg to the document.

This is cool stuff!
Bill Woodland .:. 2010-07-12 10:39:47
Thanks, Kent! I had a rate calculator I made for an insurance company, with html, css, and javascript all in one file, so they could save it onto their Windows Mobile phones, but it would only work online on an iPhone. With the help of this article, I made the mainfest file, got the text/cache-manifest setup, split the html, css, and javascript into separate files, made the icon and poof! Instant iPhone app! Next thing to play around with is storing customer info locally, and once the sale is finalized, use XHR to post it to their server.

yup...gimme that cookie.

Copyright Kent Brewster 1987-2014 .:. FAQ .:. RSS .:. Contact