Catalog 1.7 to 1.8

The major breaking change was upgrading sqldelight to 2.* you can visit their official docs for detailed steps

use latest catalog version or staring from 1.8


settings.gradle.kts

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
    versionCatalogs {
        create("kmpLibs") { 
-            from("io.telereso.kmp:catalog:1.7")
+            from("io.telereso.kmp:catalog:1.8")
            // override versions
            // version("teleresoCore", "0.3.0")
        }
    }
}

build.gradle.kts (root project)


plugins {
    //trick: for the same plugin versions in all sub-modules
    alias(kmpLibs.plugins.kotlin.android) apply false
    alias(kmpLibs.plugins.android.application) apply false
    alias(kmpLibs.plugins.android.library) apply false
    alias(kmpLibs.plugins.kotlin.jvm) apply false
    alias(kmpLibs.plugins.kotlin.multiplatform) apply false
    alias(kmpLibs.plugins.kotlin.serialization) apply false
    alias(kmpLibs.plugins.kotlin.parcelize) apply false
    alias(kmpLibs.plugins.kotlin.native.cocoapods) apply false
    alias(kmpLibs.plugins.buildkonfig) apply false
+    alias(kmpLibs.plugins.sqldelight) apply false
}

buildscript {
    repositories {
        mavenLocal()
        mavenCentral()
        google()
    }
    dependencies {
-        classpath("com.squareup.sqldelight:gradle-plugin:${kmpLibs.versions.sqldelight.get()}")
        classpath("org.jfrog.buildinfo:build-info-extractor-gradle:latest.release")
    }
}



my-client/build.gradle.kts (module project)


plugins {
    alias(kmpLibs.plugins.android.library)
    alias(kmpLibs.plugins.kotlin.multiplatform)
    alias(kmpLibs.plugins.kotlin.native.cocoapods)
    alias(kmpLibs.plugins.kotlin.serialization)
    alias(kmpLibs.plugins.kotlin.parcelize)
    alias(kmpLibs.plugins.kotlinx.kover)
    alias(kmpLibs.plugins.test.logger)
    alias(kmpLibs.plugins.dokka)
    alias(kmpLibs.plugins.buildkonfig)
    alias(kmpLibs.plugins.telereso.kmp)
+   alias(kmpLibs.plugins.sqldelight)
    alias(airasiaLibs.plugins.detekt)

-   id("com.squareup.sqldelight")
}

kotlin {
    androidTarget {
        publishLibraryVariants("release")
    }
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        val platform = when (it.name) {
            "iosSimulatorArm64", "iosX64" -> "iphonesimulator"
            else -> "iphoneos"
        }

        it.binaries.all {
            linkerOpts("-ObjC")
            linkerOpts("-L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/$platform")
            linkerOpts("-L/usr/lib/swift/")
        }
    }


    jvm()

    js {
        moduleName = "my-client"
        version = project.version as String

-       browser {
-           testTask {
-               useMocha{
-                   timeout = "5000"
-               }
-           }
-       }
-
-       nodejs{
-           testTask {
-               useMocha{
-                   timeout = "5000"
-               }
-           }
-       }
+       browser()

        binaries.library()

        binaries.executable()
        generateTypeScriptDefinitions()
    }

    sourceSets {

        all {
            languageSettings.optIn("kotlin.js.ExperimentalJsExport")
        }

        val commonMain by getting {
            dependencies {
                implementation(kmpLibs.bundles.ktor)

                implementation(kmpLibs.bundles.kotlinx)
                implementation(kmpLibs.bundles.sqldelight)
            }
        }
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test"))
                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))
                implementation(kmpLibs.test.kotlinx.coroutines.test)
                implementation(kmpLibs.bundles.test.kotest)
                // Ktor Server Mock
                implementation(kmpLibs.test.ktor.client.mock)

                implementation(kmpLibs.test.turbine)
