Saving localStorage to a more secure folder location


If you are worried whether your localStorage will persist after an iOS version upgrade or be backed up to iCloud, one coder, Kerri Shotts, has graciously given us a Javascript script that saves the localStorage data to a secure folder within iOS. Her code periodically saves localStorage data at set intervals. (This code has been updated. See bottom of article.)

When I asked her how to integrate this with PhoneGap on the index.html page, I got this from her:

<script type=”text/javascript” charset=”utf-8″>
function onDeviceReady()
{
setTimeout ( function() { loadLocalStorageAndSync ( onLocalStorageLoaded ); }, 500 );
}

function onLocalStorageLoaded()
{
// what you would have had in onDeviceReady()
var root = this;

// When this function is called, PhoneGap has been initialized and is ready to roll
function onDeviceReady()
{
var cb = ChildBrowser.install();
if(cb != null)
{
cb.onLocationChange = function(loc){ root.locChanged(loc); };
cb.onClose = function(){root.onCloseBrowser()};
cb.onOpenExternal = function(){root.onOpenExternal();};

window.plugins.childBrowser.showWebPage(“https://iphonedevlog.wordpress.com&#8221;);

}
}

function onCloseBrowser()
{
alert(“In index.html child browser closed”);
}

function locChanged(loc)
{
alert(“In index.html new loc = ” + loc);
}

function onOpenExternal()
{
alert(“In index.html onOpenExternal”);
}

$(document).bind(“mobileinit”, function()
{
$.mobile.defaultPageTransitionĀ  = ‘none’;
});
}

document.addEventListener (“deviceready”, onDeviceReady, false);

function onDeviceReady() {
document.addEventListener(“pause”, onPause, false);
}

// Handle the pause event
function onPause() {
persistData();
}
</script>

</head>

Then at the bottom of my page I have:

<script type=”text/javascript”>
loadData(); // this is my line – this loads data from localStorage back into the fields

var persistentStorage = function()
{
// Get self so we don’t have to be funny when a timeout calls us…
var self = this;

/**
* Called when the filesystem is successfully returned. Will attempt to get “localStorage.dat”
* access (and create it if necesssary).
*/
self.gotFStoWrite = function (fileSystem)
{
fileSystem.root.getFile(“localStorage.dat”, {create: true, exclusive: false}, self.gotFileEntrytoWrite, self.fail);
}

/**
*
* Called when the filesystem is successfully returned. It will attempt to open “localStorage.dat” for
* read-only access.
*/
self.gotFStoRead = function (fileSystem)
{
fileSystem.root.getFile(“localStorage.dat”, null, self.gotFileEntrytoRead, self.fail);
}

/**
*
* Called when localStorage.dat is obtained for writing. It will create a fileWriter
* which will actually write the contents of localStorage.
*/
self.gotFileEntrytoWrite = function (fileEntry)
{
fileEntry.createWriter (self.gotFileWriter, self.fail);
}

/**
*
* Called when localStorage.dat is obtained for reading. It will create a fileReader
* which will read the contents of the file into localStorage.
*/
self.gotFileEntrytoRead = function (fileEntry)
{
fileEntry.file (self.gotFileReader, self.fail);
}

/**
*
* Called when the file localStorage.dat is successfully opened for reading.
* Parses the file by splitting it into key/value pairs, and then splitting
* those pairs into the key and the value. It then saves them to localStorage
* using localStorage.setItem().
*
* NOTE: localStorage is /not/ cleared when this file is loaded.
*/
self.gotFileReader = function (file)
{
var reader = new FileReader();
reader.onloadend = function (evt) {
if (consoleLogging) { console.log (“Syncing localStorage from persistent store.”); }
var ls = evt.target.result.split(“[EOK]”);
for (var i=0;i<ls.length;i++)
{
var kv = ls[i].split(“[EQ]”);
localStorage.setItem ( kv[0], kv[1] );
}
if (consoleLogging) { console.log (“Sync complete.”); }
if (self.readCallback)
{
self.readCallback();
}
};
reader.readAsText (file);
}

/**
*
* Called when localStorage.dat is open for writing and created if necessary.
* Writes out each value in localStorage as a key/value pair.
*/
self.gotFileWriter = function (writer)
{
if (consoleLogging) { console.log (“Syncing localStorage to persistent store.”); }

var s = “”;

for (var i=0; i<localStorage.length; i++)
{
var key = localStorage.key(i);
var value = localStorage[key];
s = s + key + “[EQ]” + value + “[EOK]”;
}
writer.write ( s );

if (consoleLogging) { console.log (“Sync Complete.”); }

if (self.writeCallback)
{
self.writeCallback();
}
}

/**
*
* If an error should occur during a read or write operation,
* we will display the error. If a readCallback() is defined,
* call it.
*/
self.fail = function (error)
{
console.log (“Error: ” + error.code);
if (self.readCallback)
{
self.readCallback();
}
}

/**
*
* Kicks off a save operation. If callback is specified,
* it is called when the save operation is complete.
*/
self.write = function ( callback )
{
if (callback)
{
self.writeCallback = callback;
}
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, self.gotFStoWrite, self.fail);
}

/**
*
* Kicks off a read operation. if callback is defined,
* it is called when the operation is complete OR a read error
* occurs.
*/
self.read = function( callback )
{
if (callback)
{
self.readCallback = callback;
}
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, self.gotFStoRead, self.fail);
}
}

