I’m not able to use Koin in a KMM project :disappo...
# koin
j
I’m not able to use Koin in a KMM project 😞 really need some help: This is the Koin in the shared module
Copy code
fun initKoin(appDeclaration: KoinAppDeclaration = {}): KoinApplication {
  return startKoin {
    appDeclaration()
    modules(domainModule, remoteModule, dataModule)
  }
}

fun startIosKoin(iosModules: List<Module> = emptyList()) = initKoin {
  modules(iosModules)
}
Which is called by KoiniOS.kt
Copy code
private val iosModules: List<Module> = listOf(
  storageModule
)

fun inject() = startIosKoin(iosModules)
This
storageModule
has iOS specific objects such as
NSDefaults
Ths domain module has simple kotlin objects as well data module (repository layer) and the remote module is ktor where I have
expect/actual
engines. (which is called in the AppDelegate
KoiniOSKt.inject()
) - But for some reason whenever I try to use any use case on iOS it just breaks no
NoBeanDefFoundException#<init>(kotlin.String){
Android works fine but iOS can’t use any of the deps, is there something obvious that I am missing or do I need to post more code/plumbing?
t
What's the complete error you get? On first sight it looks like
NoBeanDefFoundException
cannot be initialized.
j
Here’s a bigger log of this, it seems like it can’t find a single dependency
Copy code
Uncaught Kotlin exception: org.koin.core.error.InstanceCreationException: Could not create instance for [Factory:'com.pleasyplay.mobile.domain.auth.executor.LoginUseCase']
    at 0   mobile                              0x0000000103bd6e1c kfun:kotlin.Exception#<init>(kotlin.String?;kotlin.Throwable?){} + 124 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/Exceptions.kt:25:63)
    at 1   mobile                              0x0000000103d3ba5c kfun:org.koin.core.error.InstanceCreationException#<init>(kotlin.String;kotlin.Exception){} + 124 (/Users/a.giuliani/workspace/koin_3.0.0/koin-projects/koin-core/src/commonMain/kotlin/org/koin/core/error/InstanceCreationException.kt:23:67)
    at 2   mobile                              0x0000000103d3d09d kfun:org.koin.core.instance.InstanceFactory#create(org.koin.core.instance.InstanceContext){}1:0 + 1469 (/Users/a.giuliani/workspace/koin_3.0.0/koin-projects/koin-core/src/commonMain/kotlin/org/koin/core/instance/InstanceFactory.kt:63:19)
    at 3   mobile                              0x0000000103d3c178 kfun:org.koin.core.instance.FactoryInstanceFactory#get(org.koin.core.instance.InstanceContext){}1:0 + 200 (/Users/a.giuliani/workspace/koin_3.0.0/koin-projects/koin-core/src/commonMain/kotlin/org/koin/core/instance/FactoryInstanceFactory.kt:36:16)
    at 4   mobile                              0x0000000103d443c4 kfun:org.koin.core.registry.InstanceRegistry#resolveInstance(kotlin.String;kotlin.Function0<org.koin.core.parameter.DefinitionParameters>?){0§<kotlin.Any?>}0:0? + 724 (/Users/a.giuliani/workspace/koin_3.0.0/koin-projects/koin-core/src/commonMain/kotlin/org/koin/core/registry/InstanceRegistry.kt:87:38)
    at 5   mobile                              0x0000000103d4f371 kfun:org.koin.core.scope.Scope#resolveInstance(org.koin.core.qualifier.Qualifier?;kotlin.reflect.KClass<*>;kotlin.Function0<org.koin.core.parameter.DefinitionParameters>?){0§<kotlin.Any?>}0:0 + 737 (/Users/a.giuliani/workspace/koin_3.0.0/koin-projects/koin-core/src/commonMain/kotlin/org/koin/core/scope/Scope.kt:208:34)
    at 6   mobile                              0x0000000103d4efe5 kfun:org.koin.core.scope.Scope#get(kotlin.reflect.KClass<*>;org.koin.core.qualifier.Qualifier?;kotlin.Function0<org.koin.core.parameter.DefinitionParameters>?){0§<kotlin.Any?>}0:0 + 2613 (/Users/a.giuliani/workspace/koin_3.0.0/koin-projects/koin-core/src/commonMain/kotlin/org/koin/core/scope/Scope.kt:194:13)
    at 7   mobile                              0x0000000103b2db24 kfun:com.pleasyplay.mobile.di.executors.AuthUseCaseFactory.<init>$<anonymous>_1#internal + 676 (/Users/joaquimley/Git/pleasyplay/mobile/mobile/src/iosMain/kotlin/com/pleasyplay/mobile/di/executors/AuthUseCaseFactory.kt:10:1321)
    at 8   mobile                              0x0000000103b2dde5 kfun:com.pleasyplay.mobile.di.executors.AuthUseCaseFactory.$<init>$<anonymous>_1$FUNCTION_REFERENCE$50.invoke#internal + 165 (/Users/joaquimley/Git/pleasyplay/mobile/mobile/src/iosMain/kotlin/com/pleasyplay/mobile/di/executors/AuthUseCaseFactory.kt:10:1321)
    at 9   mobile                              0x0000000103bca8e6 kfun:kotlin.UnsafeLazyImpl#<get-value>(){}1:0 + 550 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/backend.native/build/stdlib/kotlin/util/Lazy.kt:81:26)
    at 10  mobile                              0x0000000103b2d7e3 kfun:com.pleasyplay.mobile.di.executors.AuthUseCaseFactory#<get-loginUseCase>(){}com.pleasyplay.mobile.domain.auth.executor.LoginUseCase + 323 (/Users/joaquimley/Git/pleasyplay/mobile/mobile/src/iosMain/kotlin/com/pleasyplay/mobile/di/executors/AuthUseCaseFactory.kt:8:45)
    at 11  mobile                              0x0000000103b5af6b objc2kotlin.541 + 203 (-/<compiler-generated>:1:0)
