228

Using LocalStorage on iPhone with iOS 7 throws this error. I've been looking around for a resolvant, but considering I'm not even browsing in private, nothing is relevant.

I don't understand why localStorage would be disabled by default in iOS 7, but it seems it is? I've tested on other websites as well, but with no luck. I even tried testing it using this website: http://arty.name/localstorage.html, but it doesn't seem like it's saving anything at all for some weird reason.

Has anyone had the same problem, only they've had luck fixing it? Should I switch my storage method?

I tried hard-debugging it by only storing a few lines of information, but to no avail. I used the standard localStorage.setItem() function to save.

Nict
  • 3,066
  • 5
  • 26
  • 41
  • 2
    It usually means you tried to store something with a size that exceeded available storage space. What browser are you using (Safari, Chrome, etc.)? Can you share a little more of the code you have been using and if possible the data you're trying to store. –  Jan 16 '14 at 21:32
  • 3
    This should be considered as a bug or issue on the Safari side. It does not make sense that you can not use localStorage in incognito mode... – Maksim Luzik Feb 01 '16 at 12:09
  • 1
    Use [a feature detect that tests for this specific issue](https://github.com/download/storage-available). If storage is not available, consider shimming localStorage with [memoryStorage](https://github.com/download/memorystorage). *disclaimer: I am the author of the linked packages* – Stijn de Witt Jan 27 '17 at 14:26
  • Similar question with great solutions: http://stackoverflow.com/questions/35607724/ios-safari-private-browsing-localstorage-and-sessionstorage-support/43670472#43670472 – theUtherSide Apr 28 '17 at 01:35
  • 1
    In April 2017 a patch was merged into Safari, so it aligned with the other browsers. Will likely land in Safari 11. https://bugs.webkit.org/show_bug.cgi?id=157010 – sandstrom Aug 02 '17 at 15:00
  • 2
    I can confirm this has been fixed in Safari iOS 11. Tested Private browsing + sessionStorage.setItem() then sessionStorage.getItem() successfully on iPhone6 and iPhone8. – Kevin Gaudin Oct 17 '17 at 12:49
  • This also happens when you run into the regular ~10MB [size limit of localStorage](https://stackoverflow.com/questions/2989284/what-is-the-max-size-of-localstorage-values) Another test site is https://storage-quota.glitch.me/ – cachius May 31 '23 at 15:26

9 Answers9

377

This can occur when Safari is in private mode browsing. While in private browsing, local storage is not available at all.

One solution is to warn the user that the app needs non-private mode to work.

UPDATE: This has been fixed in Safari 11, so the behaviour is now aligned with other browsers.

sandstrom
  • 14,554
  • 7
  • 65
  • 62
Cristian Dinu
  • 3,888
  • 1
  • 14
  • 7
  • 4
    Your post was just incredibly helpful and timely for me today (less than 24 hours later). For reference, here's how to turn on/off private browsing: http://www.imore.com/how-use-private-browsing-ios-7-safari – Nick May 05 '14 at 04:24
  • 12
    +1 fixed my issue. I was checking for the existence of LocalStorage (`if( typeof Storage != 'undefined' ) { ... }`) before trying to load and save information but getting this error. Turns out `Storage` is still defined even when it's unusable. Using try/catch from now on whenever I use LocalStorage. – stevendesu Sep 15 '14 at 13:36
  • Thanks! Weird error by safari. Should have been more informative. :D – Sunny R Gupta Feb 19 '16 at 14:24
  • I fixed my issue using the following: ? – Bogdan Mates Jul 28 '16 at 12:35
  • This looks like a good idea, yet, looking at the code, it seems that the FakeLocalStorage is not persistent and would give your users a sense of false security that their data is stored. If you replace it with cookies the cookies themselves would be deleted. So this is a no-no as well. – Cristian Dinu Jul 28 '16 at 17:19
  • 2
    A fix may be incoming as of Safari Tech Preview 29: "Fixed QuotaExceededError when saving to localStorage in private browsing mode or WebDriver sessions". See https://developer.apple.com/safari/technology-preview/release-notes/ – Marc Baumbach May 03 '17 at 23:42
  • 2
    This can also occur if [storage limit is reached](https://medium.com/@nisalperi/how-i-solved-the-localstorage-quotaexceedederror-dom-exception-22-b69db46f0cee) which can be easily done by saving images for example. – csalmeida Oct 19 '17 at 08:55
  • It has been fixed in iOS 11 – guido Nov 06 '17 at 20:21
  • this has just been triggered on an iPhone with iOS 10.3.3 on one of my public projects... we are in 2018 now.. wtf apple.. even android on ice cream sandwitch would get the latest chrome version... – GottZ Mar 13 '18 at 22:50
108

As mentioned in other answers, you'll always get the QuotaExceededError in Safari Private Browser Mode on both iOS and OS X when localStorage.setItem (or sessionStorage.setItem) is called.

One solution is to do a try/catch or Modernizr check in each instance of using setItem.

However if you want a shim that simply globally stops this error being thrown, to prevent the rest of your JavaScript from breaking, you can use this:

https://gist.github.com/philfreo/68ea3cd980d72383c951

// Safari, in Private Browsing Mode, looks like it supports localStorage but all calls to setItem
// throw QuotaExceededError. We're going to detect this and just silently drop any calls to setItem
// to avoid the entire page breaking, without having to do a check at each usage of Storage.
if (typeof localStorage === 'object') {
    try {
        localStorage.setItem('localStorage', 1);
        localStorage.removeItem('localStorage');
    } catch (e) {
        Storage.prototype._setItem = Storage.prototype.setItem;
        Storage.prototype.setItem = function() {};
        alert('Your web browser does not support storing settings locally. In Safari, the most common cause of this is using "Private Browsing Mode". Some settings may not save or some features may not work properly for you.');
    }
}
philfreo
  • 41,941
  • 26
  • 128
  • 141
  • 1
    Why add the setItem to the Storage object if you wont be able to use it anyway? – Necromancer Jun 22 '16 at 17:53
  • 4
    The point of my snippet is to simply ignore JS errors from being thrown if you want your app to not be totally broken in Safari private mode. – philfreo Jun 23 '16 at 18:03
16

I use this simple function, which returns true or false, to test for localStorage availablity:

isLocalStorageNameSupported = function() {
    var testKey = 'test', storage = window.sessionStorage;
    try {
        storage.setItem(testKey, '1');
        storage.removeItem(testKey);
        return true;
    } catch (error) {
        return false;
    }
}

Now you can test for localStorage.setItem() availability before using it. Example:

if ( isLocalStorageNameSupported() ) {
    // can use localStorage.setItem('item','value')
} else {
    // can't use localStorage.setItem('item','value')
}
DrewT
  • 4,983
  • 2
  • 40
  • 53
  • Have I missed something? Why is `window.sessionStorage` been used instead of `window.localStorage` for a method called `isLocalStorageNameSupported`? – Ithar Mar 29 '16 at 09:37
  • 1
    @lthar - see the documentation here: http://www.w3schools.com/html/html5_webstorage.asp Most importantly this part: `HTML local storage provides two objects for storing data on the client: window.localStorage - stores data with no expiration date window.sessionStorage - stores data for one session (data is lost when the browser tab is closed)` – DrewT Mar 29 '16 at 18:05
  • @DrewT, but what's the difference in this situation if you remove your test key? It doesn't matter where I will store my test key if I am going to delete it. Am I wrong? Why is the session storage better that the local one? – Vladyslav Turak Sep 06 '16 at 09:33
  • 2
    @TurakVladyslav you're right there is really no difference here except that using `sessionStorage` makes it more manageable for setting breakpoints if you want to test your development. There is no true argument for which is "better" and it's really just a personal preference here that errs on the side of caution. The main thing to note is that both `sessionStorage` and `localStorage` are both implementations of the HTML5 webstorage API. – DrewT Sep 06 '16 at 16:26
5

I happened to run with the same issue in iOS 7 (with some devices no simulators).

Looks like Safari in iOS 7 has a lower storage quota, which apparently is reached by having a long history log.

I guess the best practice will be to catch the exception.

The Modernizr project has an easy patch, you should try something similar: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js

defvol
  • 14,392
  • 2
  • 22
  • 32
3

Here is an expanded solution based on DrewT's answer above that uses cookies if localStorage is not available. It uses Mozilla's docCookies library:

function localStorageGet( pKey ) {
    if( localStorageSupported() ) {
        return localStorage[pKey];
    } else {
        return docCookies.getItem( 'localstorage.'+pKey );
    }
}

function localStorageSet( pKey, pValue ) {
    if( localStorageSupported() ) {
        localStorage[pKey] = pValue;
    } else {
        docCookies.setItem( 'localstorage.'+pKey, pValue );
    }
}

// global to cache value
var gStorageSupported = undefined;
function localStorageSupported() {
    var testKey = 'test', storage = window.sessionStorage;
    if( gStorageSupported === undefined ) {
        try {
            storage.setItem(testKey, '1');
            storage.removeItem(testKey);
            gStorageSupported = true;
        } catch (error) {
            gStorageSupported = false;
        }
    }
    return gStorageSupported;
}

In your source, just use:

localStorageSet( 'foobar', 'yes' );
...
var foo = localStorageGet( 'foobar' );
...
Stickley
  • 4,561
  • 3
  • 30
  • 29
2

As already explained in other answers, when in Private Browsing mode Safari will always throw this exception when trying to save data with localStorage.setItem() .

To fix this I wrote a fake localStorage that mimics localStorage, both methods and events.

Fake localStorage: https://gist.github.com/engelfrost/fd707819658f72b42f55

This is probably not a good general solution to the problem. This was a good solution for my scenario, where the alternative would be major re-writes to an already existing application.

Josef Engelfrost
  • 2,955
  • 1
  • 29
  • 38
  • What exactly does it fix? It doesn't persist anything, so what's the point? – Esben Skov Pedersen Feb 26 '17 at 17:56
  • 1
    It "fixes" Safari when in private browsing mode. (This is not clear in my answer, thanks for pointing that out. I'll edit my answer). Nothing is supposed to be persisted when in private browsing mode regardless, so not persisting is not a relevant issue here. What this fixed for me was to allow users to run an already existing application, without major re-writes, even when in Private Browsing mode in Safari. – Josef Engelfrost Mar 01 '17 at 11:15
2

Update (2016-11-01)

I was using AmplifyJS mentioned below to work around this issue. However, for Safari in Private browsing, it was falling back to a memory-based storage. In my case, it was not appropriate because it means the storage is cleared on refresh, even if the user is still in private browsing.

Also, I have noticed a number of users who are always browsing in Private mode on iOS Safari. For that reason, a better fallback for Safari is to use cookies (if available). By default, cookies are still accessible even in private browsing. Of course, they are cleared when exiting the private browsing, but they are not cleared on refresh.

I found the local-storage-fallback library. From the documentation:

Purpose

With browser settings like "Private Browsing" it has become a problem to rely on a working window.localStorage, even in newer browsers. Even though it may exist, it will throw exceptions when trying to use setItem or getItem. This module will run appropriate checks to see what browser storage mechanism might be available, and then expose it. It uses the same API as localStorage so it should work as a drop-in replacement in most cases.

Beware of the gotchas:

  • CookieStorage has storage limits. Be careful here.
  • MemoryStorage will not persist between page loads. This is more or less a stop-gap to prevent page crashes, but may be sufficient for websites that don't do full page loads.

TL;DR:

Use local-storage-fallback (unified API with .getItem(prop) and .setItem(prop, val)):

Check and use appropriate storage adapter for browser (localStorage, sessionStorage, cookies, memory)

Original answer

To add upon previous answers, one possible workaround would be to change the storage method. There are a few librairies such as AmplifyJS and PersistJS which can help. Both libs allow persistent client-side storage through several backends.

For AmplifyJS

localStorage

  • IE 8+
  • Firefox 3.5+
  • Safari 4+
  • Chrome
  • Opera 10.5+
  • iPhone 2+
  • Android 2+

sessionStorage

  • IE 8+
  • Firefox 2+
  • Safari 4+
  • Chrome
  • Opera 10.5+
  • iPhone 2+
  • Android 2+

globalStorage

  • Firefox 2+

userData

  • IE 5 - 7
  • userData exists in newer versions of IE as well, but due to quirks in IE 9's implementation, we don't register userData if localStorage is supported.

memory

  • An in-memory store is provided as a fallback if none of the other storage types are available.

For PersistentJS

  • flash: Flash 8 persistent storage.
  • gears: Google Gears-based persistent storage.
  • localstorage: HTML5 draft storage.
  • globalstorage: HTML5 draft storage (old spec).
  • ie: Internet Explorer userdata behaviors.
  • cookie: Cookie-based persistent storage.

They offer an abstraction layer so you don't have to worry about choosing the storage type. Keep in mind there might be some limitations (such as size limits) depending on the storage type though. Right now, I am using AmplifyJS, but I still have to do some more testing on iOS 7/Safari/etc. to see if it actually solves the problem.

John
  • 3
  • 3
  • Editor John: I realize you and Jonathan Alzetta are probably the same account and you're just trying to improve your answer, but if so you should really log-in as Jonathan Alzetta and edit this answer, and then it won't go through the review queue. Recover your account if you need to. – DavidS Nov 01 '16 at 16:38
1

In April 2017 a patch was merged into Safari, so it aligned with the other browsers. This was released with Safari 11.

https://bugs.webkit.org/show_bug.cgi?id=157010

sandstrom
  • 14,554
  • 7
  • 65
  • 62
0

This question and answer helped me solve a specific problem with signing up new users in Parse.

Because the signUp( attrs, options ) function uses local storage to persist the session, if a user is in private browsing mode it throws the "QuotaExceededError: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota." exception and the success/error functions are never called.

In my case, because the error function is never called it initially appeared to be an issue with firing the click event on the submit or the redirect defined on success of sign up.

Including a warning for users resolved the issue.

Parse Javascript SDK Reference https://parse.com/docs/js/api/classes/Parse.User.html#methods_signUp

Signs up a new user with a username (or email) and password. This will create a new Parse.User on the server, and also persist the session in localStorage so that you can access the user using {@link #current}.

clayostrom
  • 51
  • 1
  • 6