Zum Hauptinhalt springen

Benutzung von benutzerdefinierten SQLite-Erweiterungen in Capacitor

Benutzerdefinierte SQLite-Erweiterungen in einer Capacitor-App laden: iOS- und Android-Beschränkungen, eine C-Erweiterung, die für beide Plattformen kompiliert wird, und wie Sie sie pro Verbindung registrieren.

Artikelcredits

Martin Donadieu

Schreiber

Valeria

Rezensent

Jordan

Redakteur

Benutzerdefinierte SQLite-Erweiterungen in einer Capacitor-App verwenden

Sie können benutzerdefinierte SQLite-Erweiterungen in einer Capacitor-App verwenden, aber nur als native code-Dateien, die in die App kompiliert wurden: auf iOS verlinken Sie die Erweiterung statisch und rufen die init-Funktion auf jeder Verbindung auf, und auf Android bündeln Sie eine SQLite-Build, die Erweiterungen laden kann, und liefern die Erweiterung als native Bibliothek pro CPU-Architektur. Diese Anleitung erklärt, warum sich die Plattformen unterscheiden, gibt Ihnen eine funktionierende Erweiterung in C und zeigt die genauen native Änderungen für @capgo/capacitor-fast-sql auf Capacitor 8.

Bevor Sie C schreiben, überprüfen Sie, ob Sie es wirklich benötigen. Viele „Erweiterungen“ sind bereits in der App integriert.

Was ist eine SQLite-Erweiterung?

Eine Erweiterung ist eine native code-Komponente, die neue Dinge mit einer SQLite-Verbindung registriert: Skalarfunktionen (haversine_km(...)Aggregatefunktionen, Collationen, virtuelle Tabellen und FTS5-Tokenisierer. Jede Erweiterung bietet einen Einstiegspunkt:

int sqlite3_<name>_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi);

SQLite ruft sie einmal pro Verbindung auf. Alles, was Sie registrieren, existiert nur auf dieser Verbindung.

Häufige Gründe, eine in einer mobilen App zu schreiben:

  • ein benutzerdefinierter FTS5-Tokenizer für eine Sprache die im integrierten unicode61 tokenizer verarbeitet schlecht.
  • a math or geo function you need in WHERE and ORDER BY (Distanzsortierung, Bewertung)
  • eine Kollation, die Namen so sortiert, wie Ihre Benutzer es erwarten.
  • Ein Vektorsuch-Extension für die lokale Abfrage.

Überprüfen Sie, was bereits implementiert ist

Führen Sie diese durch Ihren Plugin auf den ältesten iOS- und Android-Versionen durch, die Sie unterstützen:

import { FastSQL } from '@capgo/capacitor-fast-sql';

const db = await FastSQL.connect({ database: 'probe' });

const [{ v }] = await db.query('SELECT sqlite_version() AS v');
const options = await db.query('PRAGMA compile_options');
const functions = await db.query('SELECT name FROM pragma_function_list ORDER BY name');

console.log(v, options.map((r) => r.compile_options));

Dinge, für die Sie oft keine Erweiterung benötigen:

Brauchen Eingebaute Funktion Überprüfen
JSON in Spalten JSON-Funktionen (json_extract, ->>) Seit SQLite 3.38 ist dies eingebaut. Ältere Android-System-SQLite-Versionen mögen sie nicht haben.
Volltextsuche FTS4, FTS5 ENABLE_FTS4 / ENABLE_FTS5 in compile_options
Raumgebundene Boxen R*-Baum ENABLE_RTREE
sin, cos, sqrt, pow Mathematikfunktionen ENABLE_MATH_FUNCTIONS, SQLite 3.35+

Wenn die Funktion auf jedem Ziel verfügbar ist, hierauf zu stoppen. Die Web-Build von Fast SQL verwendet die offizielle SQLite-Wasm, die JSON, FTS5 und mathematische Funktionen enthält.

Weshalb iOS und Android unterschiedlich funktionieren

