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.

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 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
DEVELOPMENT  |  iOS • Insights • Xcode

NSCopying in a Swift World

TUTORIALS  |  iOS • How To • PDF • Swift • Signing

How to Sign a PDF on iOS with PSPDFKit's Signature Library

DEVELOPMENT  |  iOS • Swift • Insights

Generating API Documentation for Multiple Targets with DocC