🤔 1
For some context I was using a sort of factory to access the koin components
Copy code
class AuthUseCaseFactory : KoinComponent {
  val loginUseCase: LoginUseCase by inject()
}
a
I encountered similar issue today when trying to inject
NSURLSession
.
Copy code
fun init(config: Config, clientInfo: ClientInfo, urlSession: NSURLSession) = init(config, clientInfo) {
    module { single { urlSession } }
}
Call site:
Copy code
internal actual class PlatformNetwork : Network, KoinComponent {
    val urlSession: NSURLSession by inject()
}
Log:
Copy code
Function doesn't have or inherit @Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError.
It is considered unexpected and unhandled instead. Program will be terminated.
Uncaught Kotlin exception: kotlin.IllegalStateException: KClass for Objective-C classes is not supported yet
    at 0   KME                                 0x0000000102e85bdf kfun:kotlin.Throwable#<init>(kotlin.String?){} + 95 (/Users/teamcity1/teamcity_work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/Throwable.kt:23:37)
    at 1   KME                                 0x0000000102e7e7cd kfun:kotlin.Exception#<init>(kotlin.String?){} + 93 (/Users/teamcity1/teamcity_work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44)
    at 2   KME                                 0x0000000102e7e9ad kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 93 (/Users/teamcity1/teamcity_work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44)
    at 3   KME                                 0x0000000102e7ef1d kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 93 (/Users/teamcity1/teamcity_work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/Exceptions.kt:70:44)
    at 4   KME                                 0x0000000102ebcfaf kfun:kotlin.native.internal.KClassUnsupportedImpl#hashCode(){}<http://kotlin.Int|kotlin.Int> + 335 (/Users/teamcity1/teamcity_work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/native/internal/KClassImpl.kt:66:49)
    at 5   KME                                 0x0000000103089c9f kfun:org.koin.core.definition.BeanDefinition#hashCode(){}<http://kotlin.Int|kotlin.Int> + 383 (/Users/a.giuliani/workspace/koin_3.0.0/koin-projects/koin-core/src/commonMain/kotlin/org/koin/core/definition/BeanDefinition.kt:85:44)
    at 6   KME                                 0x0000000102e9a0a2 kfun:kotlin.collections.HashMap.hash#internal + 226 (/Users/teamcity1/teamcity_work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:205:61)
    at 7   KME                                 0x0000000102e9ae51 kfun:kotlin.collections.HashMap.findKey#internal + 385 (/Users/teamcity1/teamcity_work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:258:20)
    at 8   KME                                 0x0000000102e9797e kfun:kotlin.collections.HashMap#containsKey(1:0){}kotlin.Boolean + 126 (/Users/teamcity1/teamcity_work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:57:56)
    at 9   KME                                 0x0000000102ea4c80 kfun:kotlin.collections.HashSet#contains(1:0){}kotlin.Boolean + 240 (/Users/teamcity1/teamcity_work/f01984a9f5203417/runtime/src/main/kotlin/kotlin/collections/HashSet.kt:31:65)
    at 10  KME                                 0x00000001030a708d kfun:org.koin.core.scope.ScopeDefinition#save(org.koin.core.definition.BeanDefinition<*>;kotlin.Boolean){} + 669 (/Users/a.giuliani/workspace/koin_3.0.0/koin-projects/koin-core/src/commonMain/kotlin/org/koin/core/scope/ScopeDefinition.kt:25:25)
    at 11  KME                                 0x00000001030a7557 kfun:org.koin.core.scope.ScopeDefinition#save$default(org.koin.core.definition.BeanDefinition<*>;kotlin.Boolean;kotlin.Int){} + 167 (/Users/a.giuliani/workspace/koin_3.0.0/koin-projects/koin-core/src/commonMain/kotlin/org/koin/core/scope/ScopeDefinition.kt:24:5)