iOS. Das System libsqlite3 is built with SQLITE_OMIT_LOAD_EXTENSION, so load_extension() existiert nicht. Die App-Store-Regeln verbieten auch das Herunterladen und Ausführen neuer code (Richtlinie 2.5.2), so dass die Laden eines .dylib Bei der Ausführung ist das ohnehin nicht möglich. Die Erweiterung muss in das App-Binär kompiliert werden. Viele Tutorials rufen dann sqlite3_auto_extension() zum Start. Verlassen Sie sich nicht darauf: in der iOS SDK , die mit Xcode geliefert wird, sqlite3.h erklärt es mit API_DEPRECATED("Process-global auto extensions are not supported on Apple platforms", ...)Die zuverlässige Vorgehensweise besteht darin, Ihre init-Funktion auf jedem Verbindungshandle direkt nach dem Öffnen aufzurufen.

Android. Der SQLite-Server im Android-Framework (android.database.sqlite) hat keine API , um Erweiterungen zu laden. Sie benötigen eine SQLite-Build, die mit Ihrer App verbunden ist, die dies tut. requery/sqlite-android ist ein einfacher Ersatz für die Framework-Klassen, liefert einen aktuellen SQLite-Server (mit FTS5 und JSON) und unterstützt ladenfähige Erweiterungen über SQLiteCustomExtensionIhre Erweiterung wird zu einem .so Datei pro ABI innerhalb der APK.

Web. Der Standard-SQLite-Wasm-Build kann keine nativen code laden. Sie benötigen eine benutzerdefinierte Wasm-Build von SQLite mit Ihrer Erweiterung, die in sie kompiliert wurde.

Was die Plugins heute unterstützen

iOS Android Web
@capgo/capacitor-fast-sql 8.2 Keine Hook, Patcher erforderlich Verwendet Framework-SQLite, Patcher erforderlich Standard-SQLite Wasm
@capacitor-community/sqlite 8.1 loadExtension() wird in der JS API deklariert, aber nicht nativ implementiert Gleich Nicht implementiert

Also, für den Moment bedeutet die Hinzufügung einer Erweiterung einen kleinen nativen Patcher. Es ist ein enthaltener Änderung, und Bun kann sie auf allen Installationsmedien beibehalten.

Schritt 1: Erweiterung schreiben

Auf Distanz funktioniert es gut: nützlich, eigenständig und leicht zu überprüfen. Speichern Sie es als geo.c:

#include <math.h>
#include <stddef.h>
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1

static void haversine_km(sqlite3_context *ctx, int argc, sqlite3_value **argv) {
  (void)argc;
  for (int i = 0; i < 4; i++) {
    if (sqlite3_value_type(argv[i]) == SQLITE_NULL) {
      sqlite3_result_null(ctx);
      return;
    }
  }
  const double earth_radius_km = 6371.0088;
  const double to_rad = M_PI / 180.0;
  double lat1 = sqlite3_value_double(argv[0]) * to_rad;
  double lon1 = sqlite3_value_double(argv[1]) * to_rad;
  double lat2 = sqlite3_value_double(argv[2]) * to_rad;
  double lon2 = sqlite3_value_double(argv[3]) * to_rad;
  double dlat = lat2 - lat1;
  double dlon = lon2 - lon1;
  double a = sin(dlat / 2) * sin(dlat / 2) +
             cos(lat1) * cos(lat2) * sin(dlon / 2) * sin(dlon / 2);
  sqlite3_result_double(ctx, 2 * earth_radius_km * asin(sqrt(a)));
}

int sqlite3_geo_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi) {
  SQLITE_EXTENSION_INIT2(pApi);
  (void)pzErrMsg;
  return sqlite3_create_function_v2(
      db, "haversine_km", 4,
      SQLITE_UTF8 | SQLITE_DETERMINISTIC | SQLITE_INNOCUOUS,
      NULL, haversine_km, NULL, NULL, NULL);
}

Das gleiche Datei baut beide Wege auf:

  • Als eine ladenbare Erweiterung (Android): SQLITE_EXTENSION_INIT1/2 Routen Sie jeden SQLite-Aufruf über den pApi den SQLite in der Funktion übergibt, so dass .so kein Link gegen SQLite erfolgt.
  • Stattdessen, mit -DSQLITE_CORE (iOS): Die Makros werden zu No-ops und Aufrufe gehen direkt an die SQLite-Bibliothek, die das App verlinkt.

SQLITE_DETERMINISTIC lässt SQLite die Funktion in Ausdrücken in Indexen verwenden. SQLITE_INNOCUOUS markiert es als sicher zum Einsatz in Ansichten und Trigger.

