Search code examples
kotlinkotlin-coroutineskoinkotlin-nativekotlin-multiplatform-mobile

Kotlin Multiplatform: IllegalStateException: Must be main thread


I'm using Koin 3.0.0-alpha-4 version and when I'm trying to use injected class by koin, then it throws exception for iOS side: KotlinException=kotlin.IllegalStateException: Must be main thread, KotlinExceptionOrigin I have a singleton class where I'm initializing objects using koin like that:

@ThreadLocal
object ObjectFactory : KoinComponent{
  val appStateRepository: AppStateRepository by inject()
  val appStateRepositoryDemo =  AppStateRepository()
}

if I use appStateRepository inside background thread (Dispatchers.Default), which is injected by koin, then it throws exception IllegalStateException: Must be main thread, but if I use appStateRepositoryDemo than it works fine

Here is a method which I'm invoking from iOS to inject modules

fun initKoinIos(
  userDefaults: NSUserDefaults,
  doOnStartup: () -> Unit
): KoinApplication = initKoin {
  module {
    ...
    single { doOnStartup }
  }
}
fun initKoin(appDeclaration: KoinAppDeclaration = {}) = startKoin {
  appDeclaration()
  modules(
    platformModule,
    networkModule,
    useCaseModules,
    repositoryModule,
    commonUtils,
  )
}

Here is the usage:

fun testRepoAccess() {
     ObjFactory.appStateRepository.test() // Ok, here we are in main thread

     CoroutineScope(Dispatchers.Default).launch {
       ObjFactory.appStateRepositoryDemo.test() //  Ok
       ObjFactory.appStateRepository.test() // Not Ok, throws exception (Must be main thread)
     }
}

Expected behavior It should work for iOS like it works Android

Koin 3.0.0-alpha-4

Additional moduleDefinition

Coroutines 1.4.2-native-mt

UPDATE

I was using wrong Koin library name, now I'm using :

io.insert-koin:koin-core:3.0.1

and now I have another exception:

  kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared org.koin.core.context.GlobalContext.KoinInstanceHolder@1c02ca8 from other thread

Solution

  • Koin on Kotlin/Native requires injection on the main thread, to avoid freezing Koin state. As a result, you can't actually inject directly from a background thread.

    Adding a special inject method that would allow you to inject by switching to the main thread was intended to be added to Koin, but I'm pretty sure that never wound up in the code. Primarily because it's very rare that anybody has needed it.

    So, anyway, you can't do that with Koin. You could try Kodein, but I wrote Koin's implementation to throw precisely because touching Koin from another thread will freeze everything inside it, and that may not be what you intend to do.

    I know nobody likes non-answers, but why is ObjectFactory @ThreadLocal? I assume to keep it mutable, but if appStateRepository is a single, it would be frozen anyway.