Blog Post

Seamless Room database integration for Kotlin Multiplatform projects

Illustration: Seamless Room database integration for Kotlin Multiplatform projects

Room is a database library that uses SQLite under the hood. In the past, it worked with Android only, but since adding support for all platforms, it works with Kotlin Multiplatform (KMP) to store data in local databases on any platform supported by KMP.

This blog post will explore the process of integrating the Room database library into your Kotlin Multiplatform projects with ease and efficiency.

Setting up the Kotlin Multiplatform project

Navigate to the official Kotlin Multiplatform Wizard and configure your platforms as per your needs.

For this demo, select the checkboxes for Android, iOS, and Desktop.

After selecting the platforms, select DOWNLOAD to download the ZIP file and extract the files.

You can open this project in either Android Studio or IntelliJ IDEA

Adding dependencies

Room version 2.7.0-alpha01 and higher includes support for Kotlin Multiplatform. This post uses Kotlin version 2.0.0 with Android Gradle plugin 8.2.2.

To add Room to your multiplatform project, include the dependencies below in libs.version.toml:

[versions]
room = "2.7.0-alpha03"
ksp = "2.0.0-1.0.21"
sqlite = "2.5.0-SNAPSHOT"

[libraries]
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
sqlite-bundled = { module= "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" }

[plugins]
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
room = { id = "androidx.room", version.ref = "room" }

Now, you’ll add these to your build.gradle.kts file.

Append the plugins below in the plugins block and the dependencies below in the commonMain.dependencies block:

plugins {
    alias(libs.plugins.ksp)
    alias(libs.plugins.room)
   ...

commonMain.dependencies {
    implementation(libs.room.runtime)
    implementation(libs.sqlite.bundled)
	...

Add the Room database schema path, along with KSP support for the Room compiler, at the end of the build.gradle.kts file:

room {
    schemaDirectory("$projectDir/schemas")
}

dependencies {
    // KSP support for Room Compiler.
    kspCommonMainMetadata(libs.room.compiler)
}

// Solves implicit dependency issue and IDEs source code detection.
kotlin.sourceSets.commonMain { tasks.withType<KspTaskMetadata> { kotlin.srcDir(destinationDirectory) } }

You’ve now added all the required dependencies for Room to your KMP project.

Adding database classes

Next, create the Database class with DAOs and Entities inside the CommonMain package:

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

@Database(entities = [FruitEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun getFruitDao(): FruitDao
}

@Dao
interface FruitDao {
    @Upsert // Insert and update entities.
    suspend fun upsert(fruit: FruitEntity)

    @Delete
    suspend fun delete(fruit: FruitEntity)

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

@Entity
data class FruitEntity(
    @PrimaryKey(autoGenerate = true) val id: Long = 0L,
    val name: String,
    val description: String,
    val count: Int = 0
)

Next, add platform-specific code to create your database builder.

Android

// shared/src/androidMain/kotlin/Database.kt

fun getDatabaseBuilder(context: Context) = with(context) {
    val dbFile = applicationContext.getDatabasePath("room_android.db")
    Room.databaseBuilder<AppDatabase>(
        context = applicationContext,
        name = dbFile.absolutePath
        )
    }

iOS

// shared/src/iosMain/kotlin/Database.kt

fun getDatabaseBuilder() = Room.databaseBuilder<AppDatabase>(
    name = NSHomeDirectory() + "/room_ios.db",
    factory =  { AppDatabase::class.instantiateImpl() }
   )

Desktop

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

fun getDatabaseBuilder() = Room.databaseBuilder<AppDatabase>(
    name = File(System.getProperty("java.io.tmpdir"), "room_desktop.db").absolutePath
   )

Here’s the database builder you use to initialize your AppDatabase:

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

fun getRoomDatabase(builder: RoomDatabase.Builder<AppDatabase>): AppDatabase {
    return builder
        .addMigrations(MIGRATIONS)
        .fallbackToDestructiveMigrationOnDowngrade()
        .setDriver(BundledSQLiteDriver())
        .setQueryCoroutineContext(Dispatchers.IO)
        .build()
}

At last, the Room database setup is complete! Now, with the getRoomDatabase method, you can easily retrieve the database instance.

Conclusion

This post demonstrated how only a few lines of code can grant the power of databases in multiple platforms in a single codebase. With the instance of AppDatabase, it’s possible to perform all database transactions — like searching, inserting, and deleting data — regardless of which platform you’re on. Magic!

For a working example project of Room database implementation in KMP, refer to the following GitHub repository.

FAQ

Here are a few frequently asked questions about Room database integration in Kotlin Multiplatform projects.

What are the advantages of using Room in Kotlin Multiplatform projects?

Room simplifies database management with a clean API and provides compile-time checks for SQL queries, enhancing code reliability across platforms.

How does Room handle database migrations?

Room manages migrations through defined migration paths, allowing developers to upgrade the database schema seamlessly without losing existing data.

Can Room be used for both local and remote data storage?

While Room is primarily designed for local database storage using SQLite, it can be integrated with other libraries to facilitate remote data synchronization.

What is the importance of using Kotlin Multiplatform?

Kotlin Multiplatform enables code sharing across different platforms (like Android and iOS), reducing development time and maintaining consistency in codebases.

Is Room suitable for handling large datasets?

Yes, Room is efficient for managing large datasets, but performance can depend on how well the database is structured and the complexity of the queries.

Author
Akshay Sharma Senior Android Engineer

Akshay is an open source enthusiast who loves creating mobile apps and traveling to new places. He usually spends his free time with his family.

Share post
Free trial Ready to get started?
Free trial