Ich habe diese Datei beide Wege auf macOS kompiliert: statisch mit -DSQLITE_CORE und rufen sqlite3_geo_init(db, NULL, NULL) auf einem System-SQLite-Verbindung und als eine ladenbare Bibliothek durch load_extension. Beide liefern etwa 343,6 km für Paris nach London.

Schritt 2: iOS, kompiliere in die App und registriere pro Verbindung

Fügen Sie die Quelle zur App-Ziel hinzufügen

  1. In Xcode, ziehen Sie geo.c ins App Gruppe und markieren Sie das App Ziel.
  2. Xcode bietet eine Brückenkopfdatei an. Akzeptieren oder erstellen. App/App-Bridging-Header.h Build-Einstellungen > Swift-Compiler - Allgemein > Objective-C-Brückenkopfdatei Einstellungen &gt; Swift-Compiler - Allgemein &gt; Header für Objective-C.
  3. In Build Phasen > Quellcode kompilieren, doppelklicken geo.c und fügen Sie die pro-Datei-Flag hinzu -DSQLITE_CORE.

Das iOS SDK liefert sowohl sqlite3.h und sqlite3ext.hKeine zusätzlichen Header sind erforderlich.

App-Bridging-Header.h:

#include <sqlite3.h>

int sqlite3_geo_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi);

Patch Fast SQL, um einen offenen Hook freizugeben

Der Plugin öffnet Verbindungen innerhalb seines eigenen Moduls, daher benötigt es einen Hook, den Ihre App einstellen kann. Bun kann eine Abhängigkeit anpassen und den Patch bei jedem Installieren erneut anwenden:

bun patch @capgo/capacitor-fast-sql

Bearbeiten node_modules/@capgo/capacitor-fast-sql/ios/Sources/CapgoCapacitorFastSqlPlugin/SQLDatabase.swift. Add a public hook at the top level of the file:

public enum FastSqlExtensions {
    /// Called with every new connection handle right after it opens.
    /// Return SQLITE_OK (0) on success.
    public static var onOpen: ((OpaquePointer) -> Int32)?
}

Anschließend in init(...), nach dem Verschlüsselungsblock und vor try applyPerformanceOptions(...), nennen Sie es:

if let hook = FastSqlExtensions.onOpen {
    let rc = hook(db)
    if rc != SQLITE_OK {
        throw SQLError.openFailed(message: "Extension init failed with code \(rc)")
    }
}

Speichern Sie den Patch:

bun patch --commit node_modules/@capgo/capacitor-fast-sql

Bun schreibt ein Datei unter patches/ und registriert es in package.json unter patchedDependenciesCommit beide. Unsere Anleitung zum Patchen eines __CAPGO_KEEP_0__-Plugins Wie man einen Capacitor-Plugin anpasst Registrieren Sie die Erweiterung bei der Startphase

Beim Start registrieren Sie die Erweiterung.

In ios/App/App/AppDelegate.swift, importieren Sie das Plugin-Modul und setzen Sie den Hook in application(_:didFinishLaunchingWithOptions:):

import UIKit
import Capacitor
import CapgoCapacitorFastSql // CocoaPods module name. With SPM: import CapgoCapacitorFastSqlPlugin

// Inside the existing AppDelegate class:
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    FastSqlExtensions.onOpen = { db in
        sqlite3_geo_init(db, nil, nil)
    }
    return true
}

Da der Hook innerhalb des Plugins im offenen Pfad läuft, wird die Funktion jedem Verbindung zugewiesen, einschließlich solcher, die nach einem WebView-Neustart geöffnet werden. Die Zuweisung in didFinishLaunching stellt sicher, dass sie vor der Ausführung von JavaScript bereits vorliegt. connect().

SQLCipher wird erstellt. Wenn Sie die SQLCipher-Unterpakete des Plugins verwenden, gehört die Verbindung zum SQLite der SQLCipher, während Ihr -DSQLITE_CORE Das Datei kann auf die Symbol des Systembibliothek verweisen. Überprüfen Sie diese Combination explizit vor dem Versand, oder speichern Sie die abhängige Daten in einer unverschlüsselten Datenbank.

Schritt 3: Android, bündeln Sie eine SQLite-Datenbank, die Erweiterungen laden kann

