We're hiring!

We're actively seeking designers and developers for all three of our locations.

iOS Private Browsing + localStorage = Frustration

Recently I had been working on a little mobile web app that fetches JSON data, puts it in localStorage, and then renders the views for it. Since the data could get large, there is a check that occurs before the full JSON dump. If we have localStorage data, then the app asks the server what version is the latest. The app will then either render the data if it’s the latest or pull more data down from the server and stash it away and render it to the screen.

Everything was fine and dandy, until random iOS devices started having errors in the JavaScript. Initially it looked to be iPhone 4S models on iOS 6 that had the problem. As we began investigating, we realized other iOS devices of various models and software versions were getting the same problem too. We were stumped.

Upon attempting a pairing of the device with the Safari desktop browser for the inspector use (more info on that feature here), we finally had a break. Safari was not pairing properly because the device was in Private Browsing mode. When we turned off Private Browsing mode, the pairing with Safari worked, and the application ran fine. Further investigation into the problem showed that every device affected had Private Browsing enabled.

iOS Private Browsing + localStorage

Whats the problem with iOS Private Browsing? As you would expect, it clears cookies and history and doesn’t allow it to persist after closing mobile Safari. One would assume that localStorage would be accessible and cleared after close of the app, but there interesting caveats.

In Private Browsing mode, localStorage can be read but not written. Our code was attempting to write to the localStorage upon successful download of the JSON data. After many attempts at decent workarounds, we came up with a helper function that can be called to return the reading and writing state of localStorage:

hasStorage = ->
  try
    mod = new Date
    localStorage.setItem mod, mod.toString()
    result = localStorage.getItem(mod) == mod.toString()
    localStorage.removeItem mod
    return result

As you can see, we check to make sure localStorage is defined by writing to localStorage and making sure that object comes back out the same. The reason we had to do this in a try/catch block is because iOS localStorage writes operations throw an exception when attempted in Private Browsing mode. We can call this helper to give the state of localStorage and conditionally read and write to localStorage depending on the capability we have available.
 

Jared Sartin (33 Posts)

Web and mobile developer. Works in Ruby/RoR and JavaScript frameworks.

Strong passion for game development, UI design, and general UX.

This entry was posted in iOS / OS X and tagged , . Bookmark the permalink. Both comments and trackbacks are currently closed.

One Comment

  1. Posted April 18, 2013 at 9:50 am

    Great find! I have the same issue, even on Safari Mac.

    I get the following error:
    QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota.

    It seems it is because I use store.js.

    I have open an issue:
    https://github.com/marcuswestin/store.js/issues/66