I was develoing an KMM App, where I try to implement a regular localDatasource and remoteDatasource, using SQLDelight and Ktor respectively.
My problems comes when I try to shared the native code from AndroidApp and iosMain, into commonModule. I start to get the following error into my commonModule expect class:
Expected function 'cache' has no actual declaration in module KMM-APP.shared for JVM
Expected function 'cache' has no actual declaration in module KMM-APP.shared.iosArm64Main for Native
Expected function 'cache' has no actual declaration in module KMM-APP.shared.iosX64Main for Native
It's a bit confuse, in order I don't make use of jvm module in my proyect, although I do for IOS module.
Here it's my cacheAndroid.kt of AndroidApp module:
import android.content.Context
import com.example.kmp_app.db.PetsDatabase
import com.squareup.sqldelight.android.AndroidSqliteDriver
lateinit var appContext: Context
internal actual fun cache(): PetsDatabase {
val driver = AndroidSqliteDriver(PetsDatabase.Schema, appContext, "petsDB.db")
return PetsDatabase(driver)
}
Here is the classes of my IOS module:
import com.example.kmp_app.db.PetsDatabase
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
internal actual fun cache(): PetsDatabase {
val driver = NativeSqliteDriver(PetsDatabase.Schema, "petsDB.db")
return PetsDatabase(driver)
}
And the use into commonModule:
internal expect fun cache(): PetsDatabase
I in this last line of code where I reciving the error above, but I also get the error into the actual classes of Android and IOS modules, into their expect class variant.
Finally regarding my build.gradle(common)
plugins {
kotlin("multiplatform")
kotlin("native.cocoapods")
id("com.android.library")
id("kotlinx-serialization")
id("com.squareup.sqldelight")
}
version = "1.0"
kotlin {
targets{
ios {
binaries {
framework {
baseName = "shared"
}
}
}
// Block from https://github.com/cashapp/sqldelight/issues/2044#issuecomment-721299517.
val onPhone = System.getenv("SDK_NAME")?.startsWith("iphoneos") ?: false
if (onPhone) {
iosArm64("ios")
} else {
iosX64("ios")
}
android()
//iosSimulatorArm64() sure all ios dependencies support this target
}
cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
ios.deploymentTarget = "14.1"
podfile = project.file("../iosApp/Podfile")
}
sourceSets {
all {
languageSettings.apply {
useExperimentalAnnotation("kotlinx.coroutines.ExperimentalCoroutinesApi")
}
}
val commonMain by getting{
dependencies {
implementation(kotlin("stdlib-common"))
implementation(Coroutines.Core.core)
implementation(Ktor.Core.common)
implementation(Ktor.Json.common)
implementation(Ktor.Logging.common)
implementation(Ktor.Serialization.common)
implementation(SqlDelight.runtime)
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
implementation(Ktor.Mock.common)
}
}
val androidMain by getting{
dependencies {
implementation(kotlin("stdlib"))
implementation(Coroutines.Core.core)
implementation(Ktor.android)
implementation(Ktor.Core.jvm)
implementation(Ktor.Json.jvm)
implementation(Ktor.Logging.jvm)
implementation(Ktor.Logging.slf4j)
implementation(Ktor.Mock.jvm)
implementation(Ktor.Serialization.jvm)
implementation(Serialization.core)
implementation(SqlDelight.android)
}
}
val androidAndroidTestRelease by getting
val androidTest by getting {
dependsOn(androidAndroidTestRelease)
dependencies {
implementation(kotlin("test-junit"))
implementation("junit:junit:4.13.2")
}
}
val iosX64Main by getting
val iosArm64Main by getting
//val iosSimulatorArm64Main by getting
val ios by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
//iosSimulatorArm64Main.dependsOn(this)
dependencies {
implementation(SqlDelight.native)
}
}
}
}
android {
compileSdk = 31
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdk = 21
targetSdk = 31
versionCode = 1
versionName = "1.0"
}
}
sqldelight {
database("PetsDatabase") {
packageName = "com.example.kmp_app.db"
sourceFolders = listOf("sqldelight")
}
}
And my proyect build.gradle:
buildscript {
repositories {
google()
mavenCentral()
jcenter()
}
dependencies {
classpath("com.android.tools.build:gradle:4.2.0")
classpath(kotlin("gradle-plugin", version = Versions.kotlin))
classpath(kotlin("serialization", version = Versions.kotlin))
classpath("com.squareup.sqldelight:gradle-plugin:${Versions.sqldelight}")
}
}
allprojects {
repositories {
google()
mavenCentral()
jcenter()
}
}
plugins{
//kotlin("android") version "${Versions.kotlin}" apply false
}
I hope you can help and if like this, take thanks in advance !
I think it related with packageName
in your Gradle:
packageName = "com.example.kmp_app.db"
Try to pass route of your cache function instead of "com.example.kmp_app.db"
like if my cache function exists on dataSource.cacheSource
, we will pass "com.example.kmp_app.db.dataSource.cacheSource"
Be sure your Cache actual / expect function have the same package name like this "com.example.kmp_app.db.dataSource.cacheSource"
Shared gradle
sqldelight {
database("RecipeDatabase") {
packageName = "com.example.food1fork.Food1ForkKmm.DataSource.cacheSource"
sourceFolders = listOf("SqlDelight")
}
}
iOS module
package com.example.food1fork.Food1ForkKmm.DataSource.cacheSource
actual class DriverFactory {
actual fun createDriver(): SqlDriver {
return NativeSqliteDriver(RecipeDatabase.Schema, "recipes.db")
}
}
Android module
package com.example.food1fork.Food1ForkKmm.DataSource.cacheSource
actual class DriverFactory(private val context: Context) {
actual fun createDriver(): SqlDriver {
return AndroidSqliteDriver(RecipeDatabase.Schema, context, "recipes.db")
}
}
Shared module
package com.example.food1fork.Food1ForkKmm.DataSource.cacheSource
expect class DriverFactory {
fun createDriver(): SqlDriver
}