iPhone offline web applications: tips and gotchas

January 24th, 2010 § Comments Off on iPhone offline web applications: tips and gotchas § permalink

I spent this evening updating my “iPhone VR” Javascript/CSS demo to work with iPhone OS 3.0 (it had stopped working 100% correctly since the OS update). I also decided to spend some time making it work as an offline-capable web application.

The basic process of making your web app cacheable offline is, in theory, fairly straightforward, and generally well-documented at Apple’s website. I ran into some interesting headaches though.

Gotchas:

  • First thing to note is that your web server must serve your cache manifest file with a MIME type of text/cache-manifest. This may mean editing mime.types to add a line that looks like this:
    text/cache-manifest   manifest

    or perhaps a line in httpd.conf that looks like this:

    AddType text/cache-manifest  .manifest
  • The next thing that had me stumped for a while: while your web server may know how to take the URL http://example.com/webapp/ and automatically and invisibly serve up the file http://example.com/webapp/index.html, your offline web app knows nothing of this mapping of webapp/ to webapp/index.html. If the URL bar reads http://example.com/webapp/ and you save it locally using a home screen bookmark, it will fail to launch correctly if the device is offline, even if index.html file has been cached. The device simply does not know to look for webapp/index.html instead of webapp/. Thus, you must ensure that the URL bar reads webapp/index.html before the user makes a home screen bookmark.
  • At first, I thought I would enforce the above with a tiny bit of JavaScript that simply reads window.location and redirects to window.location + "index.html" if the URL ends in a slash. This worked while online, but it broke my app when offline, even when the redirection was not taken. Why? It seems that any reference to window.location in your script is treated as network access. Since the device is offline, it generates an error alert.

    Instead, I made my app live at webapp/main.html, and created a small webapp/index.html file that simply redirects to main.html. (I could have used a server redirect inside of an .htaccess file instead, but I chose not to, for no particular reason.)

Tips:

  • You can specify a custom icon for the home screen bookmark using a <link> element, like so:
    <link rel="apple-touch-icon" href="custom_icon.png" />

    If you don’t want iPhone to automatically apply the “shine” effect on your icon, use the following instead:

    <link rel="apple-touch-icon-precomposed" href="custom_icon.png" />
  • Since iPhone OS 3.0, offline web apps can have custom splash screens, just a like a native app! Simply add a link element to your web page:
    <link rel="apple-touch-startup-image" href="/splash.png" />

If you want to see the results of all this, go to http://bunnyherolabs.com/iphone/xform/ on your iPhone or iPod Touch, then click the “+” (add bookmark) button and choose “Add to Home Screen.” Now you can click on the “Panorama” icon on your home screen to see the demo at any time, even when not connected to the internet.

How universal is “universal”?

January 1st, 2010 § Comments Off on How universal is “universal”? § permalink

Tip: if building a +universal variant in MacPorts (for example, because you’re trying to build and install the Python Imaging Library), then you should check /opt/local/etc/macports/macports.conf to see what MacPorts considers “Universal”:

universal_archs

The machine architectures to use for +universal variant (multiple entries must be space delimited). Options include: ppc, i386, ppc64, x86_64

Default: x86_64 i386 (ppc i386 for 10.5 and earlier)

(from http://guide.macports.org/#internals.configuration-files)

On Mac OS X 10.6 (Snow Leopard), the default value for universal_archs is x86_64 i386. Note that it does not include the PowerPC architecture. This makes perfect sense, because Snow Leopard doesn’t run on PowerPCs. Unfortunately, when installing PIL with pip, it builds for PPC as well, and thus requires PPC architectures in its dependent libraries, even on 10.6. I do not know how to disable PPC support in PIL (or pip?). All of this Python extension building stuff is new to me :)

The upshot: if you are running 10.6, then you must edit macports.conf and add “ppc” to universal_archs before you follow the directions in the linked article.

iPhone newbie lessons

March 20th, 2009 § Comments Off on iPhone newbie lessons § permalink

It’s been a long time since I messed around with Objective-C, even a little bit.

A couple of things that have bitten me lately:

  • @selector(apply:) is different than @selector(apply)! Note the lack of a colon in the second one. The colon means that there is one parameter to be passed with the message. No colon, no parameter. This makes a big difference when passing a selector as an argument to something like NSArray makeObjectsPerformSelector:.
  • I keep forgetting to pass nil as the terminating argument to methods like NSArray initWithObjects:. Forgetting the terminating nil will cause the iPhone to crash with a Bad Access exception as it runs off the end of the stack into the weeds.

And one very valuable debugging tip I found here:
how to find out what exception was thrown in the Xcode debugger. When an uncaught exception kills your iPhone app, Xcode doesn’t really have much information at that point. To remedy that, go to Run -> Show -> Breakpoints and in the Breakpoints window double-click on the entry that says “Double-Click for Symbol.” Enter “objc_exception_throw” and press Return. Now, when you run your app in the debugger, any exception will stop there and let you see the exception type.