Kompiliere die Erweiterung für jeden ABI

Sie benötigen das Android NDK (installieren Sie es aus dem Android Studio SDK Manager). sqlite3ext.h plus sqlite3.h Manager) und SQLite-Bibliotheksamalgam nächstbar geo.c.

export NDK=$HOME/Library/Android/sdk/ndk/<version>
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64   # linux-x86_64 on Linux
export API=24   # your minSdkVersion

for pair in \
  aarch64-linux-android:arm64-v8a \
  armv7a-linux-androideabi:armeabi-v7a \
  x86_64-linux-android:x86_64 \
  i686-linux-android:x86
do
  triple=${pair%%:*}
  abi=${pair##*:}
  mkdir -p android/app/src/main/jniLibs/$abi
  $TOOLCHAIN/bin/clang --target=$triple$API \
    -shared -fPIC -O2 \
    -Wl,-z,max-page-size=16384 \
    -o android/app/src/main/jniLibs/$abi/libgeo.so \
    geo.c -lm
done

-Wl,-z,max-page-size=16384 passt die Bibliothek für Geräte mit 16 KB Speicherseiten an. Google Play erfordert eine 16 KB-Unterstützung für Apps, die sich auf Android 15 und höher richten, siehe 16 KB-Seitengröße und Capacitor-Plugins. Wenn Ihre App nur auf 64-Bit-Geräte verteilt wird, können Sie die beiden 32-Bit-ABIs streichen.

Die Bibliothek heißt libgeo.so, daher ist der Standard-Einstiegspunkt von SQLite sqlite3_geo_init, der dem C-Funktion entspricht.

Native Bibliotheken extrahieren Sie bei der Installation

Neuere Versionen des Android Gradle Plugins behalten .so Dateien unkomprimiert innerhalb der APK, anstatt sie zu extrahieren. SQLite lädt Erweiterungen mit einem Dateipfad, daher bitten Sie um eine Extraktion in android/app/build.gradle:

android {
    packaging {
        jniLibs {
            useLegacyPackaging = true
        }
    }
}

Hinzufügen von requery sqlite-android

In android/build.gradle (root), fügen Sie JitPack ein:

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}

Patch Fast SQL zu verwenden

Öffnen Sie das gleiche Bun Patchesitzung (bun patch @capgo/capacitor-fast-sql) und bearbeiten Sie zwei Dateien.

node_modules/@capgo/capacitor-fast-sql/android/build.gradle, in dependencies:

implementation 'com.github.requery:sqlite-android:3.49.0'

Verwenden Sie die neueste Version, die JitPack als für das Repository erstellt zeigt.

node_modules/@capgo/capacitor-fast-sql/android/src/main/java/app/capgo/capacitor/fastsql/SQLDatabase.java: Tauschen Sie die Framework-Importe für requery’s und öffnen Sie sie über eine Konfiguration, die die Erweiterungen trägt.

// Replace:
// import android.database.sqlite.SQLiteDatabase;
// import android.database.sqlite.SQLiteStatement;
import io.requery.android.database.sqlite.SQLiteCustomExtension;
import io.requery.android.database.sqlite.SQLiteDatabase;
import io.requery.android.database.sqlite.SQLiteDatabaseConfiguration;
import io.requery.android.database.sqlite.SQLiteStatement;

public class SQLDatabase implements DatabaseConnection {

    /** Set from MainActivity before the first connection opens. */
    public static final java.util.List<SQLiteCustomExtension> extensions =
        new java.util.concurrent.CopyOnWriteArrayList<>();

    private SQLiteDatabase db;
    private volatile boolean inTransaction = false;

    public SQLDatabase(String path, boolean walMode, boolean performancePresets) throws Exception {
        SQLiteDatabaseConfiguration config = new SQLiteDatabaseConfiguration(
            path, SQLiteDatabase.CREATE_IF_NECESSARY);
        config.customExtensions.addAll(extensions);
        this.db = SQLiteDatabase.openDatabase(config, null, null);
        SQLPerformanceConfig.apply(db::execSQL, walMode, performancePresets);
    }

    // ... the rest of the class stays as it is
}

requery’s Klassen spiegeln die Framework API (rawQuery, compileStatement, execSQL, Transaktionen), so bleibt der Rest der Datei unverändert. android.database.Cursor bleibt wie es ist. Committe das Patchesitzung mit bun patch --commit node_modules/@capgo/capacitor-fast-sql.