+               implementation(kmpLibs.test.telereso.core)
            }
        }
        val jvmMain by getting {
            dependencies {
                implementation(kmpLibs.ktor.client.okhttp)
                implementation(kmpLibs.okhttp.logging)
                implementation(kmpLibs.sqldelight.runtime.jvm)
                implementation(kmpLibs.sqldelight.sqlite.driver)
            }
        }

        val jvmTest by getting {
            dependsOn(commonTest)
            dependencies {
+               implementation(kmpLibs.sqldelight.sqlite.driver)
            }
        }
        val androidMain by getting {
            dependencies {
                implementation(kmpLibs.ktor.client.okhttp)
                implementation(kmpLibs.okhttp.logging)
+               implementation(kmpLibs.sqldelight.android.driver)
            }
        }
        val androidUnitTest by getting {
            dependsOn(commonTest)
            dependencies {
+               implementation(kmpLibs.sqldelight.sqlite.driver)
            }
        }
        val iosX64Main by getting
        val iosArm64Main by getting
        val iosSimulatorArm64Main by getting

        /**
         * By using the by creating scope, we ensure the rest of the Darwin targets
         * pick dependecies from the iOSMain.
         * Note using this actual implementations should only exist in the iosMain else
         * the project will complain.
         */
        val iosMain by creating {
            dependsOn(commonMain)
            iosX64Main.dependsOn(this)
            iosArm64Main.dependsOn(this)
            iosSimulatorArm64Main.dependsOn(this)
            dependencies {
                implementation(kmpLibs.ktor.client.darwin)
                implementation(kmpLibs.sqldelight.native.driver)
            }
        }
        val iosX64Test by getting
        val iosArm64Test by getting
        val iosSimulatorArm64Test by getting {
            dependsOn(commonTest)
        }
        val iosTest by creating {
            dependsOn(commonTest)
            iosX64Test.dependsOn(this)
            iosArm64Test.dependsOn(this)
        }

        /**
         * Adding main and test for JS.
         */
        val jsMain by getting {
            dependencies {
                implementation(kmpLibs.ktor.client.js)

-               implementation(kmpLibs.sqldelight.sqljs.driver)
+               implementation(kmpLibs.sqldelight.web.worker.driver)
+               implementation(npm("@cashapp/sqldelight-sqljs-worker", "2.0.2"))
+               implementation(devNpm("copy-webpack-plugin", "9.1.0"))

                implementation(npm("sql.js", kmpLibs.versions.sqlJs.get()))
            }
        }
        val jsTest by getting {
            dependsOn(commonTest)
            dependencies {
-               implementation(kmpLibs.sqldelight.sqljs.driver)
+               implementation(kmpLibs.sqldelight.web.worker.driver)
            }
        }
    }
}


