Storage

Several storage APIs are available for Cordova applications. See html5rocks storage overview and tutorial, for a more complete overview and examples.

Each API offers advantages and disadvantages, which are summarized here. You should choose whichever best suits your needs. You can also use several different approaches within a single application for different purposes.

LocalStorage

Local storage provides simple, synchronous key/value pair storage, and is supported by the underlying WebView implementations on all Cordova platforms.

Usage Summary

Local storage can be accessed via window.localStorage. The following code snippet shows the most important methods exposed by the returned Storage object:

var storage = window.localStorage;
var value = storage.getItem(key); // Pass a key name to get its value.
storage.setItem(key, value) // Pass a key name and its value to add or update that key.
storage.removeItem(key) // Pass a key name to remove that key from storage.

For more information, see:

Advantages

  • Supported by all Cordova platforms.
  • Its simple, synchronous API means it is easy to use.

Disadvantages

  • Only stores strings, so complex data structures have to be serialized, and only data that can be serialized can be stored.
  • Performs poorly with large amounts of data. In particular:
    • The lack of indexing means searches require manually iterating all data.
    • Storing large or complex items is slow due to the need to serialize/de-serialize.
    • Synchronous API means calls will lock up the user interface.
  • Limited total amount of storage (typically around 5MB).
  • iOS stores localStorage data in a location that may be cleaned out by the OS when space is required.

IndexedDB

The goal of the IndexedDB API is to combine the strengths of the LocalStorage and WebSQL APIs, while avoiding their weaknesses. IndexedDB lets you store arbitrary JavaScript objects (provided they are supported by the structured clone algorithm), indexed with a key. It provides some of the benefits of SQL tables, without constraining the structure or needing to define it up front.

IndexedDB provides a simple and easy to understand data model, much like LocalStorage. But unlike LocalStorage, you can create multiple databases, with multiple stores per database, and its asynchronous API and search indexes provide performance benefits.

IndexedDB is supported by the underlying WebView on all platforms, with known limitations on the following platforms:

  • browser
  • Windows

Web browser limitations

The actual behavior may depend on which browser is used. There could be differences between the behavior on the Safari and Firefox browsers, for example.

Windows Limitations

Windows platform support for IndexedDB is incomplete. For example, it lacks the following features:

  • Not available in web workers.
  • Doesn't support array keyPaths.
  • Doesn't support array keys.
  • Doesn't support object lookup via compound index.

Usage Summary

  • IndexedDB works asynchronously - you request a particular database operation, then get notified of the result via a DOM event.
  • When you make a request, you get a request object, which provides onerror and onsuccess events, as well as properties such as result, error and readyState.

The following code snippet demonstrates some simple usage of IndexedDB:

var db;
var databaseName = 'myDB';
var databaseVersion = 1;
var openRequest = window.indexedDB.open(databaseName, databaseVersion);
openRequest.onerror = function (event) {
    console.log(openRequest.errorCode);
};
openRequest.onsuccess = function (event) {
    // Database is open and initialized - we're good to proceed.
    db = openRequest.result;
    displayData();
};
openRequest.onupgradeneeded = function (event) {
    // This is either a newly created database, or a new version number
    // has been submitted to the open() call.
    var db = event.target.result;
    db.onerror = function () {
        console.log(db.errorCode);
    };

    // Create an object store and indexes. A key is a data value used to organize
    // and retrieve values in the object store. The keyPath option identifies where
    // the key is stored. If a key path is specified, the store can only contain
    // JavaScript objects, and each object stored must have a property with the
    // same name as the key path (unless the autoIncrement option is true).
    var store = db.createObjectStore('customers', { keyPath: 'customerId' });

    // Define the indexes we want to use. Objects we add to the store don't need
    // to contain these properties, but they will only appear in the specified
    // index of they do.
    //
    // syntax: store.createIndex(indexName, keyPath[, parameters]);
    //
    // All these values could have duplicates, so set unique to false
    store.createIndex('firstName', 'firstName', { unique: false });
    store.createIndex('lastName', 'lastName', { unique: false });
    store.createIndex('street', 'street', { unique: false });
    store.createIndex('city', 'city', { unique: false });
    store.createIndex('zipCode', 'zipCode', { unique: false });
    store.createIndex('country', 'country', { unique: false });

    // Once the store is created, populate it
    store.transaction.oncomplete = function (event) {
        // The transaction method takes an array of the names of object stores
        // and indexes that will be in the scope of the transaction (or a single
        // string to access a single object store). The transaction will be
        // read-only unless the optional 'readwrite' parameter is specified.
        // It returns a transaction object, which provides an objectStore method
        // to access one of the object stores that are in the scope of this
        //transaction.
        var customerStore = db.transaction('customers', 'readwrite').objectStore('customers');
        customers.forEach(function (customer) {
            customerStore.add(customer);
        });
    };
};

function displayData() {
}

For more information, see:

Advantages

  • Good performance - asynchronous API won't block the UI, and indexing provides good search performance.
  • Simple data model easier to learn than SQL.
  • More flexible structure than WebSQL.
  • Multiple databases and object stores provides more structure than LocalStorage.
  • Robustness from using a transactional database model.
  • Support for versioning.

Disadvantages

  • Complex API with nested callbacks.
  • Limited total amount of storage and possible eviction as described on MDN.

Plugin-Based Options

FileSystem API

The FileSystem API was a W3C spec that was implemented by Chrome, but not other browsers. It provides APIs to store and retrieve data on the local file system, and is described in some detail in an excellent html5rocks article. While the API is not supported natively by any Cordova platform, the File plugin provides an extensive implementation that is available across all Cordova platforms.

SQLite Plugin

The SQLite plugin provides an API virtually identical to WebSQL described above. The main differences are:

  • It is available with support for the Windows platform.
  • It effectively has no size limitations.

It is available in the following variations:

  • cordova-sqlite-storage - core version that includes its own sqlite3 implementation. It supports iOS, Android & Windows platforms.
  • cordova-sqlite-ext - extended version with additional features including REGEXP support on Android and iOS.
  • cordova-sqlite-evfree - similar to cordova-sqlite-ext but with improved memory handling. Available under GPL v3 or commercial license.

Other Plugins

Search Cordova plugins for other plugins that provide alternative storage options.