/**
*
* Saves localStorage to the persistent store in localStorage.dat in the documents folder.
*
* If callback is defined, it is executed when the save operation is complete. If
* syncLocalStorageInterval is greater than zero, a timeout is created in order to
* call saveLocalStorage again, essentially creating a repeating save.
*/
function saveLocalStorage( callback )
{
var self = this;
var o = new persistentStorage();
self.callback = callback;
o.write(function()
{
if (syncLocalStorageInterval>0)
{
setTimeout (saveLocalStorage, syncLocalStorageInterval);
}
if (self.callback) { self.callback(); self.callback = null; }
});

}

/**
*
* Loads the localStorage data from the Documents/localStorage.dat file.
*
* If callback is defined, it is called after the load is complete or an
* error occurs.
*/
function loadLocalStorage( callback )
{
var o = new persistentStorage();
o.read( callback );
}

/**
*
* This one kicks everything off. It calls loadLocalStorage to load the
* localStorage data from the persistent store, and then, if syncLocalStorageInterval
* is greater than zero, sets up the next save operation.
*
* If callback is defined, it is called after the read is complete or an
* error occurs. Useful for defining when the application should start (as it should
* load the store completely before beginning.)
*/
function loadLocalStorageAndSync( callback )
{
var self = this;
self.callback = callback;

loadLocalStorage(function()
{
if (syncLocalStorageInterval>0)
{
setTimeout (saveLocalStorage, syncLocalStorageInterval);
}
if (self.callback) { self.callback(); self.callback = null; }
});
}

// to kick start it, just do this: (we give 500ms for the filesystem to become ready)
// startApp below is the name of your startup routines.
setTimeout (function()
{
loadLocalStorageAndSync ( startApp ); // start the app after we load the settings!
}, 500); // kick off a load in 500ms

</script>

<script type=”text/Javascript”>
/* This is a Jquery function that saves textarea, select, and text input boxes to localStorage when the boxes receive input from a keyboard. It does NOT save the data if I had called it to the fields through my loadData() function */
$(document).delegate(“textarea,select,input[type=’text’]”,”change”,
function() { persistData($(this));
}
);

</script>

[At this writing, PhoneGap/Cordova is at version 1.4, Xcode 4, iPhone 4s, iOS 5.]

 

Kerri just uploaded a newer version at http://pastebin.com/5881768B With this version, one changes an Xcode file; no Javascript is required. (I haven’t tested this solution with an upgrade to 5.1 yet, to see if the data carries over.)

3 thoughts on “Saving localStorage to a more secure folder location

  1. Pingback: Sobering news for developers who use local storage or SQLite | iPhone Dev Log

    • I hesitate to include an example because the later PhoneGap versions already include code that will automatically back up your localStorage content, putting it in the correct folder. So the two approaches on this page are now out of date and are no longer needed. Hooray!

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.