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:
Method | Android stock browser 4.3 | Chrome 40 | IOS WebView 6 and 7 | IOS WebView 8 |
---|---|---|---|---|
Local Storage | 2 MB | 10 MB | 5 MB | 5 MB |
Web SQL | 200 MB | up to quota | 50 MB | 50 MB |
Indexed DB | N/A | up to quota | N/A | up 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: