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.