Local data storage for mobile apps

Preview:

Citation preview

Gran Sasso Science Institute

Ivano Malavolta

Local Storage

Roadmap

Introduction

Web Storage

WebSQL

IndexedDB

File System Access

Final Considerations

Local storage and file system access

There are 4 ways to store data locally in Cordova:

•  Web storage

•  Local Storage

•  Session Storage

•  WebSQL

•  Indexed DB

•  File System Access

Web storage, WebSQL, and IndexedDB conform to W3C specifications and are provided by the browser itself

File system access API conforms to its corresponding W3C specification

Web Storage

LocalStorage

stores data in key/value pairs

persists across browser sessions

SessionStorage

stores data in key/value pairs

data is erased when a browser session ends

WebSQL

relational DB

support for tables creation, insert, update, …

transactional

persists across browser sessions

WebSQL

It provides you a structured SQL relational database

You have to setup a DB schema

You can then perform classical SQL queries

tx.executeSql(‘SELECT  *  FROM  User’,  [],        function(tx,  result)  {          //  callback  code  });  

IndexedDB

•  It combines Web Storage and WebSQL

•  You can save data as key/value pairs

•  You can define multiple DBs

•  Good Performance

–  data is indexed

–  Asynchronous à it does not block the UI

You can see a store as a big SQL table with only key/value pairs

à you don’t need to define a schema upfront

File System

•  you can access files locally to your app

•  supports main FS operation–  creation, move, delete, rename, etc.

•  it is not transactional

•  persists across browser sessions

Roadmap

Introduction

Web Storage

WebSQL

IndexedDB

File System Access

Final Considerations

Web Storage

It is based on a single persistent object called localStorage

You can set values by callingwindow.localStorage.setItem(“name”, “Ivano”);

You can get values back by calling var name = window.localStorage.getItem(“name”);

Supported Methods

.key(0) Returns the name of the key at the position specified

.getItem(“key”) Returns the item identified by it's key

.setItem(“key”, “value”) Saves and item at the key provided

.removeItem(“key”) Removes the item identified by it's key

.clear() Removes all the key-value pairs

Complex Objects

Current implementations support only string-to-string mappings

à  you can store only strings

à  keys can be only strings

You can use JSON serialization if you need to store complex data structures

Example of JSON Serialization

// simple class declaration function Person(name, surname) { this.name = name; this.surname = surname;

} // object creation var user = new Person(‘Ivano’, ‘Malavolta’); // object serialization window.localStorage.setItem(“user”, JSON.stringify(user)); // object retrieval var current =

JSON.parse(window.localStorage.getItem(“user”));

Checking Existence

You can simply check if the needed element is == null

if (window.localStorage.getItem(“user”)) {

// there is an object in user

} else { // the user key does not have any value

}

Selecting elements

In this case you have to manually iterate on elements

var users = [...]; // array of Person objects window.localStorage.setItem(“users”,

JSON.stringify(users)); var allUsers =

JSON.parse(window.localStorage.getItem(“users”)); var ivanos = []; for(var i=0; i<allUsers.length; i++) { if(allUsers[i].name == ‘Ivano’) ivanos.push(allUsers[i]);

}

Counting Elements

Also in this case, we have to do it manually

var usersNumber =

JSON.parse(window.localStorage.getItem(“users“)).length;

Session Storage

Session Storage provides the same interface as Local Storage

à you can call the same methods

but

Session Storage is cleared between app launches

Roadmap

Introduction

Web Storage

WebSQL

IndexedDB

File System Access

Final Considerations

WebSQL

It provides you a structured SQL relational database

You have to setup a DB schema

You can then perform classical SQL queries

