Multiplatform setup with the SqlJs Driver
First apply the gradle plugin in your project.
plugins {
id("app.cash.sqldelight") version "2.0.0-alpha05"
}
repositories {
google()
mavenCentral()
}
sqldelight {
databases {
create("Database") {
packageName.set("com.example")
}
}
}
plugins {
id "app.cash.sqldelight" version "2.0.0-alpha05"
}
repositories {
google()
mavenCentral()
}
sqldelight {
databases {
Database { // This will be the name of the generated database class.
packageName = "com.example"
}
}
}
Put your SQL statements in a .sq
file under src/main/sqldelight
. Typically the first statement in the SQL file creates a table.
-- src/main/sqldelight/com/example/sqldelight/hockey/data/Player.sq
CREATE TABLE hockeyPlayer (
player_number INTEGER PRIMARY KEY NOT NULL,
full_name TEXT NOT NULL
);
CREATE INDEX hockeyPlayer_full_name ON hockeyPlayer(full_name);
INSERT INTO hockeyPlayer (player_number, full_name)
VALUES (15, 'Ryan Getzlaf');
From this SQLDelight will generate a Database
Kotlin class with an associated Schema
object that can be used to create your database and run your statements on it. Doing this also requires a driver, which SQLDelight provides implementations of:
kotlin {
// The drivers needed will change depending on what platforms you target:
sourceSets.androidMain.dependencies {
implementation "app.cash.sqldelight:android-driver:2.0.0-alpha05"
}
// or sourceSets.iosMain, sourceSets.windowsMain, etc.
sourceSets.nativeMain.dependencies {
implementation "app.cash.sqldelight:native-driver:2.0.0-alpha05"
}
sourceSets.jvmMain.dependencies {
implementation "app.cash.sqldelight:sqlite-driver:2.0.0-alpha05"
}
sourceSets.jsMain.dependencies {
implementation "app.cash.sqldelight:sqljs-driver:2.0.0-alpha05"
implementation npm("sql.js", "1.6.2")
implementation devNpm("copy-webpack-plugin", "9.1.0")
}
}
Because the SqlJs driver must be initialized asynchronously, the drivers for other platforms must be initialized in a compatible way to be usable in a common source set.
The drivers can be initialized in a coroutine, and a higher-order function can be used to ensure that the driver is initialized before executing a block of code that requires the database:
// in src/commonMain/kotlin
expect suspend fun provideDbDriver(schema: SqlDriver.Schema): SqlDriver
class SharedDatabase(
private val driverProvider: suspend (SqlDriver.Schema) -> SqlDriver
) {
private var database: Database? = null
suspend fun initDatabase() {
if (database == null) {
database = driverProvider(Database.Schema).createDatabase()
}
}
suspend operator fun <R> invoke(block: suspend (Database) -> R): R {
initDatabase()
return block(database!!)
}
private fun SqlDriver.createDatabase(): Database { /* ... */ }
}
val sharedDb = SharedDatabase(::createTestDbDriver)
class DataRepository(
private val withDatabase: SharedDatabase = sharedDb
) {
suspend fun getData() = withDatabase { database ->
/* Do something with the database */
}
}
// in src/jsMain/kotlin
actual suspend fun provideDbDriver(schema: SqlDriver.Schema): SqlDriver {
return initSqlDriver(schema).await()
}
// in src/nativeMain/kotlin
actual suspend fun provideDbDriver(schema: SqlDriver.Schema): SqlDriver {
return NativeSqliteDriver(schema, "test.db")
}
// in src/jvmMain/kotlin
actual suspend fun provideDbDriver(schema: SqlDriver.Schema): SqlDriver {
return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY).also { driver ->
schema.create(driver)
}
}