for no good reason this is making me angry about the iPad

This will interest nobody, but I need to complain (and perhaps spare others some fruitless googling): the iPhone version of Safari seems to be broken when using AJAX in offline caching in HTML5 applications.

“Why would you want to use AJAX in an offline app?” you ask, and it’s a good question. Well, I’m trying to make an HTML5 interface to the office door-opening thingy, because the native iPhone version, being a non-App Store offering, will be expiring soon in an irritating way (insert entirely different anti-Apple rant here).

Users will, of course, need network access to open the door. But I don’t see any particular reason to make users download the 300-some-K of assets necessary to make a proper jQTouch iPhone app work every time they need to get into the office (or change their settings) — particularly since I’m using a little onSuccess wav file (just for fun). So I set about using some offline caching to keep all that material decidedly on the iPhone, and not subject to the whims of Safari’s caching policy.

This works fine until it’s time to issue the request to open the door. The cache manifest file, which allows you to specify which items will be stored offline, allows for a whitelisted “NETWORK” section that will never be subject to the cache. Typically, you add your AJAX endpoint(s) to that section. Except in this case that AJAX request is made to a different domain than the one where the application lives (and when the application is being served from an offline cache who knows where it *thinks* it lives?), utilizing the now years-old-but-still-awfully-clever JSONP to escape the same-domain restrictions of a vanilla AJAX request.

This all works fine, according to other people. But not for me. My application’s endpoint uses SSL in order to protect users’ door-opening credentials, and that seems to make the difference. It just. doesn’t. work. If you change the cache manifest, prompting the app to be reloaded, everything will work fine. Once. When you reload the app, pulling from the offline cache, the AJAX request produces a momentary activity indicator in the status bar, but there’s no response. (No error, either! Thanks a bunch, Apple and/or jQuery.)

I’ve tried updating jQuery. I’ve tried whitelisting “*”. I’ve tried whitelisting the http version of the endpoint, and just the path portion of the endpoint, and the full domain name of the endpoint (SSL and not). No dice.

Here’s the relevant code, for anyone feeling particularly curious/masochistic. I’ve tried a lot of variations on it — at this point I’m pretty sure that this aspect of iPhone Safari circa OS 3.1.2* is just broken. Ah well. No caching isn’t the end of the world, but it is kind of annoying.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// I've tried it with and without the callback param (as I go back and forth between $.getJSON and $.ajax) -- doesn't make a difference
var url = 'https://gatekeeper.sunlightlabs.com/[SECRET]/api/?device_id=' + localStorage.deviceID + '&pin=' + PIN + '&format=json&callback=?';

$.ajax({
  url: url,
  dataType: 'jsonp',
  error: function(XMLHttpRequest, textStatus, errorThrown){
    alert(textStatus); // this never happens
  },
  success: function(data){
    localStorage.message = 'Welcome, ' + data.first_name + '!';
    localStorage.lastOpen = (new Date()).getTime();
    if(localStorage.disableSound=='on')
    {
      alert(localStorage.message);
      localStorage.message = '';
    }
    else
    {
      location.href = 'zelda.wav'; // play the zelda door-opening music on success        
    }
  }
});

* Gotta keep that tethering

2 Responses to “for no good reason this is making me angry about the iPad”

  1. Jason says:

    I was trying to find a solution for Ajax calls not working also. Not sure if this will work for your purposes, but I found (using jQtouch) that if I dynamically generate a timestamp on the Ajax call this allowed caching to work.

    e.g.
    Dynamically create the link with Javascript:
    Ajax link

  2. Jason says:

    sorry the code got turned into a link, should be:

    var date = New Date();
    var timestamp = date.getTime();

    $(‘divID’).html(“Ajax Link“);

Leave a Reply