Dies ändert nur den unverschlüsselten Pfad. Verschlüsselte Verbindungen verwenden SQLCipher’s eigene Klassen, die dieses Patches nicht berührt.

Registrieren Sie die Erweiterung in MainActivity

package com.example.app;

import android.os.Bundle;
import app.capgo.capacitor.fastsql.SQLDatabase;
import com.getcapacitor.BridgeActivity;
import io.requery.android.database.sqlite.SQLiteCustomExtension;

public class MainActivity extends BridgeActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        String path = getApplicationInfo().nativeLibraryDir + "/libgeo.so";
        SQLDatabase.extensions.add(new SQLiteCustomExtension(path, "sqlite3_geo_init"));
        super.onCreate(savedInstanceState);
    }
}

Registrieren Sie vor super.onCreate so die Liste wird vorher gefüllt, bevor die WebView JavaScript ausführen kann.

Java-Funktionen ohne C: Android

Wenn Sie nur eine Skalarfunktion benötigen, kann requery eine in Java geschriebene registrieren, ohne dass ein NDK erforderlich ist. Im gepatchten SQLDatabase Konstruktor, nachdem Sie geöffnet haben:

db.addFunction("haversine_km", 4, (args, result) -> {
    double r = 6371.0088;
    double lat1 = Math.toRadians(args.getDouble(0));
    double lat2 = Math.toRadians(args.getDouble(2));
    double dlat = lat2 - lat1;
    double dlon = Math.toRadians(args.getDouble(3) - args.getDouble(1));
    double a = Math.pow(Math.sin(dlat / 2), 2)
             + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dlon / 2), 2);
    result.set(2 * r * Math.asin(Math.sqrt(a)));
});

Dies ist langsamer als C für schwere Abfragen, da jeder Aufruf die JNI überquert, aber es ist viel einfacher zu pflegen. FTS5-Tokenisierer und virtuelle Tabellen benötigen immer noch C.

Schritt 4: Verwenden Sie es aus JavaScript

After bunx cap sync und eine native Rekonstruktion:

import { FastSQL } from '@capgo/capacitor-fast-sql';

const db = await FastSQL.connect({ database: 'places' });

await db.execute(`CREATE TABLE IF NOT EXISTS places (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  lat REAL NOT NULL,
  lon REAL NOT NULL
)`);

const nearby = await db.query(
  `SELECT id, name, haversine_km(?, ?, lat, lon) AS km
   FROM places
   WHERE lat BETWEEN ? AND ? AND lon BETWEEN ? AND ?
   ORDER BY km
   LIMIT 20`,
  [userLat, userLon, userLat - 0.5, userLat + 0.5, userLon - 0.7, userLon + 0.7],
);

The BETWEEN Die Box ermöglicht es SQLite, einen Index auf (lat, lon) um Kandidaten vor der Funktion zu verengen, bevor sie genaue Entfernungen berechnet. Ohne es läuft die Funktion auf jeder Zeile.

Eine JavaScript-Fallback für die Webanwendung

Die Web-Ausgabe kann Ihren C code nicht laden, daher detektieren Sie ihn und fahren Sie zurück:

import { Capacitor } from '@capacitor/core';

export const hasGeoExtension = Capacitor.isNativePlatform();

// On the web: query the bounding box, then sort in JS with the same formula.

Benutzerdefinierte FTS5-Tokenisierer

Das gleiche Mechanismus registriert FTS5-Tokenisierer. In Ihrer Initialisierungsfunktion erhalten Sie den fts5_api Zeiger mit dem SELECT fts5(?1) Muster aus der FTS5-Dokumentation , rufen Sie dann xCreateTokenizerzwei zusätzliche Anforderungen:

  • Die SQLite-Bibliothek muss FTS5 enthalten. requery’s Build enthält es. Auf iOS überprüfen Sie PRAGMA compile_options für ENABLE_FTS5.
  • Jede Verbindung, die in das FTS-Table schreibt oder ausliest, muss den Tokenisierer registrieren, weil FTS5 den Tokenisierer-Namen in der Tabelle definiert. Dies ist der Grund, warum die pro-Verbindung-Hook wichtig ist.