XY: Want to let library user provide their own NSURLSession, because some might prefer adding SSL pinning.
j
Any pointers on how to move forward fixing this?
r
Koin has a limitation where you can't store something as an Objective-C or Swift type in your Koin graph, because Koin always checks the KClass under the hood and these types don't have KClasses. There's a couple ways you can work around that: 1. Wrap Obj-C/Swjft types in Kotlin objects and add the Kotlin object to Koin 2. Cast the Obj-C/Swift things to
Any
and then use
named()
or other qualifier to distinguish them instead of the type
j
Well it seems like I’m not getting that issue Russell, I was able to provide (still not working) my Kotlin instances to swift, but I’m more than happy to try with named, thank you for your suggestion. I’m getting another issue now
Caused by: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen com.--.mobile.remote.config.HttpClientProvider
--
Copy code
internal class HttpClientProvider(
  private val apiConfig: ApiConfig,
  private val accessTokenProvider: AccessTokenProvider
) {

  private val _httpClient = HttpClient {
      setupFrameworkEngine()
      setupDefaultRequest()
      setupJson()
      setupLogging()
      setupExceptionHandler()
    }

  fun provide(): HttpClient {
    return _httpClient
  }
I’ve read this: https://github.com/JetBrains/kotlin-native/blob/master/IMMUTABILITY.md But I can’t seem to be able to annotate that
_httpClient
property or the
fun provide()
Any pointers? @russhwolf — Thank you btw for your generous time 🙏
r
You can try calling
ensureNeverFrozen()
in an init block inside
HttpClientProvider
. You'll get an exception telling you what froze it. You may need to add an expect/actual around
ensureNeverFrozen()
since it's native-only, unless you're using a library like Stately which has already done this
a
Thanks, @russhwolf. This helped ->
Copy code
2. Cast the Obj-C/Swift things to Any and then use named() or other qualifier to distinguish them instead of the type
j
@aiidziis/@russhwolf can you give me an example how to do that? 🤔 not sure I’m following
a
Looks like you have different issue. I think you need to follow Russell advice and add
ensureNeverFrozen()
to
init()
blocks to determine which part of code is getting frozen.
j
This issue was not related to any of these, I was able to make this work without touching the Koin graph, it was due to the fact the httpclient was created at the domain level while ktor’s engine was on each platform, merging this instantiation to the engine provider fixed it The pains of being at the edge of tech 😛 — Thank you both very much for your help 🙏
573 Views