Actual/Expect 类 在 Kotlin Multiplatform 项目中不起作用

Actual/Expect classes doesn't work in Kotlin Mutliplatform proyect

我正在开发一个 KMM 应用程序,我尝试在其中分别使用 SQLDelight 和 Ktor 实现常规的本地数据源和远程数据源。

当我尝试将本机代码从 AndroidApp 和 iosMain 共享到 commonModule 时,我的问题就来了。我开始在我的 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

有点混乱,因为我没有在我的项目中使用 jvm 模块,尽管我使用 IOS 模块。

这是我的 cacheAndroid.kt 的 Android 应用程序模块:

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)
}

这是我的 IOS 模块的 classes:

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)
}

并使用到commonModule中:

internal expect fun cache(): PetsDatabase

我在最后一行代码中收到了上面的错误,但我也将错误带入了 Android 和 IOS 模块的实际 classes,进入了它们的预计 class 变体。

最后关于我的build.gradle(普通)

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")
    }
}

还有我的项目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
}

希望您能帮到我,如果喜欢,请提前致谢!

我认为它与您 Gradle 中的 packageName 有关:

 packageName = "com.example.kmp_app.db"
  1. 尝试传递缓存函数的路由而不是 "com.example.kmp_app.db" 比如如果我的缓存函数存在于 dataSource.cacheSource,我们将传递 "com.example.kmp_app.db.dataSource.cacheSource"

  2. 确保您的 Cache actual / expect 函数具有与此相同的包名称 "com.example.kmp_app.db.dataSource.cacheSource"

共享gradle

sqldelight {
    database("RecipeDatabase") {
        packageName = "com.example.food1fork.Food1ForkKmm.DataSource.cacheSource"
        sourceFolders = listOf("SqlDelight")
    }
}

iOS 模块

package com.example.food1fork.Food1ForkKmm.DataSource.cacheSource

actual class DriverFactory {
    actual fun createDriver(): SqlDriver {
        return NativeSqliteDriver(RecipeDatabase.Schema, "recipes.db")
    }
}

Android 模块

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")
    }
}

共享模块

package com.example.food1fork.Food1ForkKmm.DataSource.cacheSource

expect class DriverFactory {
    fun createDriver(): SqlDriver
}