-sqldelight {
-    database("MyClientDatabase") {
-        packageName = "com.example.myclient"
-
-        sourceFolders = listOf("sqldelight")
-
-        schemaOutputDirectory = file("src/commonMain/sqldelight/com/example/myclient/cache")
-
-        verifyMigrations = true
-}

+sqldelight {
+    databases {
+        create("MyClientDatabase") {
+            packageName = dbPackage
+            schemaOutputDirectory = file("src/commonMain/sqldelight/com/example/myclient/cache")
+            verifyMigrations = true
+            generateAsync.set(true)
+        }
+    }
+}


my-client/karma.config.d/sqljs-config.js

Add karma test config in the module you used sqldelight plugin

const path = require("path");
const os = require("os");
const dist = path.resolve("../../../node_modules/sql.js/dist/")
const wasm = path.join(dist, "sql-wasm.wasm")

config.files.push({
    pattern: wasm,
    served: true,
    watched: false,
    included: false,
    nocache: false,
});

config.proxies["/sql-wasm.wasm"] = path.join("/absolute/", wasm)

// Adapted from: https://github.com/ryanclark/karma-webpack/issues/498#issuecomment-790040818
const output = {
  path: path.join(os.tmpdir(), '_karma_webpack_') + Math.floor(Math.random() * 1000000),
}
config.set({
  webpack: {...config.webpack, output},
  client: {
      mocha: {
        timeout: 10000
      }
    }
});

config.files.push({
  pattern: `${output.path}/**/*`,
  watched: false,
  included: false,
});

config.files.push({
   pattern: 'kotlin/*.json',
   watched: true,
   served: true,
   included: false
});


my-client/webpack.config.d/node.js

update your webpack config

config.resolve.alias = {
    fs: false,
    path: false,
    crypto: false,
}

+ const CopyWebpackPlugin = require('copy-webpack-plugin');
+ config.plugins.push(
+     new CopyWebpackPlugin({
+         patterns: [
+             '../../../node_modules/sql.js/dist/sql-wasm.wasm'
+         ]
+     })
+ );


my-client/src/commonMain/kotlin/com/example/my/client/cache/Dao.kt

Update the following

  • executeAsList -> awaitAsList
  • executeAsOne -> awaitAsOne
  • executeAsOneOrNull -> awaitAsOneOrNull
  • mapToList() -> mapToList(DispatchersProvider.Default)

my-client/src/commonMain/kotlin/com/example/my/client/cache/SharedDatabase.kt

internal class SharedDatabase(
-    private val driverProvider: suspend (schema: SqlDriver.Schema, databaseDriverFactory: MyClientDatabaseDriverFactory?) -> SqlDriver,
-    private val databaseDriverFactory: MyClientDatabaseDriverFactory?
+    private val databaseDriverFactory: SqlDriverFactory
{
    private var database: MyClientDatabase? = null

    private suspend fun initDatabase() {
        if (database == null) {
-            database = Dao.getDatabase(driverProvider(MyClientDatabase.Schema, databaseDriverFactory))
+            database = Dao.getDatabase(databaseDriverFactory.createDriver())
        }
    }

    suspend operator fun <R> invoke(block: suspend (MyClientDatabase) -> R): R {
        initDatabase()
        return block(database!!)
    }

    suspend fun <R> queries(block: suspend (MyClientDatabaseQueries) -> R): R {
        initDatabase()
        return block(database!!.myClientDatabaseQueries)
    }
}

my-client/src/commonMain/kotlin/com/example/my/client/MyClientManger.kt

    private val dataBase: SharedDatabase by lazy {
_        SharedDatabase(::provideDbDriver, databaseDriverFactory)
+        SharedDatabase(databaseDriverFactory ?: MyClientDatabaseDriverFactory.default())
    }

my-client/src/commonMain/kotlin/com/example/my/client/Platform.kt


-expect interface Parcelable

-@OptIn(ExperimentalMultiplatform::class)
-@OptionalExpectation
-@Target(AnnotationTarget.CLASS)
-@Retention(AnnotationRetention.BINARY)
-expect annotation class CommonParcelize()

-expect suspend fun provideDbDriver(
-    schema: SqlDriver.Schema,
-    databaseDriverFactory: MyClientDatabaseDriverFactory?
-): SqlDriver

-expect class MyClientDatabaseDriverFactory {
-    fun createDriver(): SqlDriver
-}

+expect open class MyClientDatabaseDriverFactory : SqlDriverFactory {
+    companion object {
+        fun default(databaseName: String? = null): SqlDriverFactory
+    }
+    override fun getAsyncSchema(): SqlSchema<QueryResult.AsyncValue<Unit>>
+}

my-client/src/androidMain/kotlin/PlatformAndroid.kt

- /**
-  * Android we type the alias to the real Parcelable
-  */
- actual typealias  Parcelable = android.os.Parcelable
- 
- actual suspend fun provideDbDriver(
-     schema: SqlDriver.Schema,
-     databaseDriverFactory: MyClientDatabaseDriverFactory?
- ): SqlDriver {
-     return databaseDriverFactory!!.createDriver()
- }
+ 
+ actual class MyClientDatabaseDriverFactory(private val context: Context) {
+     actual fun createDriver(): SqlDriver {
+         return AndroidSqliteDriver(MyClientDatabase.Schema, context, Dao.DATABASE_NAME)
+ actual open class MyClientDatabaseDriverFactory(
+     context: Context?,
+     databaseName: String? = null
+ ) :
+     SqlDriverFactory(databaseName ?: Dao.DATABASE_NAME, context) {
+     actual companion object {
+         actual fun default(databaseName: String?): SqlDriverFactory {
+             return MyClientDatabaseDriverFactory(null, databaseName)
+         }
+     }
+ }
+     actual override fun getAsyncSchema() = MyClientDatabase.Schema
+     override fun getSchema(): SqlSchema<QueryResult.Value<Unit>>? =MyClientDatabase.Schema.synchronous()
+ }

my-client/src/commonTest/kotlin/cache/DaoTest.kt


class DaoTest {

+    companion object {
+        fun getTestFactory(): SqlDriverFactory {
+            return TestSqlDriverFactory(MyClientDatabaseDriverFactory.default())
+        }
+    }
    
    
}


my-client/src/androidUnitTest/kotlin/PlatformAndroidTest.kt


- import com.squareup.sqldelight.db.SqlDriver
- import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
- import java.io.File
- 
- actual class Resource actual constructor(actual val name: String) {
-     private val file = File("${TestUtils.RESOURCE_PATH}/$name")
- 
-     actual fun exists(): Boolean = file.exists()
- 
-     actual fun readText(): String = file.readText()
- }
- 
- actual suspend fun provideDbDriverTest(schema: SqlDriver.Schema,
-                                        databaseDriverFactory: MyClientDatabaseDriverFactory?): SqlDriver {
-     return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY).apply {
-         schema.create(this)
-     }
- }


my-client/src/iosTest/kotlin/PlatformIosTest.kt


-private var dbIndex = 0

-@OptIn(ExperimentalForeignApi::class)
-actual class Resource actual constructor(actual val name: String) {
-    val pathParts = name.split("[.|/]".toRegex())
-    val path = NSBundle.mainBundle.pathForResource("resources/${pathParts[0]}", pathParts[1])
-    private val file: CPointer<FILE>? = fopen(path, "r")
-    actual fun exists(): Boolean = file != null
-
-    actual fun readText(): String {
-        fseek(file, 0, SEEK_END)
-        val size = ftell(file)
-        rewind(file)
-
-        return memScoped {
-            val tmp = allocArray<ByteVar>(size)
-            fread(tmp, sizeOf<ByteVar>().convert(), size.convert(), file)
-            tmp.toKString()
-        }
-    }
-}

-actual suspend fun provideDbDriverTest(schema: SqlDriver.Schema,
-                                       databaseDriverFactory: MyClientDatabaseDriverFactory?): SqlDriver {
-    return NativeSqliteDriver(
-        DatabaseConfiguration(
-            name = "${++dbIndex}-${Dao.DATABASE_NAME}",
-            version = schema.version,
-            create = { connection ->
-                wrapConnection(connection) { schema.create(it) }
-            },
-            upgrade = { connection, oldVersion, newVersion ->
-                wrapConnection(connection) {
-                    schema.migrate(it, oldVersion, newVersion)
-                }
-            },
-            inMemory = true
-        )
-    )


run

./gradlew kotlinUpgradeYarnLock