Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion IMPLEMENTATION_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
- Project deletion is handled safely (nullifies habit references before deletion)
- All filtering is optional (null projectId shows all habits)
- Color parsing is cross-platform compatible (no android.graphics.Color dependency)
- Follows existing codebase patterns: BaseViewModel, sealed events/actions, DI via Kodein
- Follows existing codebase patterns: BaseViewModel, sealed events/actions, DI via Koin

## Files Created (16)
- core/database/migrations/Migration7to8.kt
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Adopted to full Compose Multiplatform and Kotlin Multiplatform
- Presentation: KViewModel
- Database: Room
- Resources: LibRes
- DI: Kodein
- DI: Koin
- UI: Compose Multiplatform

### Supported Platforms
Expand Down
5 changes: 4 additions & 1 deletion composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ kotlin {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.datetime)

implementation(libs.kodein.di)
implementation(libs.koin.core)
implementation(libs.koin.compose)
implementation(libs.koin.compose.viewmodel)

implementation(libs.uuid)

Expand All @@ -103,6 +105,7 @@ kotlin {
androidMain.dependencies {
implementation(libs.androidx.appcompat)
implementation(libs.androidx.activity.compose)
implementation(libs.koin.android)
}

jvmMain.dependencies {
Expand Down
9 changes: 6 additions & 3 deletions composeApp/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}

# Keep Kodein
-keep class org.kodein.** { *; }
-keep @org.kodein.di.DI$Tag class *
# Keep Koin
-keep class org.koin.** { *; }
-keep class org.koin.core.** { *; }
-keepclassmembers class * {
public <init>(...);
}

# General Android rules
-keepclassmembers class * implements android.os.Parcelable {
Expand Down
12 changes: 5 additions & 7 deletions composeApp/src/androidMain/kotlin/di/Providers.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package di

import core.platform.ImagePicker
import org.kodein.di.DI
import org.kodein.di.bind
import org.kodein.di.instance
import org.kodein.di.singleton
import org.koin.core.module.Module
import org.koin.dsl.module

actual fun DI.Builder.provideImagePicker() {
bind<ImagePicker>() with singleton {
instance<PlatformConfiguration>().imagePicker
actual fun Module.provideImagePicker() {
single<ImagePicker> {
get<PlatformConfiguration>().imagePicker
}
}
6 changes: 3 additions & 3 deletions composeApp/src/commonMain/kotlin/di/CoreModule.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package di

import org.kodein.di.DI
import org.koin.dsl.module

val coreModule = DI.Module("coreModule") {
importAll(
val coreModule = module {
includes(
serializationModule
)
}
31 changes: 14 additions & 17 deletions composeApp/src/commonMain/kotlin/di/DatabaseModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,30 @@ import feature.daily.data.DailyDao
import feature.habits.data.HabitDao
import feature.projects.data.ProjectDao
import feature.tracker.data.TrackerDao
import org.kodein.di.DI
import org.kodein.di.bind
import org.kodein.di.instance
import org.kodein.di.singleton
import org.koin.dsl.module

fun databaseModule() = DI.Module("database") {
bind<AppDatabase>() with singleton {
instance<Any>("appDatabase") as AppDatabase
val databaseModule = module {
single<AppDatabase> {
get(qualifier = org.koin.core.qualifier.named("appDatabase"))
}

bind<HabitDao>() with singleton {
instance<AppDatabase>().getHabitDao()
single<HabitDao> {
get<AppDatabase>().getHabitDao()
}

bind<TrackerDao>() with singleton {
instance<AppDatabase>().getTrackerDao()
single<TrackerDao> {
get<AppDatabase>().getTrackerDao()
}

bind<DailyDao>() with singleton {
instance<AppDatabase>().getDailyDao()
single<DailyDao> {
get<AppDatabase>().getDailyDao()
}

bind<UserProfileDao>() with singleton {
instance<AppDatabase>().getUserProfileDao()
single<UserProfileDao> {
get<AppDatabase>().getUserProfileDao()
}

bind<ProjectDao>() with singleton {
instance<AppDatabase>().getProjectDao()
single<ProjectDao> {
get<AppDatabase>().getProjectDao()
}
}
39 changes: 18 additions & 21 deletions composeApp/src/commonMain/kotlin/di/FeatureModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,42 @@ import feature.habits.domain.CreateHabitUseCase
import feature.projects.di.projectModule
import feature.settings.domain.ClearAllHabitsUseCase
import feature.tracker.domain.UpdateTrackerValueUseCase
import org.kodein.di.DI
import org.kodein.di.bind
import org.kodein.di.instance
import org.kodein.di.singleton
import org.koin.dsl.module

fun featureModule() = DI.Module("feature") {
importAll(
val featureModule = module {
includes(
detailModule,
projectModule
)

// Use Cases
bind<GetHabitsForTodayUseCase>() with singleton {
single<GetHabitsForTodayUseCase> {
GetHabitsForTodayUseCase(
habitDao = instance(),
trackerDao = instance(),
dailyDao = instance()
habitDao = get(),
trackerDao = get(),
dailyDao = get()
)
}
bind<SwitchHabitUseCase>() with singleton {
single<SwitchHabitUseCase> {
SwitchHabitUseCase(
habitDao = instance(),
dailyDao = instance()
habitDao = get(),
dailyDao = get()
)
}
bind<CreateHabitUseCase>() with singleton {
single<CreateHabitUseCase> {
CreateHabitUseCase(
habitDao = instance()
habitDao = get()
)
}
bind<UpdateTrackerValueUseCase>() with singleton {
single<UpdateTrackerValueUseCase> {
UpdateTrackerValueUseCase(
trackerDao = instance()
trackerDao = get()
)
}
bind<ClearAllHabitsUseCase>() with singleton {
single<ClearAllHabitsUseCase> {
ClearAllHabitsUseCase(
habitDao = instance(),
dailyDao = instance()
habitDao = get(),
dailyDao = get()
)
}
}
43 changes: 24 additions & 19 deletions composeApp/src/commonMain/kotlin/di/PlatformSDK.kt
Original file line number Diff line number Diff line change
@@ -1,43 +1,48 @@
package di

import org.kodein.di.DI
import org.kodein.di.DirectDI
import org.kodein.di.bind
import org.kodein.di.direct
import org.kodein.di.instance
import org.kodein.di.singleton
import org.koin.core.Koin
import org.koin.core.context.GlobalContext
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.core.qualifier.named
import org.koin.dsl.module

object PlatformSDK {
private var _di: DirectDI? = null
val di: DirectDI
get() = requireNotNull(_di)
private var _koin: Koin? = null
val koin: Koin
get() = requireNotNull(_koin)

fun init(
configuration: PlatformConfiguration,
appDatabase: Any? = null
) {
val configModule = DI.Module("config") {
bind<PlatformConfiguration>() with singleton { configuration }
// Stop any existing Koin instance to allow reinitialization
if (GlobalContext.getOrNull() != null) {
stopKoin()
}

val configModule = module {
single<PlatformConfiguration> { configuration }
if (appDatabase != null) {
bind<Any>("appDatabase") with singleton { appDatabase }
single(qualifier = named("appDatabase")) { appDatabase }
}
}

val platformModule = DI.Module("platform") {
val platformModule = module {
provideImagePicker()
}

_di = DI {
importAll(
_koin = startKoin {
modules(
configModule,
platformModule,
databaseModule(),
featureModule()
databaseModule,
featureModule
)
}.direct
}.koin
}

inline fun <reified T> instance(): T {
return di.instance()
return koin.get()
}
}
4 changes: 2 additions & 2 deletions composeApp/src/commonMain/kotlin/di/Providers.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package di

import org.kodein.di.DI
import org.koin.core.module.Module

expect fun DI.Builder.provideImagePicker()
expect fun Module.provideImagePicker()
8 changes: 3 additions & 5 deletions composeApp/src/commonMain/kotlin/di/SerializationModule.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package di

import kotlinx.serialization.json.Json
import org.kodein.di.DI
import org.kodein.di.bind
import org.kodein.di.singleton
import org.koin.dsl.module

val serializationModule = DI.Module("serializationModule") {
bind<Json>() with singleton {
val serializationModule = module {
single<Json> {
Json {
isLenient = true
ignoreUnknownKeys = true
Expand Down
26 changes: 11 additions & 15 deletions composeApp/src/commonMain/kotlin/feature/daily/di/DailyModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,26 @@ package feature.daily.di

import core.database.AppDatabase
import data.features.daily.DailyRepository
import di.Inject.instance
import feature.daily.data.DailyDao
import feature.daily.domain.GetHabitsForTodayUseCase
import feature.daily.domain.SwitchHabitUseCase
import org.kodein.di.DI
import org.kodein.di.bind
import org.kodein.di.provider
import org.kodein.di.singleton
import org.koin.dsl.module

val dailyModule = DI.Module("DailyModule") {
bind<DailyDao>() with singleton {
val appDatabase = instance<AppDatabase>()
val dailyModule = module {
single<DailyDao> {
val appDatabase = get<AppDatabase>()
appDatabase.getDailyDao()
}
bind<GetHabitsForTodayUseCase>() with provider {
GetHabitsForTodayUseCase(instance(), instance(), instance())

factory<GetHabitsForTodayUseCase> {
GetHabitsForTodayUseCase(get(), get(), get())
}
bind<SwitchHabitUseCase>() with provider {
SwitchHabitUseCase(instance(), instance())

factory<SwitchHabitUseCase> {
SwitchHabitUseCase(get(), get())
}

bind<DailyRepository>() with provider {
factory<DailyRepository> {
DailyRepository()
}
}
20 changes: 10 additions & 10 deletions composeApp/src/commonMain/kotlin/feature/detail/di/DetailModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ package feature.detail.di
import feature.detail.domain.DeleteHabitUseCase
import feature.detail.domain.GetDetailInfoUseCase
import feature.detail.domain.UpdateHabitUseCase
import org.kodein.di.*
import org.koin.dsl.module

val detailModule = DI.Module("detailModule") {
bind<GetDetailInfoUseCase>() with provider {
GetDetailInfoUseCase(instance())
val detailModule = module {
factory<GetDetailInfoUseCase> {
GetDetailInfoUseCase(get())
}
bind<DeleteHabitUseCase>() with provider {
DeleteHabitUseCase(instance())

factory<DeleteHabitUseCase> {
DeleteHabitUseCase(get())
}
bind<UpdateHabitUseCase>() with provider {
UpdateHabitUseCase(instance())

factory<UpdateHabitUseCase> {
UpdateHabitUseCase(get())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ package feature.habits.di
import core.database.AppDatabase
import feature.habits.data.HabitDao
import feature.habits.domain.CreateHabitUseCase
import org.kodein.di.*
import org.koin.dsl.module

val habitModule = DI.Module("HabitModule") {
bind<HabitDao>() with singleton {
val appDatabase = instance<AppDatabase>()
val habitModule = module {
single<HabitDao> {
val appDatabase = get<AppDatabase>()
appDatabase.getHabitDao()
}
bind<CreateHabitUseCase>() with provider {
CreateHabitUseCase(instance())

factory<CreateHabitUseCase> {
CreateHabitUseCase(get())
}
}
Loading
Loading