FeaturesOctober 26, 2025
Jetpack Libraries Now Support Kotlin Multiplatform: Room, DataStore & ViewModel Guide (2025)
Google's Jetpack libraries now officially support Kotlin Multiplatform. Learn how to use Room 2.8.3, DataStore 1.1.7, and ViewModel 2.9.4 in your shared code with step-by-step guides and production-ready examples.
Posted by
Related reading
Compose Multiplatform for iOS Stable in 2025: What Developers Need to Know
Compose Multiplatform for iOS is now stable with version 1.8.0. Learn how Kotlin Multiplatform's UI framework enables shared UI code across Android and iOS with native performance and 96% code reuse.
KMPShip: Kotlin Multiplatform Starter Kit for Mobile Apps
Discover how KMPShip helps you launch Android and iOS apps faster with a production-ready Kotlin Multiplatform starter built for developers.

TL;DR: Google Officially Backs Kotlin Multiplatform
Google announced at I/O 2025 that core Jetpack libraries now support Kotlin Multiplatform, marking a massive milestone for cross-platform Android development:
- Room 2.8.3: Share your database logic across Android, iOS, and Desktop (JVM)
- DataStore 1.1.7: Type-safe preferences in shared code with full coroutine support
- ViewModel 2.9.4: Lifecycle-aware ViewModels working natively on all platforms
- Production-ready: Stable releases, not experimental or alpha versions
The verdict: If you're an Android developer considering iOS expansion, Google just removed the biggest barrier to Kotlin Multiplatform adoption.
The announcement that changes everything for Android developers
In May 2025, at both Google I/O (May 20-21) and KotlinConf (May 21-23), Google made a landmark announcement: the Jetpack libraries you use every day (Room, DataStore, ViewModel, and more) now officially support Kotlin Multiplatform.
This isn't just another framework update. This is Google saying: "We're all-in on Kotlin Multiplatform."
For years, Android developers faced a painful choice when expanding to iOS: either rewrite everything in Swift or adopt a cross-platform framework like Flutter or React Native, leaving behind the Jetpack ecosystem they know and love.
Not anymore.
Why this matters
- Your Android skills now work on iOS: No need to learn Swift or completely different architectures
- Share the complex parts: Database logic, networking, business rules, and ViewModels
- Keep native UIs: Use Jetpack Compose on Android, SwiftUI on iOS (or share UI with Compose Multiplatform)
- Google's commitment: Official support means long-term maintenance, documentation, and ecosystem growth
What Jetpack libraries support Kotlin Multiplatform?
As of October 2025, here's the current state of Jetpack library support for KMP:
Stable & Production-Ready
Room Database (Version 2.8.3, Released October 22, 2025)
- Platforms: Android, iOS, JVM (Desktop)
- Full suspend function support and Flow-based reactive queries
- Official Documentation
DataStore (Version 1.1.7, Stable)
- Preferences DataStore supported in KMP
- Type-safe key-value storage with coroutines
- Official Documentation
ViewModel & Lifecycle (Version 2.9.4, Stable)
- Full lifecycle-aware components in shared code
- ViewModelFactory pattern supported
- Testing utilities included
- Official Documentation
Also Stable:
- Collections
- SavedState
- Paging
- Navigation Compose (2.9.1)
Room 2.8.3: Your database, everywhere
Room has been the gold standard for Android database management since 2017. Now it works across all major platforms with minimal code changes.
What's new in Room 2.8.3 for KMP
Platform Support:
Room now officially supports Android, iOS, and JVM (Desktop), allowing you to share database logic across mobile and desktop platforms.
Kotlin-First Architecture:
Room's runtime has been completely converted from Java to Kotlin, with Kotlin 2.0 as the minimum requirement. Code generation now produces Kotlin by default (not Java), resulting in better type safety and more idiomatic code.
Modern Tooling:
- KSP2 support: Faster compilation and better error messages
- BundledSQLiteDriver: Consistent SQLite behavior across all platforms
- Coroutine-native: Suspend functions and Flow are first-class citizens
Setting up Room for Kotlin Multiplatform
Step 1: Add dependencies
In your
shared/build.gradle.kts file:kotlinkotlin { sourceSets { commonMain.dependencies { implementation("androidx.room:room-runtime:2.8.3") implementation("androidx.sqlite:sqlite-bundled:2.5.0-alpha11") } } } dependencies { add("kspCommonMainMetadata", "androidx.room:room-compiler:2.8.3") }
Step 2: Define your database in commonMain
kotlin// shared/src/commonMain/kotlin/com/example/app/database/AppDatabase.kt @Database(entities = [Task::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun taskDao(): TaskDao } @Entity(tableName = "tasks") data class Task( @PrimaryKey(autoGenerate = true) val id: Long = 0, val title: String, val isCompleted: Boolean = false, val createdAt: Long = System.currentTimeMillis() ) @Dao interface TaskDao { @Query("SELECT * FROM tasks ORDER BY createdAt DESC") fun getAllTasks(): Flow<List<Task>> @Insert suspend fun insertTask(task: Task) @Update suspend fun updateTask(task: Task) @Delete suspend fun deleteTask(task: Task) @Query("SELECT * FROM tasks WHERE isCompleted = :completed") fun getTasksByStatus(completed: Boolean): Flow<List<Task>> }
Step 3: Create the database builder
Use the expect/actual pattern for platform-specific initialization:
kotlin// shared/src/commonMain/kotlin/com/example/app/database/DatabaseBuilder.kt expect class DatabaseBuilder { fun build(): AppDatabase } fun getDatabaseBuilder(): DatabaseBuilder = DatabaseBuilder()
kotlin// shared/src/androidMain/kotlin/com/example/app/database/DatabaseBuilder.android.kt import android.content.Context import androidx.room.Room import androidx.sqlite.driver.bundled.BundledSQLiteDriver actual class DatabaseBuilder(private val context: Context) { actual fun build(): AppDatabase { val dbFile = context.getDatabasePath("app_database.db") return Room.databaseBuilder<AppDatabase>( context = context, name = dbFile.absolutePath ) .setDriver(BundledSQLiteDriver()) .build() } }
kotlin// shared/src/iosMain/kotlin/com/example/app/database/DatabaseBuilder.ios.kt import androidx.room.Room import androidx.sqlite.driver.bundled.BundledSQLiteDriver import platform.Foundation.NSDocumentDirectory import platform.Foundation.NSFileManager import platform.Foundation.NSUserDomainMask actual class DatabaseBuilder { actual fun build(): AppDatabase { val documentDirectory = NSFileManager.defaultManager.URLForDirectory( directory = NSDocumentDirectory, inDomain = NSUserDomainMask, appropriateForURL = null, create = false, error = null ) val dbFile = requireNotNull(documentDirectory?.path) + "/app_database.db" return Room.databaseBuilder<AppDatabase>( name = dbFile ) .setDriver(BundledSQLiteDriver()) .build() } }
Migration from Android-only Room
If you have an existing Android app with Room, here's the migration path:
- Move entity and DAO definitions to
commonMain(they're pure Kotlin, no changes needed) - Update queries to use suspend functions and Flow (if not already)
- Create platform-specific database builders using the expect/actual pattern shown above
- Test on both platforms to ensure schema consistency
Key Gotcha: Schema export for iOS requires additional KSP configuration. Consult the official migration guide for details.
DataStore 1.1.7: Type-safe preferences in shared code
DataStore is Android's modern replacement for SharedPreferences, offering type-safe, asynchronous data storage with coroutines. Now you can use it across all platforms.
Why use DataStore in Kotlin Multiplatform?
Compared to platform-specific solutions:
- Type safety: Strong typing prevents runtime errors
- Coroutine-native: Async operations without callbacks
- Crash-safe: Atomic write operations with transactions
- Observable: React to changes with Flow
- Consistent API: Same code on all platforms
Setting up DataStore for KMP
Step 1: Add dependencies
kotlinkotlin { sourceSets { commonMain.dependencies { implementation("androidx.datastore:datastore:1.1.7") implementation("androidx.datastore:datastore-preferences:1.1.7") } } }
Step 2: Create the DataStore factory
DataStore needs platform-specific file paths. Use the expect/actual pattern:
kotlin// shared/src/commonMain/kotlin/com/example/app/data/DataStoreFactory.kt import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences expect fun createDataStore(): DataStore<Preferences>
kotlin// shared/src/androidMain/kotlin/com/example/app/data/DataStoreFactory.android.kt import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.preferencesDataStore private val Context.dataStore: DataStore<Preferences> by preferencesDataStore( name = "app_preferences" ) actual fun createDataStore(): DataStore<Preferences> { // Context must be passed from Android application return appContext.dataStore } // Helper to set context from Android app private lateinit var appContext: Context fun initDataStore(context: Context) { appContext = context.applicationContext }
kotlin// shared/src/iosMain/kotlin/com/example/app/data/DataStoreFactory.ios.kt import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.PreferenceDataStoreFactory import kotlinx.cinterop.ExperimentalForeignApi import okio.Path.Companion.toPath import platform.Foundation.NSDocumentDirectory import platform.Foundation.NSFileManager import platform.Foundation.NSUserDomainMask @OptIn(ExperimentalForeignApi::class) actual fun createDataStore(): DataStore<Preferences> { val documentDirectory = NSFileManager.defaultManager.URLForDirectory( directory = NSDocumentDirectory, inDomain = NSUserDomainMask, appropriateForURL = null, create = false, error = null ) val path = requireNotNull(documentDirectory?.path) + "/app_preferences.preferences_pb" return PreferenceDataStoreFactory.createWithPath( produceFile = { path.toPath() } ) }
Step 3: Create a preferences repository
kotlin// shared/src/commonMain/kotlin/com/example/app/data/PreferencesRepository.kt import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map class PreferencesRepository(private val dataStore: DataStore<Preferences>) { private companion object { val THEME_MODE_KEY = stringPreferencesKey("theme_mode") val NOTIFICATIONS_ENABLED_KEY = booleanPreferencesKey("notifications_enabled") } val themeMode: Flow<String?> = dataStore.data.map { preferences -> preferences[THEME_MODE_KEY] } val notificationsEnabled: Flow<Boolean> = dataStore.data.map { preferences -> preferences[NOTIFICATIONS_ENABLED_KEY] ?: true } suspend fun setThemeMode(mode: String) { dataStore.edit { preferences -> preferences[THEME_MODE_KEY] = mode } } suspend fun setNotificationsEnabled(enabled: Boolean) { dataStore.edit { preferences -> preferences[NOTIFICATIONS_ENABLED_KEY] = enabled } } }
Now you have type-safe preferences accessible from all platforms with the exact same API!
ViewModel 2.9.4: Lifecycle-aware logic everywhere
ViewModel has been the cornerstone of Android architecture since Architecture Components launched. Now you can use ViewModels in shared code and consume them from both Jetpack Compose and SwiftUI.
Setting up ViewModel for KMP
Step 1: Add dependencies
kotlinkotlin { sourceSets { commonMain.dependencies { api("androidx.lifecycle:lifecycle-viewmodel:2.9.4") } } }
Note: Use
api instead of implementation for better Swift interoperability.Step 2: Create ViewModels in commonMain
kotlin// shared/src/commonMain/kotlin/com/example/app/presentation/TaskListViewModel.kt import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch class TaskListViewModel( private val taskDao: TaskDao ) : ViewModel() { private val _uiState = MutableStateFlow<TaskListUiState>(TaskListUiState.Loading) val uiState: StateFlow<TaskListUiState> = _uiState.asStateFlow() init { loadTasks() } private fun loadTasks() { viewModelScope.launch { taskDao.getAllTasks().collect { tasks -> _uiState.value = TaskListUiState.Success(tasks) } } } fun addTask(title: String) { viewModelScope.launch { taskDao.insertTask(Task(title = title)) } } fun toggleTask(task: Task) { viewModelScope.launch { taskDao.updateTask(task.copy(isCompleted = !task.isCompleted)) } } fun deleteTask(task: Task) { viewModelScope.launch { taskDao.deleteTask(task) } } } sealed class TaskListUiState { object Loading : TaskListUiState() data class Success(val tasks: List<Task>) : TaskListUiState() data class Error(val message: String) : TaskListUiState() }
Step 3: Create ViewModelFactory
kotlin// shared/src/commonMain/kotlin/com/example/app/di/ViewModelFactory.kt import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory val taskListViewModelFactory = viewModelFactory { initializer { TaskListViewModel( taskDao = getDatabaseBuilder().build().taskDao() ) } }
Step 4: Use in Compose Multiplatform (Android/iOS)
kotlin// shared/src/commonMain/kotlin/com/example/app/ui/TaskListScreen.kt @Composable fun TaskListScreen(viewModel: TaskListViewModel = viewModel(factory = taskListViewModelFactory)) { val uiState by viewModel.uiState.collectAsState() when (val state = uiState) { is TaskListUiState.Loading -> { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } is TaskListUiState.Success -> { TaskList( tasks = state.tasks, onTaskClick = { viewModel.toggleTask(it) }, onDeleteTask = { viewModel.deleteTask(it) } ) } is TaskListUiState.Error -> { ErrorScreen(message = state.message) } } }
Step 5: Use in SwiftUI (iOS)
For SwiftUI integration, you'll need a helper library like SKIE or KMP-NativeCoroutines to properly observe Kotlin Flows from Swift:
swift// iosApp/TaskListView.swift import SwiftUI import shared struct TaskListView: View { @StateObject private var viewModel = TaskListViewModel( taskDao: DatabaseBuilder().build().taskDao() ) var body: some View { // SwiftUI implementation } }
Real-world migration: Android app to KMP
Let's walk through a practical example of migrating an existing Android app to use shared Jetpack libraries.
Before: Android-only architecture
kotlin// Android app/src/main/kotlin/com/example/app/data/TaskRepository.kt class TaskRepository(private val taskDao: TaskDao) { fun getAllTasks(): Flow<List<Task>> = taskDao.getAllTasks() suspend fun addTask(task: Task) = taskDao.insertTask(task) } // Android app/src/main/kotlin/com/example/app/ui/TaskViewModel.kt class TaskViewModel(private val repository: TaskRepository) : ViewModel() { val tasks: StateFlow<List<Task>> = repository.getAllTasks() .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) }
After: Shared KMP code
Step 1: Move Room entities and DAOs to
shared/src/commonMainStep 2: Move repository to shared code
kotlin// shared/src/commonMain/kotlin/com/example/app/data/TaskRepository.kt class TaskRepository(private val taskDao: TaskDao) { fun getAllTasks(): Flow<List<Task>> = taskDao.getAllTasks() suspend fun addTask(task: Task) = taskDao.insertTask(task) }
Step 3: Move ViewModel to shared code
kotlin// shared/src/commonMain/kotlin/com/example/app/presentation/TaskViewModel.kt class TaskViewModel(private val repository: TaskRepository) : ViewModel() { val tasks: StateFlow<List<Task>> = repository.getAllTasks() .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) }
Step 4: Update Android app to use shared module
kotlin// Android app/src/main/kotlin/com/example/app/MainActivity.kt class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Initialize shared module with Android context initDataStore(this) setContent { val viewModel: TaskViewModel = viewModel( factory = taskViewModelFactory ) TaskListScreen(viewModel) } } }
Step 5: Add iOS target
Now your business logic, database, and ViewModels work on iOS with zero additional code!
What you've achieved
✅ 90-95% code sharing between Android and iOS
✅ Single source of truth for business logic
✅ Consistent behavior across platforms
✅ Reduced maintenance burden: fix bugs once
✅ Faster feature development: build once, deploy everywhere
Getting started with KMPShip: Skip the setup complexity
While setting up Jetpack libraries for KMP is now officially supported, it still requires significant configuration work:
- Setting up KSP and room compiler correctly
- Configuring platform-specific database and DataStore paths
- Setting up dependency injection
- Configuring CI/CD for both platforms
- Adding authentication, payments, and other production features
KMPShip eliminates weeks of setup work by providing a production-ready Kotlin Multiplatform boilerplate with all Jetpack libraries pre-configured and working out of the box.
KMPShip includes everything you need:
- ✅ Production-ready KMP architecture
- ✅ Dependency injection setup (Koin)
- ✅ Complete sample app with best practices
- ✅ Authentication (Google, Apple, Email)
- ✅ In-app purchases and subscriptions
- ✅ Push notifications
- ✅ CI/CD pipelines
- ✅ Complete sample app
From idea to production-ready app in 30 minutes, not weeks.
The bottom line for 2025
Google's official support for Jetpack libraries in Kotlin Multiplatform removes the biggest barrier to KMP adoption, which is ecosystem maturity.
You no longer need to choose between the Android tools you know and cross-platform reach. You can have both.
For Android developers, this is your path to iOS development without abandoning your expertise.
For startups and indie developers, this means faster time to market with less code to maintain.
For enterprises, this means lower development costs and consistent business logic across platforms.
The technology is stable. The tooling is ready. The ecosystem is growing. The best time to start with Kotlin Multiplatform is right now.
Sources and references
- Android's Kotlin Multiplatform announcements at Google I/O and KotlinConf 25 (Android Developers Blog)
- Set up Room Database for KMP (Android Developers)
- Room 2.8.3 Release Notes (Android Developers)
- Set up DataStore for KMP (Android Developers)
- Set up ViewModel for KMP (Android Developers)
- Add Kotlin Multiplatform to an existing project (Android Developers)
- What's Next for Kotlin Multiplatform, August 2025 Update (JetBrains Kotlin Blog)
Continue Your KMP Journey
Expand your Kotlin Multiplatform knowledge with these related guides:
- KMP vs Flutter vs React Native comparison: Choose the right framework
- How to Set Up Kotlin Multiplatform: Complete setup guide
- Compose Multiplatform for iOS Stable: Share UI code too
Start Building with Jetpack Libraries Today
Stop reinventing the wheel. Your Android expertise is now your iOS advantage. The Jetpack libraries you know and trust work everywhere Kotlin runs.
The cross-platform mobile development landscape has fundamentally changed. Google's commitment to Kotlin Multiplatform means the ecosystem will only grow stronger. The question isn't whether to adopt KMP, it's whether you'll lead or follow.