Search code examples
kotlingradle-kotlin-dslkotlin-multiplatformkapt

Kotlin Multiplatform Dependency Injection with Annotations


I am attempting to use this library for dependency injection within my Kotlin Multiplatform project - https://github.com/corbella83/PopKorn

The library is based on kapt and the Android part works perfectly! However, I'm struggling to understand why iOS doesn't seem to be working (I have filed an issue on their end but wanted to ask here in case I'm issing something obvious).

I have a vanilla KMM project out-of-the-box from the KMM Android Studio plugin with the following dependency-related setup:

// In shared/build.gradle.kts

kotlin {
  ...
    
  sourceSets {
    val commonMain by getting {
      dependencies {
         implementation("cc.popkorn:popkorn:2.1.1")
      }
    }
  }
}

dependencies {
  "kapt" ("cc.popkorn:popkorn-compiler:2.1.1") <- SHOULD THIS BE HERE?
}
// In androidApp/build.gradle.kts

dependencies {
  implementation("cc.popkorn:popkorn:2.1.1")
}
// In commonMain source set

@Injectable
class Greeting {
  fun greeting(): String {
    return "Hello, ${Platform().platform}!"
  }
}
// In MainActivity.kt

val greeting by popkorn<Greeting>() <- WORKS!
// In iosMain/Bridge.kt

fun init(creator: (ObjCClass) -> Mapping) = cc.popkorn.setup(creator)

fun getInjector() = InjectorObjC(popKorn())
// In iosApp

@main
struct iOSApp: App {
    init() {
        BridgeKt.doInit { (clazz) -> PopkornMapping in
            return clazz.alloc() as! PopkornMapping
        }
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
// In ContentView.swift

let greet = (BridgeKt.getInjector().inject(clazz: Greeting.self) as! Greeting).greeting()

The error I have is Could not find Provider for this class: com.example.myapplication.Greeting. Did you forget to add @Injectable? which is clearly not true as I do have the annotation.

My concern is that when android compiles I can see some logs related to annotation generation with PopKorn, however I do not see this when I build the iOS app using the following Run Script (the default when a new project is created):

./gradlew :shared:embedAndSignAppleFrameworkForXcode

Is there any additional setup required for kapt to work on iOS?


Solution

  • I ended up using a different library for dependency injection for Kotlin Multiplatform (Kodein) but for what it's worth I thought it'd be helpful for anyone having similar issues to know how I resolved it. The key was to expose the generated code to Swift using this:

    val iosMain by getting {
        ...
        kotlin.srcDir("${buildDir.absolutePath}/generated/source/kaptKotlin/")
    }
    

    After this the injection from Swift worked as it was meant to. However, I ended up having more issues trying to inject things from multiple modules and felt Kodein has better support for what I'm trying to do at the moment.