commonMain
) module’s build.gradle.kts
, add the following dependencies:commonMain.dependencies {
// Koin core
api("io.insert-koin:koin-core:3.5.0")
// Koin for Compose
api("io.insert-koin:koin-androidx-compose:3.5.0")
api("io.insert-koin:koin-androidx-compose-viewmodel:3.5.0")
// Room
implementation(libs.androidx.room.runtime)
implementation(libs.sqlite.bundled)
// DataStore
implementation("androidx.datastore:datastore:1.1.4")
implementation("androidx.datastore:datastore-preferences:1.1.4")
}
androidMain.dependencies {
// Android-specific Koin integration
implementation("io.insert-koin:koin-android:3.5.0")
}
// AppDatabase.kt
@Database(entities = [YourEntity::class], version = 1)
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun yourDao(): YourDao
}
// The Room compiler generates the `actual` implementations.
@Suppress("NO_ACTUAL_FOR_EXPECT")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase> {
override fun initialize(): AppDatabase
}
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.fallbackToDestructiveMigrationOnDowngrade(false)
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
// YourDao.kt
@Dao
interface YourDao {
@Query("SELECT * FROM your_table")
suspend fun getAll(): List<YourEntity>
// Add other queries...
}
// AppPreferencesRepository.kt
class AppPreferencesRepository(private val dataStore: DataStore<Preferences>) {
private val someKey = booleanPreferencesKey("some_key")
val somePreference: Flow<Boolean> = dataStore.data
.map { preferences -> preferences[someKey] ?: false }
suspend fun updateSomePreference(value: Boolean) {
dataStore.edit { preferences ->
preferences[someKey] = value
}
}
}
// androidMain
fun createDatabaseBuilder(ctx: Context): RoomDatabase.Builder<AppDatabase> {
val appContext = ctx.applicationContext
val dbFile = appContext.getDatabasePath("app.db")
return Room.databaseBuilder<AppDatabase>(
context = appContext,
name = dbFile.absolutePath
)
}
fun createDataStore(context: Context): DataStore<Preferences> {
return PreferenceDataStoreFactory.create(
produceFile = { context.preferencesDataStoreFile("app.preferences_pb") }
)
}
// iosMain
import platform.Foundation.NSApplicationSupportDirectory
import platform.Foundation.NSFileManager
import platform.Foundation.NSSearchPathForDirectoriesInDomains
import platform.Foundation.NSUserDomainMask
fun createDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true)
val basePath = paths.firstOrNull() ?: error("Cannot access application support directory")
return Room.databaseBuilder<AppDatabase>(
name = "$basePath/database.db"
)
}
fun createDataStore(): DataStore<Preferences> {
return PreferenceDataStoreFactory.createWithPath(
produceFile = {
val root = PathUtils.getPrivateFileStorageDirectory(null)
"${root}/app.preferences_pb".toPath()
}
)
}
Koin.kt
in your commonMain
to define your modules:expect val platformModule: Module
fun initKoinModules(
databaseBuilder: RoomDatabase.Builder<AppDatabase>,
createDataStore: () -> DataStore<Preferences>
): Array<Module> {
val dataModule = module {
// Database
single<AppDatabase> { getRoomDatabase(databaseBuilder) }
single<YourDao> { get<AppDatabase>().yourDao() }
// DataStore
single<DataStore<Preferences>> { createDataStore() }
single<AppPreferencesRepository> { AppPreferencesRepository(get()) }
// Repositories
single<YourRepository> { YourRepository(get(), get()) }
// ViewModels
viewModel { YourViewModel(get()) }
}
return arrayOf(dataModule, platformModule)
}
androidMain
// KoinHelper.kt
actual val platformModule = module {
// Android-specific singletons/factories
}
fun initKoin(context: Context) {
val koinModules = initKoinModules(
databaseBuilder = createDatabaseBuilder(context),
createDataStore = { createDataStore(context) }
)
startKoin {
androidContext(context)
modules(*koinModules)
}
}
// YourApplication.kt
class YourApplication : Application() {
override fun onCreate() {
super.onCreate()
initKoin(this)
}
}
// KoinHelper.kt
actual val platformModule = module {
// iOS-specific singletons/factories
}
fun initKoin() {
val koinModules = initKoinModules(
databaseBuilder = createDatabaseBuilder(),
createDataStore = { createDataStore() }
)
startKoin {
modules(*koinModules)
}
}
iOSApp.swift
:@main
struct iOSApp: App {
init() {
KoinHelperKt.doInitKoin()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class YourViewModel(
private val repository: YourRepository,
private val preferences: AppPreferencesRepository
) : ViewModel() {
// Use your dependencies here
}
@Composable
fun YourScreen(viewModel: YourViewModel = koinViewModel()) {
// Use your viewModel here
}
class YourRepository(
private val dao: YourDao,
private val preferences: AppPreferencesRepository
) {
// Use your dependencies here
}
single
for shared instances (database, DataStore, repositories)viewModel
for Compose view modelsfactory
for new instances when needed