14

I have implemented the latest version of Room in my KMP project (roomCommon = "2.7.0-alpha06" ). The new version change the instantiation setup for a RoomDatabase in a KMP project

https://developer.android.com/jetpack/androidx/releases/room#2.7.0-alpha06

Now we have to implement RoomDatabaseConstructor.

So that's my database file on commonMain directory:

import androidx.room.ConstructedBy
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.RoomDatabaseConstructor
import data.database.dao.UserPreferencesDAO
import data.database.entities.UserPreferencesEntity

const val DATABASE_NAME = "app_database.db"

expect object MyDatabaseCtor : RoomDatabaseConstructor<AppDatabase>

@Database(entities = [UserPreferencesEntity::class], version = 1)
@ConstructedBy(MyDatabaseCtor::class)
abstract class AppDatabase : RoomDatabase(), DB {
    abstract fun getDao(): UserPreferencesDAO
    override fun clearAllTables(): Unit {}
}

interface DB {
    fun clearAllTables(): Unit {}
}

Them, after that I have implemented my actual functions on androidMain and iosMain

iOS:

actual object MyDatabaseCtor : RoomDatabaseConstructor<AppDatabase> {
    override fun initialize(): AppDatabase {
        return getDatabase()
    }
}

fun getDatabase(): AppDatabase {
    return Room.databaseBuilder<AppDatabase>(name = DATABASE_NAME).setDriver(BundledSQLiteDriver())
        .setQueryCoroutineContext(Dispatchers.IO).build()
}

Android:

actual object MyDatabaseCtor : RoomDatabaseConstructor<AppDatabase>{

    private val appContext = RickMortyApp.context
    override fun initialize(): AppDatabase {
        val dbFile = appContext.getDatabasePath(DATABASE_NAME)
        return Room.databaseBuilder<AppDatabase>(appContext, dbFile.absolutePath)
            .setDriver(BundledSQLiteDriver())
            .setQueryCoroutineContext(Dispatchers.IO)
            .build()
    }
}

The problem is when I build the code I get a autogenerated class

public actual object MyDatabaseCtor : RoomDatabaseConstructor<AppDatabase> {
  override fun initialize(): AppDatabase = `data`.database.AppDatabase_Impl()
}

And my Actual/Expected classes start to fail with the next message:

**Error1:**

project/composeApp/build/generated/ksp/metadata/commonMain/kotlin/data/database/MyDatabaseCtor.kt:5:22 'actual object MyDatabaseCtor : RoomDatabaseConstructor<AppDatabase>' has no corresponding expected declaration

**Error 2:**

Redeclaration:
actual object MyDatabaseCtor : RoomDatabaseConstructor<AppDatabase>

**Error 3:**

/composeApp/src/androidMain/kotlin/data/database/Database.android.kt:13:15 'actual object MyDatabaseCtor : RoomDatabaseConstructor<AppDatabase>' has no corresponding expected declaration

I tried to clean project to remove de autogenerated class but after compile is the same error class.

2
  • Same here. I also noticed this warning on the expect version of the Constructor. Looks like maybe they didn't test these updates with Kotlin 2.0? """Object 'DatabaseConstructor': expect and corresponding actual are declared in the same module, which will be prohibited in Kotlin 2.0. See youtrack.jetbrains.com/issue/KT-55177""" Commented Aug 12, 2024 at 3:46
  • @avatar_aang if you don't run KSP on commonMain, that error disappears Commented Aug 14, 2024 at 4:32

5 Answers 5

10

Ok I think I figured this out.

I replaced this line

dependencies.kspCommonMainMetadata(libs.room.compiler)

with this:

dependencies {
    listOf(
        "kspAndroid",
        // "kspJvm",
        "kspIosSimulatorArm64",
        "kspIosX64",
        "kspIosArm64"
    ).forEach {
        add(it, libs.room.compiler)
    }
}

Based on the advice here: https://issuetracker.google.com/issues/342905180#comment21

I also removed this code which was a workaround for errors in the previous version

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>().configureEach {
    if (name != "kspCommonMainKotlinMetadata") {
        dependsOn("kspCommonMainKotlinMetadata")
    }
}

For some reason JVM is still not working with this error, which is why I have it commented out for the moment: Configuration with name 'kspJvm' not found.

Sign up to request clarification or add additional context in comments.

5 Comments

I get another errro: Cannot change attributes of configuration ':composeApp:debugFrameworkIosX64' after it has been locked for mutation
Nope, doesn't work.
This approach above doesn't work for me.
worked for after bumping room version to 2.7.0-alpha08 and following the steps in issuetracker.google.com/issues/342905180#comment21
kspJvm is likely not working due to adding a name when including jvm as a target. For example, if you use jvm("desktop") Then you should use add("kspDesktop", libs.room.compiler)
2

Changed build.gradle.kts (app level) file like this

ksp { arg("room.schemaLocation","${projectDir}/schemas") } added dependencies:

dependencies { 

    // Android

    add("kspAndroid", libs.room.compiler)

    // JVM (Desktop)

    add("kspDesktop", libs.room.compiler)

    // iOS

    add("kspIosSimulatorArm64", libs.room.compiler)

    add("kspIosX64", libs.room.compiler)

    add("kspIosArm64", libs.room.compiler)

}

It worked for me.

Comments

1

This is what worked for me

  • bump room version to 2.7.0-alpha08
  • remove room gradle plugin from build.gradle.kts
  • replace room { schema ...} with
ksp {
    arg("room.schemaLocation", "${projectDir}/schemas")
}

Comments

1
// shared/src/commonMain/kotlin/Database.kt

@Database(entities = [TodoEntity::class], version = 1)
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
  abstract fun getDao(): TodoDao
}

// The Room compiler generates the `actual` implementations.
@Suppress("NO_ACTUAL_FOR_EXPECT")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase> {
    override fun initialize(): AppDatabase
}

@Dao
interface TodoDao {
  @Insert
  suspend fun insert(item: TodoEntity)

  @Query("SELECT count(*) FROM TodoEntity")
  suspend fun count(): Int

  @Query("SELECT * FROM TodoEntity")
  fun getAllAsFlow(): Flow<List<TodoEntity>>
}

@Entity
data class TodoEntity(
  @PrimaryKey(autoGenerate = true) val id: Long = 0,
  val title: String,
  val content: String
)

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
0

You do not need to implement actual classes.

@Suppress("KotlinNoActualForExpect")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase> {
    override fun initialize(): AppDatabase
}

You can follow the implementation steps at: https://developer.android.com/kotlin/multiplatform/room

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.