The Room persistence library provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite. This page focuses on using Room in Kotlin Multiplatform (KMP) projects. For more information on using Room, see Save data in a local database using Room or our official samples.
Setting up dependencies
The current version of Room that supports KMP is 2.7.0-alpha01 or higher.
To setup Room in your KMP project, add the dependencies for the artifacts in the
build.gradle.kts
file for your module:
androidx.room:room-gradle-plugin
- The Gradle Plugin to configure Room schemasandroidx.room:room-compiler
- The KSP processor that generates codeandroidx.room:room-runtime
- The runtime part of the libraryandroidx.sqlite:sqlite-bundled
- (Optional) The bundled SQLite library
Additionally you need to configure Room's SQLite driver. These drivers differ based on the target platform. See Driver implementations for descriptions of available driver implementations.
For additional setup information, see the following:
- Set schema location using Room Gradle Plugin.
- KSP with Kotlin Multiplatform.
- Adding runtime dependencies.
Defining the database classes
You need to create a database class annotated with @Database
along with DAOs
and entities inside the common source set of your shared KMP module. Placing
these classes in common sources will allow them to be shared across all target
platforms.
// shared/src/commonMain/kotlin/Database.kt
@Database(entities = [TodoEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getDao(): TodoDao
}
@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
)
Note that you can use actual / expect declarations to create
platform-specific Room implementations. For example, you can add a
platform-specific DAO that is defined in common code using expect
and then
specify the actual
definitions with additional queries in platform-specific
source sets.
Creating the database builder
You need to define a database builder to instantiate Room on each platform. This
is the only part of the API that is required to be in platform-specific source
sets due to the differences in file system APIs. For example, in Android,
database location is usually obtained through the
Context.getDatabasePath()
API, while for iOS, the database location is
obtained using NSHomeDirectory
.
Android
To create the database instance, specify a Context along with the database path. You don't need to specify a database factory.
// shared/src/androidMain/kotlin/Database.kt
fun getDatabaseBuilder(ctx: Context): RoomDatabase.Builder<AppDatabase> {
val appContext = ctx.applicationContext
val dbFile = appContext.getDatabasePath("my_room.db")
return Room.databaseBuilder<AppDatabase>(
context = appContext,
name = dbFile.absolutePath
)
}
iOS
To create the database instance, provide a database factory along with the
database path. The database factory is a lambda function that invokes a
generated extension function whose name is instantiateImpl
with a receiver of
type KClass<T>
, where T
is the type of the @Database
annotated class.
// shared/src/iosMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFilePath = NSHomeDirectory() + "/my_room.db"
return Room.databaseBuilder<AppDatabase>(
name = dbFilePath,
factory = { AppDatabase::class.instantiateImpl() }
)
}
JVM (Desktop)
To create the database instance, specify only the database path. You don't need to provide a database factory.
// shared/src/commonMain/kotlin/Database.kt
fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFile = File(System.getProperty("java.io.tmpdir"), "my_room.db")
return Room.databaseBuilder<AppDatabase>(
name = dbFile.absolutePath,
)
}
Database instantiation
Once you obtain the RoomDatabase.Builder
from one of the platform-specific
constructors, you can configure the rest of the Room database in common code
along with the actual database instantiation.
// shared/src/commonMain/kotlin/Database.kt
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.addMigrations(MIGRATIONS)
.fallbackToDestructiveMigrationOnDowngrade()
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
Selecting a SQLiteDriver
The previous code snippets use the BundledSQLiteDriver
. This is the
recommended driver that includes SQLite compiled from source, which provides the
most consistent and up-to-date version of SQLite across all platforms. If you
wish to use the OS-provided SQLite, use the setDriver
API in platform-specific
source sets that specify a platform-specific driver. For Android, you can use
AndroidSQLiteDriver
, while for iOS you can use the NativeSQLiteDriver
. To
use NativeSQLiteDriver
, you need to provide a linker option so that the iOS
app dynamically links with the system SQLite.
// shared/build.gradle.kts
kotlin {
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "TodoApp"
isStatic = true
// Required when using NativeSQLiteDriver
linkerOpts.add("-lsqlite3")
}
}
}
Differences
Room was originally developed as an Android library and was later migrated to KMP with a focus on API compatibility. The KMP version of Room differs somewhat between platforms and from the Android-specific version. These differences are listed and described as follows.
Blocking DAO functions
When using Room for KMP, all DAO functions compiled for non-Android platforms
need to be suspend
functions with the exception of reactive return types, such
as Flow
.
// shared/src/commonMain/kotlin/MultiplatformDao.kt
@Dao
interface MultiplatformDao {
// ERROR: Blocking function not valid for non-Android targets
@Query("SELECT * FROM Entity")
fun blockingQuery(): List<Entity>
// OK
@Query("SELECT * FROM Entity")
suspend fun query(): List<Entity>
// OK
@Query("SELECT * FROM Entity")
fun queryFlow(): Flow<List<Entity>>
// ERROR: Blocking function not valid for non-Android targets
@Transaction
fun blockingTransaction() { // … }
// OK
@Transaction
suspend fun transaction() { // … }
}
Room benefits from the feature-rich asynchronous kotlinx.coroutines
library
that Kotlin offers for multiple platforms. For optimal functionality, suspend
functions are enforced for DAOs compiled in a KMP project, with the exception of
Android specific DAOs to maintain backwards compatibility with the existing
codebase.
Feature differences with KMP
This section describes how features differ between KMP and Android platform versions of Room.
@RawQuery DAO functions
Functions annotated with @RawQuery
that are compiled for non-Android platforms
will produce an error. We intend to add support for @RawQuery
in a future
version of Room.
Query Callback
The following APIs for configuring query callbacks are not available in common and are thus unavailable in platforms other than Android.
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
We intend to add support for query callback in a future version of Room.
The API to configure a RoomDatabase
with a query callback
RoomDatabase.Builder.setQueryCallback
along with the callback interface
RoomDatabase.QueryCallback
are not available in common and thus not available
in other platforms other than Android.
Auto Closing Database
The API to enable auto-closing after a timeout,
RoomDatabase.Builder.setAutoCloseTimeout
, is only available on Android and is
not available in other platforms.
Pre-package Database
The following APIs to create a RoomDatabase
using an existing database (i.e. a
pre-packaged database) are not available in common and are thus not available in
other platforms other than Android. These APIs are:
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
We intend to add support for pre-packaged databases in a future version of Room.
Multi-Instance Invalidation
The API to enable multi-instance invalidation,
RoomDatabase.Builder.enableMultiInstanceInvalidation
is only available on
Android and is not available in common or other platforms.