kosi-libs / Kodein

Painless Kotlin Dependency Injection

Home Page:https://kosi-libs.org/kodein

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

App freezing when using iOS

alfietapping opened this issue · comments

dependency - org.kodein.di:kodein-di-framework-compose:7:20:1

Im injecting a ScreenModel from the Voyager library and all is working well when i run the app on Android, but if i try to run it on iOS i get a vague error from xcode which i suspect is around memory management and the app freezes

Exception	NSTaggedPointerString *	0x8000000105b9ffd5
self	@thin iosApp.iOSApp.Type	iosApp.iOSApp	

Heres my screen model

class HomeScreenModel(
    private val savedTeamRepository: SavedTeamRepository
) : CoreScreenModel<HomeState, HomeEvent, HomeEffect>(HomeState())

Im then using realm

class SavedTeamRepositoryImpl(
    private val realm: Realm
) : SavedTeamRepository {

    override suspend fun insert(savedTeam: SavedTeam) {
        val dbTeam = savedTeam.toDbModel()
        realm.write {
            copyToRealm(dbTeam)
        }
    }

    override suspend fun observeAll(): Flow<List<SavedTeam>> = flow {

        val savedTeamsFlow = realm.query<DbSavedTeam>().asFlow()

        savedTeamsFlow.collect { changes: ResultsChange<DbSavedTeam> ->

            emit(changes.list.map { it.toEntity() })
        }
    }
}

Providing deps

val dataModule = DI.Module(name = "dataModule") {
    bindSingleton { RealmConfiguration.create(schema = provideRealmSchemaObjects()) }
    bindSingleton { Realm.open(instance()) }
    bindProvider<SavedTeamRepository> { SavedTeamRepositoryImpl(instance()) }
}

val homeModule = DI.Module(name = "homeModule") {
    bindProvider {
        HomeScreenModel(instance())
    }
}

private fun provideRealmSchemaObjects() = setOf(
    DbSavedTeam::class,
    DbPlayer::class,
    DbOffset::class
)

Then eventually in my App composable

@Composable
fun App() = withDI(homeModule, dataModule) {

    Navigator(HomeContent) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .background(Color.White)
        ) {
            AppContent(
                modifier = Modifier
                    .weight(1f)
                    .fillMaxWidth()
            ) {
                CurrentScreen()
            }

            BottomNavBar(modifier = Modifier)
        }
    }
}

Also just to add this other error on xcode

Thread 1: signal SIGABRT

Having similar error when trying to start similar @Composable fun App() = withDI {...} from XCode. @romainbsl do you plan to support Compose Multiplatform for iOS?

(Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)

Stacktrace excerpt

