Search code examples
kotlinkotlin-multiplatform

Kotlin MPP expect / actual with different signatures


I have a manager class that has an Android and iOS impl (from a 3rd party lib). "ex. MyManagerImpl()". To construct the 3rd party manager, iOS does not require a Context but Android does. I created a common class "MyManager" which pulls out all the common methods needed to be called within commonMain.

//commonMain
expect class MyManager {
  fun method1()
  companion object Factory {
    makeManager(): MyManager
  }
}

val manager = MyManager.Factory.makeManager() // ex intended usage

//androidMain
MyManagerImpl(context: Context) {
  fun method1()
}

actual class MyManager private constructor(manager: MyManagerImpl) {
  ..
  actual companion object Factory {
    override fun makeManager(): MyManager {
      return MyManager(MyManagerImpl(?how to get context?))
    }
  }
}

//iosMain
MyManagerImpl() {
  fun method1()
}

actual class MyManager private constructor(manager: MyManagerImpl) {
  ..
  actual companion object Factory {
    override fun makeManager(): MyManager {
      return MyManager(MyManagerImpl())
    }
  }
}

What is the cleanest way to merge the two implementations? Is it possible to do so even tho they have different constructor dependencies? We would like to be able to construct the classes lazily within commonMain. Is this possible?


Solution

  • There isn't a super clean way to do this, as a general rule. There's no way to just globally grab a Context from Android. Although not pretty, I'd do something like this:

    //androidMain
    class MyManagerImpl(context: Context) {
        fun method1(){}
    }
    
    actual class MyManager private constructor(manager: MyManagerImpl) {
    
        actual companion object Factory {
            lateinit var factoryContxet :Context
            override fun makeManager(): MyManager {
                return MyManager(MyManagerImpl(factoryContxet))
            }
        }
    }
    
    class SampleApplication : Application{
        override fun onCreate() {
            super.onCreate()
            MyManager.Factory.factoryContxet = this
        }
    }
    

    If you want to be able to call this from any code, init the Context on app start. Holding that in a static reference won't show up on everybody's best practice list, but it's not a technical issue per see. Alternatively, you could do something like that with an Activity, but that has it's own set of issues.