tx.executeSql("SELECT * FROM User“, [],

function(tx, result) { // callback code });

Opening a DB

Done via a dedicated function

var db = openDatabase(‘Test', ‘1.0', ‘Test DB', 100000);

It creates a new SQLite DB and returns a new Database object

The Database object will be used to manipulate the data

Opening a DB: syntax

openDatabase(name, version, displayname, size);

namethe name of the DB

versionthe version of the DB

displaynamethe display name of the DB

sizethe size of the DB in bytes

Database

It allows to manipulate the data via 2 methods:

changeVersion

atomically verify the version number and change it

db.changeVersion("1.0", "1.1");

transaction

performs a DB transaction

Transactions

It allow you to execute SQL statements against the DB

db.transaction(queries, error, success);

3 functions as parameters:

queries : contains the queries to be performed

error : executed if the transaction results in an error

success : executed if the transaction terminates correctly

Transaction Example

http://bit.ly/JlUJde

executeSql

It is the method that performs a SQL statement

The user can build up a database transaction by calling the executeSql method multiple times

function populateDB(tx) { tx.executeSql('DROP TABLE IF EXISTS USER'); tx.executeSql('CREATE TABLE IF NOT EXISTS USER (id

unique, name, surname)');

tx.executeSql('INSERT INTO USER(id, name, surname) VALUES (1, ?, ?)‘, [“Ivano“, “Malavolta“], success, error);

}

Result Sets

When the executeSql method is called, it will invoke it's callback with a SQLResultSet parameter

It has 3 properties:

insertId

the ID of the row that has been inserted

rowsAffected

the number of rows changed by the SQL statement

rows

the data returned from a SQL select statement

rows is an object of type SQLResultSetList 

Results Sets Example

... tx.executeSql('INSERT INTO USER(id, name,surname) VALUES (5, ?, ?)‘, [“Ivano“, “Malavolta“], success, error);

}

function success(tx, results) {

var affected = results.rowsAffected(); // 1

}

function error(err) {

// code for managing the error

}

Result Set Lists

It contains the data returned from a SQL Select statement

length the number of rows returned by the SQL query

 

item(index) returns the row at the specified index represented by a JavaScript object

Result Set List Example

... tx.executeSql(‘SELECT * FROM USER‘, [], success, error);

}

function success(tx, results) {

var size = results.rows.length; for (var i=0; i<size; i++){

console.log(results.rows.item(i).name); }

}

Errors

It contains information about an occurred error

code

A predefined error code

es. UNKNOWN_ERR,

DATABASE_ERR,

TOO_LARGE_ERR,

QUOTA_ERR,

TIMEOUT_ERR,

SYNTAX_ERR

messageA description of the error

error not considered by any other error codes

internal error of the database

the result set is too large

the db now exceeds the storage space of the app

•  the statement is not sintactically correct•  the number of parameters does not match with

placeholders

no reasonable time to get the lock for the transition

Error Code Example

...

tx.executeSql(‘SELECT * FROM USER‘,[], success, error); }

function error(err) { console.log(err.code);

}

Roadmap

Introduction

Web Storage

WebSQL

IndexedDB

File System Access

Final Considerations

Indexed DB

It tries to combine Web Storage and WebSQL

You can save data as key/value pairs

You can define multiple DBs

Good Performance

data is indexed

asynchronous à it does not block the UI

Indexed DB

An Indexed DB is a collection of object stores

You can drop objects into the stores

You can see a store as a big SQL table with only key/value pairs

à you don’t need to define a schema upfront

IndexedDB !== mobile storage

still not fully supported by iOS

Roadmap

Introduction

Web Storage

WebSQL

IndexedDB

File System Access

Final Considerations

File System Access

It allows you to read, write and navigate file system hierarchies

It is fundamental for managing and storing large files and binary content on the client-side

File System Access Workflow

1.  request file system access–  persistent or temporary file system

2.  then you can perform CRUD operations for both files and folders:–  Create

–  Read

–  Update

–  Delete

Request File System

requestFileSystem(type, size, successCb, [errorCb])

type

LocalFileSystem.TEMPORARYLocalFileSystem .PERSISTENT

sizesize in bytes the app will require for storage

successCbsuccess callback, its argument is a FileSystem object

ErrorCberror callback, its argument is a FileError object

Temporary VS Persistent

Temporary

the files stored by the app can be deleted at the browser’s discretion 

à no guarantee of persistence

Persistent

cannot be deleted by the browser without authorization by the app

Local File System Example

window.requestFileSystem( LocalFileSystem.PERSISTENT,

0, onSuccess,

onError);

function onSuccess(fileSystem) {

console.log(fileSystem.name);

}

leave it to zero, Apache Cordova will take care of it

File System

The FileSystem object has 2 properties:

name

the name of the file system

it is unique across the list of exposed file systems

root

the DirectoryEntry object representing the root folder of the file system

Resolving a File URI

window.resolveLocalFileSystemURI

retrieve a DirectoryEntry or FileEntry using a URI

window.resolveLocalFileSystemURI(

"file:///userImg.png", onSuccess, onError);

function onSuccess(fileEntry) { console.log(fileEntry.name);

}

Important directories

Entities

FileEntry

DirectoryEntry

File

FileReader

FileWriter

DirectoryReader

The real objects

Descriptor

Writing & Reading objects

File Entry

It represents a file on a file system

isFile (boolean)

Always true

isDirectory (boolean)

Always false

name (DOMString)

the name of the FileEntry, excluding the path 

fullPath (DOMString)

the full absolute path from the root

File Entry Methods

getMetadata(success, fail) Look up metadata about a file

setMetadata(success, fail, metadataObject) Sets the metadata of the file

moveTo(parentEntry, newName, success, fail)Move a file to a different location on the file system

copyTo(parentEntry, newName, success, fail)Copy a file to a different location on the file system

toURL()Return a URL that can be used to locate a file

File Entry Methods

remove(success, fail)

Delete a file

getParent(success, fail)

Look up the parent directory

createWriter(success, fail)

Creates a FileWriter object that can be used to write to a file

file(success, fail)

Creates a File object containing file properties

File

It contains attributes of a single file

name (DOMString)

The name of the file

fullPath (DOMString)

The full path of the file including the file name

type (DOMString)

The mime type of the file 

lastModifiedDate (Date)

The last time the file was modified

size (long)  

The size of the file in bytes

Directory Entry

It represents a directory on a file system

It has the same properties of FileEntry

Directory Entry Methods

getMetadata(success, fail) Look up metadata about a directory

setMetadata(success, fail, metadataObject) Sets the metadata of the directory

moveTo(parentEntry, newName, success, fail)Move a directory to a different location on the file system

copyTo(parentEntry, newName, success, fail)Copy a directory to a different location on the file system

toURL()Return a URL that can be used to locate a directory

Directory Entry Methods

getParent(success, fail)Look up the parent directory

createReader()Creates a DirectoryReader object that can be used to read a directory

getDirectory(path, options, success, fail)Creates or looks up a directoryoptions: create: (true | false)exclusive: (true | false)

Directory Entry Methods

getFile(path, options, success, fail)

Create or look up a file within the directory

options are used when the file does not exist:

create à (true | false)

exclusive à (true | false)

removeRecursively(success, fail)

Delete a directory and all of its contents

File Reader

It is used to read the contents of a file

Files can be read as:

•  text

•  base64 data encoded string

•  binary string

•  array buffer

You can also abort() a file reading activity

You can register your own event listeners to receive the following events:loadstart, progress, load, loadend, error, abort

File Reader Example

entry.file(win, fail);

function win(file) {

var reader = new FileReader(); reader.onloadend = function(evt) {

console.log(evt.target.result); }; reader.readAsText(file);

// reader.abort(); };

function fail(evt) { console.log(error.code);

};

File Writer

It is used to write to a file

The data to be written must be UTF-8 encoded

You can register your own event listeners to receive the following events:

writestart, progress, write, writeend, error, abort

File Writer

A FileWriter is created for a single file

You can use it to write to a file multiple times

à the FileWriter maintains the file's position and length attributes, so you can seek and write anywhere in the file

By default, the FileWriter writes to the beginning of the file (will overwrite existing data)

Set the optional append boolean to true in the FileWriter's constructor to begin writing to the end of the file

File Writer Methods

abort()

Aborts writing file

seek(byte)

Moves the file pointer to the byte specified.

truncate(length)

Shortens the file to the length specified.

write(data)

Writes data to the file

File Writer Example

entry.createWriter(win, fail);

function win(writer) {

writer.onwrite = function(evt) { console.log(“ok");

}; writer.write(“Ivano Malavolta");

};

function fail(evt) { // error management

};

Directory Reader

It is an object that lists files and directories in a directory

It has only one method:

readEntries(success, fail)

Read the entries of the directory

Directory Reader Example

var directoryReader = dirEntry.createReader(); directoryReader.readEntries(success, fail);

function success(entries) {

var i; for (i=0; i<entries.length; i++) { console.log(entries[i].name); }

}

function fail(error) {

console.log(error.code);

}

A Final Example

window.requestFileSystem(window.PERSISTENT, 0, initFS, error); function initFS(fs) { fs.root.getFile(‘log.txt', {}, win, error); } function win(fileEntry) { fileEntry.file(read, error); } function read(file) { var reader = new FileReader();

reader.onloadend = function(e) { console.log(this.result); }; reader.readAsText(file);

} function error(err) { console.log(err);}

Looking for a file and reading it

File upload

Upload files to a remote server via an HTTP multi-part POST request

var fileURI; // the path of the file on the device

var server; // encoded URL of the server var win; // success callback

var fail; // error callback

var options; // optional parameters (see next slide)

var trustAllHosts; // optional boolean parameter, // true to accept all security certificates

var ft = new FileTransfer();

ft.upload(fileURI, encodeURI(server), win, fail, options);

File upload options

The FileUploadOptions can be used to specify additional parameters to the upload script

var options = new FileUploadOptions();

options.fileKey="file”;

options.fileName= “fileName”);

options.mimeType="text/plain";

File upload options

File upload result

A FileUploadResult object is passed to the success callback

Properties:

bytesSent: the number of bytes sent to the server

responseCode: The HTTP response code returned by the server

response: The HTTP response returned by the server as string

headers: the headers of the HTTP response by the server

not supported in iOS

not supported in iOS

File download

Downloads files from a remote server via an HTTP GET request

var source; // URL of the file to be downloaded

var target; // full path of the file to be saved var win; // success callback (takes FileEntry object) var fail; // error callback

var options; // optional parameters (only headers)

var trustAllHosts; // optional boolean parameter, // true to accept all security certificates

var ft = new FileTransfer();

ft.download(encodeURI(source),target, win, fail, options);

File transfer abort

Used to stop an on-going file transfer

var ft = new FileTransfer(); ft.upload(fileURI, encodeURI(server), win, fail, options); // perform some operation ft.abort():

File transfer progress

Special callback activated every time a new piece of data is

transferred

ft.onprogress = function(progress) { if (progressEvent.lengthComputable) { console.log((progress.loaded / progress.total) + “%”); } else { console.log(“progressed”); } }; ft.download(...); // the same works for upload

File transfer error

The FileTransferError object stores information about an error

occurred during a file transfer (either upload or download)

Properties:

code: predefined error code

source: URI of the source

target: URI of the target

http_status: HTTP status code from the server (if received)

FileTransferError.FILE_NOT_FOUND_ERRFileTransferError.INVALID_URL_ERRFileTransferError.CONNECTION_ERRFileTransferError.ABORT_ERR

Roadmap

Introduction

Web Storage

WebSQL

IndexedDB

File System Access

Final Considerations

Considerations

You will likely use more than one API in combination

à Use the right API for the right job

Web Storage

•  it is not transactional à race conditions

•  very simple API, no schema

•  only String data à performance issues for complex data due to JSON serialization

•  session storage will be cleared after the app is closed

•  limited quota

Considerations

WebSQL

SQL-based à fast and efficient

transactional à more robust

asynchronous à does not block the UI

rigid data structure à data integrity vs agility

limited quota

Considerations

IndexedDB

simple data model à easy to use

transactional à more robust

asynchronous à does not block the UI

good search performance à indexed data

data is unstructured à integrity problems

limited quota

not fully supported by every platform (e.g., iOS)

Considerations

File System

asynchronous à does not block the UI

not transactional

indexing and search are not built-in à you have to implement your lookup functions

unlimited quota à useful for images, videos, documents, etc.

Platforms support

About quotas...

Local Storage

~ 10Mb

Session Storage

~ 10Mb

WebSQL

~ 50-80Mb (depends on the device)

Indexed DB

~ 50-80Mb (depends on the device)

File system

unlimited

Native DB

unlimited

Exercises

Extend the previous exercises you developed about Frascati events so that users can:

1.  save a specific event or “ente” as favorited via local storage

2.  define a dedicated “Favorites” view of the app;

3.  define a WebSQL DB for storing events and “enti”;–  here you can support a very limited subset of the data

4.  define a data prefetcher that at the first launch of the app saves all the data coming from the Rest API to the WEBSQL database;–  in the subsequent launches of the app, the data must come from

the WebSQL database, and not from the Rest API

Data:http://www.ivanomalavolta.com/files/data/frascatiEventi.jsonhttp://www.ivanomalavolta.com/files/data/frascatiEnti.json

References

http://cordova.apache.org/docs/en/edge

ContactIvano Malavolta |

Gran Sasso Science Institute

iivanoo

ivano.malavolta@gssi.infn.it

www.ivanomalavolta.com