The quota exceeded error means you are out of disk space. Google Chrome, Safari and other browsers were recently updated to be much more conservative in how much free space the disk must have for it to store extended caching files which Gigwell relies on.
It is limited to about 5MB and can contain only strings. LocalStorage is not accessible from web workers or service workers. Cookies have their uses, but should not be used for storage.
In short, you can't, but you can set a cookie via JavaScript 😉 Safari on iOS supports localStorage, but in Private Mode it simply throws an error when you try to save anything to it, which is not great. Also it breaks the behaviour of your app on iPhones and iPads.
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.
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.');
}
}
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')
}
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
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' );
...
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.
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.
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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With