-
Notifications
You must be signed in to change notification settings - Fork 0
Attempt to add jeep-sqlite for Capacitor SQLite support #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@khromov Does this work? Trying hard myself to get sqlite working native and on web. Just opened an issue for better docs. |
|
Yes, I've gotten it to work on web more or less but I haven't updated this PR. You have to lock jeep-sqlite at v2.5.3 and @capacitor-community/sqlite at v5.4.1. I recently tried newer versions and they don't work, but haven't figured out what broke. If you can figure it out on the latest version you are welcome to contribute. As far as I can tell everything works normally on iOS/Android, it's just web being a hassle. Keep in mind it's not a complete example, you'll have to tweak it yourself. Create the db.ts class below. Call it with something like: const result = await query(`SELECT * FROM options WHERE name = ? LIMIT 1`, [optionName]);
const option = result?.values?.[0]?.value ?? null;import { PUBLIC_DB_DEBUG } from '$env/static/public';
import { CapacitorSQLite, SQLiteConnection } from '@capacitor-community/sqlite';
import type {
DBSQLiteValues,
SQLiteDBConnection,
capSQLiteChanges
} from '@capacitor-community/sqlite';
import { isNative, isWeb } from './capacitor';
//import localForage from 'localforage';
let sqlite: SQLiteConnection | undefined = undefined;
let db: SQLiteDBConnection | undefined = undefined;
// https://ionicacademy.com/sqlite-ionic-with-capacitor/
// https://github.com/capacitor-community/sqlite/blob/master/docs/Web-Usage.md
let firstCall = true;
const DB_NAME = 'mydb';
/**
* Database migrations
*/
const version1 = [
// options
`CREATE TABLE "options" (
"name" TEXT NOT NULL UNIQUE,
"value" TEXT,
PRIMARY KEY("name")
)`,
`CREATE INDEX "options_name_idx" ON "options" (
"name"
)`,
];
export async function maybeInitialize(): Promise<any> {
if (!sqlite) {
sqlite = new SQLiteConnection(CapacitorSQLite);
}
if (isWeb() && firstCall) {
const i = await import('jeep-sqlite/dist/components/jeep-sqlite');
customElements.define('jeep-sqlite', i.JeepSqlite);
console.log('🌐 Initializing Web Jeep SQLite - first call');
firstCall = false;
const jeepEl = document.createElement('jeep-sqlite');
document.body.appendChild(jeepEl);
const res = await customElements.whenDefined('jeep-sqlite');
await sqlite.initWebStore();
console.log('✅ Web Jeep SQLite loaded');
}
try {
await sqlite.checkConnectionsConsistency();
} catch (e) {
console.log('❌ Could not check connections consistency', e);
}
if (db && (await db?.isDBOpen())) {
// console.log('⚠️ SQLite already initialized');
return;
}
// Try to retreive existing connection
try {
console.log('⚡ Trying to retrieve connection');
db = await sqlite?.retrieveConnection(DB_NAME, false);
console.log('⚡ Retrieved connection', db);
} catch (e) {
console.log('Could not retrieve connection', e);
}
// If no connection exists, create a new one
if (!db) {
try {
console.log('🚀 Initializing SQLite');
await sqlite.addUpgradeStatement(DB_NAME, 1, version1);
// await sqlite.addUpgradeStatement(DB_NAME, 2, version2);
db = await sqlite?.createConnection(DB_NAME, false, 'no-encryption', 1, false);
try {
const openResult = await db?.open();
if (isWeb()) {
await sqlite.saveToStore(DB_NAME);
}
console.log('🚀 SQLite initialized');
// await db.close();
} catch (e) {
console.log('Could not connect to db', e);
}
} catch (e) {
console.log('Could not open db', e);
}
}
}
export async function close(): Promise<any> {
await persistWeb();
await db?.close();
}
export const persistWeb = async () => {
if (isWeb()) {
if (PUBLIC_DB_DEBUG === 'true') {
console.log('💾 Persisting web DB');
}
await sqlite?.saveToStore(DB_NAME);
}
};
export async function run(query: string, params: any[] = []): Promise<capSQLiteChanges> {
return queryInternal(query, params, 'run');
}
export async function query(query: string, params: any[] = []): Promise<DBSQLiteValues> {
return queryInternal(query, params, 'query');
}
export async function queryInternal(
incomingQuery: string,
params: any[] = [],
mode: 'run' | 'query' = 'query'
): Promise<any | null> {
const timingStart = new Date();
await maybeInitialize();
if (PUBLIC_DB_DEBUG === 'true') {
console.info('----');
console.info(`🔰 Query: ${incomingQuery}`);
console.info('📊 Data: ', params);
}
if (db?.isDBOpen() && sqlite) {
let results;
if (mode === 'run') {
results = await db?.run(incomingQuery, params);
} else {
results = await db?.query(incomingQuery, params);
}
// TODO: Probably not needed for selects for example
const isSelectQuery = incomingQuery.toLowerCase().startsWith('select');
if (!isSelectQuery) {
//if (PUBLIC_DB_DEBUG === 'true') {
// console.log('📢 Non-select query ran, persisting web DB');
//}
await persistWeb();
}
if (PUBLIC_DB_DEBUG === 'true') {
console.info('⏰ Query execution time: %dms', new Date().getTime() - timingStart.getTime());
console.info('----');
}
return results;
} else {
return null;
}
}
export async function deleteDb(): Promise<void> {
await maybeInitialize();
await db?.close();
await CapacitorSQLite.deleteDatabase({
database: DB_NAME
});
await maybeInitialize();
}
export async function exportDb(): Promise<void> {
if (isNative()) {
return;
}
const localForage = await import('localforage');
//await db?.flush();
await persistWeb();
const newDbInstance = localForage.default.createInstance({
name: 'jeepSqliteStore',
storeName: 'databases'
});
const data = await newDbInstance.getItem<Uint8Array>(`${DB_NAME}SQLite.db`);
console.log(newDbInstance);
if (!data) return;
const blob = new Blob([data], { type: 'application/vnd.sqlite3' });
const a = document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = `${DB_NAME}.sqlite`;
a.click();
}
export async function importDb(dbFile: File): Promise<void> {
if (isNative()) {
console.log('🚫 Importing a DB is only supported on Web.');
return;
}
const localForage = await import('localforage');
const reader = new FileReader();
reader.onload = async (event) => {
if (!event.target?.result) {
console.log('❌ Could not read the file.');
return;
}
// Convert result into Uint8Array format
const data = new Uint8Array(event.target.result as ArrayBuffer);
// Store the data in localForage
const newDbInstance = localForage.default.createInstance({
name: 'jeepSqliteStore',
storeName: 'databases'
});
// Explicitly remove db
await newDbInstance.removeItem(`${DB_NAME}SQLite.db`);
await newDbInstance.setItem(`${DB_NAME}SQLite.db`, data);
// Reinitialize your database connection to reflect the imported data
await maybeInitialize();
};
reader.onerror = () => {
console.log('❌ Error reading the file.');
};
reader.readAsArrayBuffer(dbFile);
} |
Example Vue+Vite integration:
https://github.com/jepiqueau/vite-vue-sqlite-app/blob/544058bd39d7cd7b64eda8ae0d5de4bcf55364f5/src/main.ts#L6