CREATE VIRTUAL TABLE docs USING fts5(title, body, tokenize = 'my_tokenizer');

Versand und Wartung

  • Native Rebuild erforderlich. Erweiterungen, Patches und Gradle-Änderungen sind native. Sie benötigen eine neue App-Store- und Play-Store-Version. Capgo Build Man kann sowohl im Cloud bereitstellen als auch signieren. Seit April 2026 müssen iOS-Uploads mit Xcode 26 oder höher erstellt werden.
  • JavaScript kann immer noch schnell laufen. SQL, das aufruft haversine_km lebt in Ihrem Web-Bundle und kann mit live Updatesversendet werden, solange die native App, die die Erweiterung enthält, bereits installiert ist. Neue SQL-Abfragen werden auf eine native Version überprüft, wenn Benutzer noch eine ältere Version ausführen können.
  • Plugin-Updates. Wenn Sie Fast SQL aktualisieren, wird Bun die Patches erneut angewendet. Wenn die gepatchten Zeilen verschoben wurden, erfolgt die Installation lautstark anstatt stillschweigend, um Ihre Hook zu entfernen. Erstellen Sie den Patch auf der neuen Version neu.

Fehlersuche

no such function: haversine_km auf iOS. Der Hook wurde vorher nicht gesetzt. connect()oder die Datenbank wurde vor der Anwendung geöffnet. didFinishLaunching prüfen. Logge in der Hook, um sicherzustellen, dass er aufgerufen wird.

Crasht innerhalb sqlite3_geo_init auf iOS. -DSQLITE_CORE fehlt, sodass das kompilierte Datei als eine ladenbare Erweiterung, die auf den pApi Zeiger verweist, kompiliert wird, der nil hier ist. Füge die pro-Datei-Flag in Compile Sources hinzu.

no such function auf Android. Der gepatchte SQLDatabase importiert immer noch android.database.sqlite.SQLiteDatabaseoder die Erweiterung wurde der Liste nach der ersten Verbindung hinzugefügt. Log SQLDatabase.extensions.size() bei der Verbindung

dlopen failed: library "…/libgeo.so" not found. Die Bibliothek fehlt für das Geräte-ABI (die meisten Smartphones sind arm64-v8aoder native Bibliotheken wurden nicht extrahiert. Überprüfen useLegacyPackaging und inspizieren Sie das APK mit Android Studios APK-Analysewerkzeug.

undefined symbol: sqlite3_geo_init. Der Eingangspunktname, der an SQLiteCustomExtension passt nicht zum C-Funktion, oder die Funktion wurde deklariert static.

Das Play-Console warnt vor der 16-Byte-Alignment. Rebuild die .so Dateien mit -Wl,-z,max-page-size=16384 und einem aktuellen NDK, und aktualisieren Sie requery auf eine Version, die mit 16-Byte-Alignment erstellt wurde.

Die Funktion funktioniert auf Android, aber nicht im iOS-Simulator. Make sure geo.c ist ein Mitglied der App Zielgruppe, nicht nur in der Datei-Struktur vorhanden ist.

Zusammenfassung

Plattform Wie die Erweiterung in das Wo sie registriert ist
iOS Mit der App kompiliert wird -DSQLITE_CORE Plugin-Hook, der auf jeden neuen Verbindung sqlite3_geo_init bei jeder neuen Verbindung
Android .so per ABI in jniLibs, geladen durch requery sqlite-android SQLiteCustomExtension Liste wird bei jedem Verbindungsaufbau gelesen
Web Von der Standard SQLite Wasm nicht unterstützt JavaScript-Fallback

Start by checking the built-in features. If you still need an extension, write it once in C, keep the native patches small, and test on the oldest devices you support. For the general plugin setup, see Wie Sie SQLite in einer Capacitor-App verwenden.

Live-Updates für Capacitor-Apps

Wenn ein Web-Schicht-Bug live ist, versenden Sie die Reparatur über Capgo anstatt Tage auf die Genehmigung der App-Stores zu warten. Die Benutzer erhalten die Aktualisierung im Hintergrund, während native Änderungen im normalen Review-Verfahren bleiben.

Neueste von unserem Blog

Capgo gibt Ihnen die besten Einblicke, die Sie benötigen, um ein wirklich professionelles Mobiltelefon-App zu erstellen.