PHP, Web and Mobile development

How to store offline data with localForage in your Ionic or Angular mobile App

Most people uses localStorage to store persistent data locally when developing a Ionic or Angular mobile App.
However this method has some drawbacks: its slow; it can’t handle binary data and its total size is limited to only few megabytes.

Along localStorage there are other two methods available to save data locally: IndexedDB and WebSQL. But the availability and the API of those methods vary on every platform.

Lets summarize the limit of the various offline mobile storage methods (according to this post ) for the most common mobile’s platforms:

MethodAndroid stock browser 4.3Chrome 40IOS WebView 6 and 7IOS WebView 8
Local Storage2 MB10 MB 5 MB5 MB
Web SQL200 MBup to quota 50 MB50 MB
Indexed DBN/Aup to quota N/Aup to quota

So, remember that if you want to support old android 4.x your limit is 200MB.

localForage to the rescue

To escape this babel tower like situation its possible to use a plain javascript library called localForage, follow the previous link to their github’s project and download the file dist/localforage.min.js into your project.

localForage provides a common asynchronous API with callbacks and E6 promises to the above methods. Its automatically select the best local storage method to use depending on the running platform.

localForage also automatically serialize and unserialize your data to JSON: this way you can store objects and other types of data.

localForage has an excellent documentation that cover almost everything: i will not stay here describing all of it; however i wish to give you a few usefull tips.

initializing localForage

Initializing localForage isn’t necessary but if you wish to debug wich storage method localForage has automatically selected then you can use this AngularJS code:

localforage.ready(function() {
    $log.info('localforage ready', localforage.driver() );
});

If you’re not satisfied and you wish to choose a different driver then you can use the config method:

localforage.config({
    name: 'MyLocalForageDbName',
    driver: [localforage.INDEXEDDB, localforage.WEBSQL, localforage.LOCALSTORAGE],
    size: 200*1024*1024, // 200MB - used only by WebSql
 });

I suggest you to use the config method at least to set a custom name and to choose a custom quota for the Web SQL storage in bytes.

Retrieving and setting data

While setting data is pretty straightforward using the setItem method, instead, the retrieving data has a few caveats.

First of all it has two syntax: one using a callback, the other using an E6 promise. See below:

localforage.getItem('somekey', function(err, value) {
    console.log(value);
});
localforage.getItem('somekey').then(function(value) {
    console.log(value);
});

The problem here is that localForage never returned any error in all my apps. When a key does not exists i expected it to return an error: instead it will return a NULL value.

Also if you use setItem to set a key’s value to “undefined” then it will be converted to NULL. Knowing that then be carefull when you plan your data’s structure and how you use it.

Said that, i see  no point in bloating the code with .catch functions everywhere. I suggest you to use only the callback syntax checking for both error and null value with a single “if“.

Updating Ionic or Angular templates

Note that if you change some scope’s variable used in a template inside of any localForage’s callback this will not trigger any visibile update.

So, remember to use $scope.$apply(); for that.

Known limitations

There is no way of knowing the total bytes usage of the database. You can try iterating through all the objects and do an estimate calculation but this will be time-consuming. A faster but complex solution will be to abstract any setItem and removeItem method and to update a custom counter with the estimated size that will be stored along with the data.

Also remember that iterating over multiple items can be time-consuming. It will be faster to get/set a single big object.

Using it in Angular2

Of course it works with Angular2 since its a simple javascript library. So if your typings are ok then in your code it would be enough to use:

const localForage = require("localforage");

to make your editor happy (remember to include the JS in index.html).

More on offline storage

If you want to know more about offline storage may i suggest you this book “Client Side Data Storage“. Maybe its old but its usefull: