React Native LiteSync
LiteSync plugin for React Native (Android and iOS)
This plugin works as a wrapper over the LiteSync native library. It can be used with both the free and the full versions
This is a fork of react-native-sqlite-storage
Main differences:
- Links to the LiteSync native library
- Query parameters are not converted to strings
Features:
- iOS and Android supported via identical JavaScript API
- SQL transactions
- JavaScript interface via callbacks or Promises
- Pre-populated SQLite database import from application bundle and sandbox (for dbs that do not use LiteSync)
Installation
# using yarn
yarn add react-native-litesync react-native-udp
# using npm
npm install react-native-litesync react-native-udp
Then run:
cd ios && pod install && cd ..
For React Native 0.59 and below (manual linking) please follow these instructions
Native Libraries
To install the free version of LiteSync native libraries, execute the following:
wget http://litesync.io/download/litesync.aar
wget http://litesync.io/download/litesync-free-ios-native-libs.tar.gz
tar zxvf litesync-free-ios-native-libs.tar.gz lib
mv lib node_modules/react-native-litesync/platforms/ios/
mv litesync.aar node_modules/react-native-litesync/platforms/android/
When moving to the full version just copy the libraries to the respective folders as done above, replacing the existing files.
How to Use
Here is an example code:
var SQLite = require('react-native-litesync')
on_error = (err) => {
console.log("Error:", err);
}
on_success = () => {
console.log("SQL executed");
}
on_db_open = () => {
console.log("The database was opened");
}
// open the database
var uri = "file:test.db?node=secondary&connect=tcp://server:port";
var db = SQLite.openDatabase({name: uri}, on_db_open, on_error);
db.on('error', on_error); // this should be the first callback
db.on('not_ready', () => {
// the user is not logged in. show the login screen (and do not access the database)
...
});
db.on('ready', () => {
// the user is already logged in or the login was successful. show the main screen
...
});
db.on('sync', () => {
// the db received an update. update the screen with new data
show_items();
});
insert_items = () => {
db.transaction((tx) => {
// CREATE TABLE IF NOT EXISTS tasks (id INTEGER PRIMARY KEY, name, done, row_owner)
tx.executeSql("INSERT INTO tasks (name,done) VALUES ('Learn React Native',1)", []);
tx.executeSql("INSERT INTO tasks (name,done) VALUES ('Use SQLite',1)", []);
tx.executeSql("INSERT INTO tasks (name,done) VALUES ('Test LiteSync',0)", []);
}, () => {
// success callback = transaction committed
show_items();
}, on_error);
}
show_items = () => {
db.executeSql('SELECT * FROM tasks', [], (result) => {
// Get rows with Web SQL Database spec compliance
var len = result.rows.length;
for (let i = 0; i < len; i++) {
let task = result.rows.item(i);
console.log(`Task: ${task.name}, done: ${task.done}`);
}
// Alternatively, you can use the non-standard raw method
/*
let tasks = result.rows.raw(); // shallow copy of the rows Array
tasks.forEach(row => console.log(`Task: ${task.name}, done: ${task.done}`));
*/
});
}
Working Examples
These example apps can be used with the AwesomeProject generated by React Native.
All you have to do is to copy one of those files into your AwesomeProject replacing the index.js
Opening a Database
SQLite.openDatabase({name: uri}, successcb, errorcb);
Database Status
The app should subscribe to database events to act according to its state
If the database is not ready, the not_ready
event will be fired. This generally means that it is
the first time the app is being open and/or the user has not signed up or logged in yet:
db.on('not_ready', () => {
// the user is not logged in. show the login screen (and do not access the database)
...
});
If the user has already signed up or logged in, then the database will fire the ready
event:
db.on('ready', () => {
// the user is already logged in or the login was successful. show the main screen
...
});
It is also possible to subscrive to sync
events, so the app can read fresh data from the
database and update the screen:
db.on('sync', () => {
// the db received an update. update the screen with new data
...
});
To check the full status of the database we can use:
db.executeSql("pragma sync_status", [], (result) => {
if (result.rows && result.rows.length > 0) {
var status = result.rows.item(0);
console.log('sync status:', status.sync_status);
} else {
console.log('LiteSync is not active')
}
}, (msg) => {
console.log('could not run "pragma sync_status":', msg)
});
User Sign Up & User Login
LiteSync replicates the same database to all the users.
You need to implement the authorization by yourself, if required.
For an easier approach that supports user login, check OctoDB
Importing a pre-populated database
This is NOT supported if the database uses LiteSync, because the database will be downloaded from the primary node(s) at the first run.
But as this library also supports normal SQLite databases, you can import an existing pre-populated database file into your application when opening a normal SQLite database.
On this case follow the instructions at the original repo
Attaching another database
SQLite3 offers the capability to attach another database to an existing database instance, i.e. for making cross database JOINs available.
This feature allows to SELECT and JOIN tables over multiple databases with only one statement and only one database connection.
To archieve this, you need to open both databases and to call the attach()
method of the destination (or master) database to the other ones.
let dbMaster, dbSecond;
dbSecond = SQLite.openDatabase({name: 'second'},
(db) => {
dbMaster = SQLite.openDatabase({name: 'master'},
(db) => {
dbMaster.attach( "second", "second", () => console.log("Database attached successfully"), () => console.log("ERROR"))
},
(err) => console.log("Error on opening database 'master'", err)
);
},
(err) => console.log("Error on opening database 'second'", err)
);
The first argument of attach()
is the name of the database, which is used in SQLite.openDatabase()
. The second argument is the alias, that is used to query on tables of the attached database.
The following statement would select data from the master database and include the "second" database within a simple SELECT/JOIN statement:
SELECT * FROM user INNER JOIN second.subscriptions s ON s.user_id = user.id
To detach a database use the detach()
method:
dbMaster.detach( 'second', successCallback, errorCallback );
There is also Promise support for attach()
and detach()
as shown in the example application under the
test folder
Promises
To enable promises, run:
SQLite.enablePromise(true);
Known Issues
- React Native does not distinguish between integers and doubles. Only a Numeric type is available on the interface point. You can check the original issue
The current solution is to cast the bound value in the SQL statement as shown here:
INSERT INTO products (name,qty,price) VALUES (?, cast(? as integer), cast(? as real))