#0	0x00000001033dc8b8 in kfun:org.kodein.di.DI.Module.<get-_name>#internal at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/DI.kt:592
#1	0x00000001033dc9f4 in kfun:org.kodein.di.DI.Module#<get-name>(){}kotlin.String at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/DI.kt:593
#2	0x00000001033f12c0 in kfun:org.kodein.di.internal.DIBuilderImpl#import(org.kodein.di.DI.Module;kotlin.Boolean){} at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIBuilderImpl.kt:278
#3	0x000000010340d230 in kfun:org.kodein.di.internal.DIBuilderImpl#import(org.kodein.di.DI.Module;kotlin.Boolean){}-trampoline at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIBuilderImpl.kt:277
#4	0x00000001033f1924 in <inlined-out:<anonymous>> [inlined] at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIBuilderImpl.kt:295
#5	0x00000001033f1914 in kfun:kotlin.collections#forEach__at__kotlin.Array<out|0:0>(kotlin.Function1<0:0,kotlin.Unit>){0§<kotlin.Any?>} [inlined] at /opt/buildAgent/work/2fed3917837e7e79/kotlin/libraries/stdlib/common/src/generated/_Arrays.kt:13309
#6	0x00000001033f1914 in kfun:org.kodein.di.internal.DIBuilderImpl#importAll(kotlin.Array<out|org.kodein.di.DI.Module>...;kotlin.Boolean){} at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIBuilderImpl.kt:295
#7	0x0000000103407984 in kfun:org.kodein.di.DI.Builder#importAll(kotlin.Array<out|org.kodein.di.DI.Module>...;kotlin.Boolean){}-trampoline at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/DI.kt:482
#8	0x00000001033dc660 in kfun:org.kodein.di.DI.Builder#importAll$default(kotlin.Array<out|org.kodein.di.DI.Module>...;kotlin.Boolean;kotlin.Int){} at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/DI.kt:482
#9	0x00000001021f14f8 in kfun:di.di$lambda$0#internal at /Users/user/Documents/xcode/mingli_ios/projects/Mingli Font Viewer/composeApp/src/commonMain/kotlin/di/Kodein.kt:20
#10	0x00000001021f2120 in kfun:di.$di$lambda$0$FUNCTION_REFERENCE$0.invoke#internal at /Users/user/Documents/xcode/mingli_ios/projects/Mingli Font Viewer/composeApp/src/commonMain/kotlin/di/Kodein.kt:19
#11	0x00000001021f2198 in kfun:di.$di$lambda$0$FUNCTION_REFERENCE$0.$<bridge-UNNN>invoke(org.kodein.di.DI.MainBuilder){}#internal at /Users/user/Documents/xcode/mingli_ios/projects/Mingli Font Viewer/composeApp/src/commonMain/kotlin/di/Kodein.kt:19
#12	0x00000001024ddfdc in kfun:kotlin.Function1#invoke(1:0){}1:1-trampoline at /Users/admin/.gradle/daemon/8.2.1/[K][Suspend]Functions:1
#13	0x00000001033fb0f4 in kfun:kotlin#apply__at__0:0(kotlin.Function1<0:0,kotlin.Unit>){0§<kotlin.Any?>}0:0 [inlined] at /opt/buildAgent/work/2fed3917837e7e79/kotlin/libraries/stdlib/src/kotlin/util/Standard.kt:83
#14	0x00000001033fb0d8 in kfun:org.kodein.di.internal.DIImpl.Companion.newBuilder#internal at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIImpl.kt:21
#15	0x00000001033fafc0 in kfun:org.kodein.di.internal.DIImpl#<init>(kotlin.Boolean;kotlin.Function1<org.kodein.di.DI.MainBuilder,kotlin.Unit>){} at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIImpl.kt:18
#16	0x00000001033dd590 in kfun:org.kodein.di.DI.Companion#invoke(kotlin.Boolean;kotlin.Function1<org.kodein.di.DI.MainBuilder,kotlin.Unit>){}org.kodein.di.DI at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/DI.kt:626
#17	0x00000001033dd678 in kfun:org.kodein.di.DI.Companion#invoke$default(kotlin.Boolean;kotlin.Function1<org.kodein.di.DI.MainBuilder,kotlin.Unit>;kotlin.Int){}org.kodein.di.DI at /Users/runner/work/Kodein/Kodein/kodein-di/src/commonMain/kotlin/org/kodein/di/DI.kt:626
#18	0x00000001021f0fd0 in kfun:di.$init_global#internal at /Users/user/Documents/xcode/mingli_ios/projects/Mingli Font Viewer/composeApp/src/commonMain/kotlin/di/Kodein.kt:19
#19	0x0000000102552f48 in CallInitGlobalPossiblyLock ()
#20	0x00000001021f1144 in kfun:di#<get-di>(){}org.kodein.di.DI at /Users/user/Documents/xcode/mingli_ios/projects/Mingli Font Viewer/composeApp/src/commonMain/kotlin/di/Kodein.kt:1
#21	0x00000001021f7f64 in kfun:screen#App(androidx.compose.runtime.Composer?;kotlin.Int){} at /Users/user/Documents/xcode/mingli_ios/projects/Mingli Font Viewer/composeApp/src/commonMain/kotlin/screen/App.kt:12

Hi, Actually I do use Kodein inside a Compose Multiplatform project, with Android and iOS.
I was not able to reproduce it.

Do you have a simple reproducer project ?

@schmidt9 @alfietapping
Just retried on CMP and it's ok.
Do you do something special while creating your instances? like using coroutines or something?

Here is my (non) reproducer:
KMP-DI-REPRODUCER.zip

@romainbsl your reproducer works, so problem seems to be in other dependency (Voyager), trying to figure out with a separate reproducer

            voyagerVersion = "1.0.0"
...

            // https://voyager.adriel.cafe/setup
            implementation(libs.voyager.navigator)
            implementation(libs.voyager.transitions)
            implementation(libs.voyager.screenmodel)
            // https://voyager.adriel.cafe/screenmodel/kodein-integration
            implementation(libs.voyager.kodein)

I also usse voyager with Kodein on a CMP project.
Given your context, can you try to reproduce it start from my project ?

Well I seem to find the cause (not obvious for me) - declaration order

import org.kodein.di.DI
import org.kodein.di.bindProvider

// declaration order matters:
// if you declare di (2) before homeScreenModule (1) crash occurs on both Android and iOS

// 1
val homeScreenModule = DI.Module(name = "homeScreen") {
    bindProvider {
        HomeScreenModel()
    }
}

// 2
val di = DI {
    importAll(
        homeScreenModule
    )
}

KodeinIOSTest.zip

Well... This looks normal to me.

if you do something like

val di = DI { import(myModule) }

val myModule = DI.Module("whatever") { ... }

Using val di = DI you are doing a direct assignement. Then DI { } DSL is executed. So it tries to import the module with a reference this is NOT yet created.

This is like doing a function:

fun do() {
  val a = b + 1 // b is unknown
  val b = 2
}

However, with Kodein you can still declare (2) before (1) by using a lazy property, like:

// 2
val di by DI.lazy {
    importAll(homeScreenModule)
}

// 1
val homeScreenModule = DI.Module(name = "homeScreen") {
    bindProvider {
        HomeScreenModel()
    }
}

There is no compiler issue, as the DI lambda could be lazy, but the compiler knows nothing about this.

I closed the issue, but do not hesitate to reopen if needed.

thanks a lot for quick help, actually I'm not sure that @alfietapping has the same issue... but mine is solved