Hey everyone, I’m using Koin in a multiplatform pr...
# koin
e
Hey everyone, I’m using Koin in a multiplatform project, and I’m having an issue on iOS/Swfit retrieving Kotlin objects from my Koin application. I’m using the extension methods for
get
from the KampKit project, but for some objects I get a
EXC_BAD_ACCESS
crash. Can anyone point me to what I’m doing wrong? Snippets in comments.
Copy code
let koinApplication = MySdkKt.initializeSdk(configuration: SdkConfiguration.Builder().baseUrl(baseUrl: "<http://google.com.com>").sessionId(sessionId: "a").customerId(customerId: "a").deviceInfo(deviceInfo: DeviceInfo(platform: "a", platformVersion: "a", deviceDetails: "a", deviceToken: "a")).deviceBlackBox(deviceBlackBox: SwiftBlackBoxProvider()).build())

        let config = koinApplication.koin.get(objCClass: BaseConfiguration.self, parameter: "View Controller") as! BaseConfiguration
        
        print(config.customerId)
The above Swift works fine, but if I try to access something I set up in my
initializeSdk
method, I get
EXC_BAD_ACCESS
Copy code
let customerRepo = MySdkKt.sdkKoinApp.koin.get(objCClass: SdkCustomerRepository.self, parameter: "View Controller") as! SdkCustomerRepository //This fails
Additionally, everything works fine on Android
r
Can you say any more about how
BaseConfiguration
and
SdkCustomerRepository
differ? Is any of the repository access happening from a background thread?
e
@russhwolf, yes, sure. BaseConfiguration is a sealed class with a couple of subtypes that validate different attributes/fields. SdkCustomerRepository is an interface with a couple simple methods that has a discrete implementation.
Copy code
sealed class BaseConfiguration(
    open val sessionId: String,
    open val baseUrl: String,
    open val deviceBlackBox: String,
    open val deviceInfo: DeviceInfo,
    open val loggingEnabled: Boolean,
    open val customerId: String,
)
Copy code
interface SdkCustomerRepository{
    suspend fun authenticateSdkCustomer(customerId: String, ssoToken: String):Either<Failure, SdkCustomerResponse>

    suspend fun getSdkCustomer(customerId: String, ssoToken: String):Either<Failure, SdkCustomerResponse>
}

class SdkCustomerRepositoryImpl(
    private val sdkCustomerApi: SdkCustomerApi,
    val locationProvider: LocationProvider
) : SdkCustomerRepository, KoinComponent {
}
They are being set up in a koin module like this:
Copy code
val sdkModule = module {

        single { configuration } //the arguments to the SDK that control how Koin is configured

        single { LocationProvider() } //A Wrapper Around Device's Location Services

//some ktor instances used for API's...

        single { SdkInitializeApi() }

        single<SdkInitializeRepository> { SdkInitializeRepositoryImpl() }

        single { SdkCustomerApi(get(), get(qualifier(jsonHttpClientQualifierName))) }

        single<SdkCustomerRepository> { SdkCustomerRepositoryImpl(get(), get()) }
}
Both accesses are being made in a
viewDidLoad
method, so they should both be from the main thread. I haven’t gotten to the point where threading should be a problem. The only difference I can really think of is the configuration is being created in swift and passed to the KMP library, while the
SdkCustomerRepository
is being instantiated in KMP. I can access the config from Koin in swift, but neither of the repos in the module snippet above.
r
Hm. Not clear to me what's going wrong. There is a limitation that you can't bind Swift/Obj-C types to the Koin graph, but I don't think that's what's happening here.
1
This probably won't help, but what happens if you drop the
KoinComponent
interface from the repo? You shouldn't need it since you're doing constructor injection, but I also don't see why it would cause a problem
e
I’m actually trying to move away from constructor injection, but sure, I’ll go ahead and give that a shot.
Yeah, it results in the same EXC_BAD_ACCESS w/o the KoinComponent interface.
Copy code
Thread 1: EXC_BAD_ACCESS (code=1, address=0x31b)
Otherwise things are working fine. If I instantiate Kotlin KMP classes in Swift (things like a interactors/use cases) that lazily load dependencies using
by inject()
they work fine. But there are a few things I’d like to access in the Koin graph directly from Swift…
a
I had a similar problem trying to inject interfaces into swift. https://github.com/abhishekdewan101/Scout/blob/e9fa218a6dda1cbb2080547c39f6fd1779405faa/core/src/iosMain/kotlin/com/abhishek101/core/di/KoinIos.kt#L37 Wrote this to help solve that. See if this works for you
👀 1
e
@Abhishek Dewan I added that extension method and invoked it as follows:
Copy code
let repo = MySdkKt.sdkKoinApplication.koin.get(objCProtocol: SdkCustomerRepository.self) as! SdkCustomerRepository
And it resulted in an unrecognized selector crash:
Copy code
2021-06-10 16:48:22.398739-0500 iosSampleApp[72487:1483526] -[ISSKoin_coreKoin getObjCProtocol:]: unrecognized selector sent to instance 0x600001e41410
2021-06-10 16:48:22.413973-0500 iosSampleApp[72487:1483526] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ISSKoin_coreKoin getObjCProtocol:]: unrecognized selector sent to instance 0x600001e41410'
*** First throw call stack:
(
	0   CoreFoundation                      0x00007fff20422fba __exceptionPreprocess + 242
	1   libobjc.A.dylib                     0x00007fff20193ff5 objc_exception_throw + 48
	2   CoreFoundation                      0x00007fff20431d2f +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0
	3   CoreFoundation                      0x00007fff204274cf ___forwarding___ + 1455
	4   CoreFoundation                      0x00007fff204297a8 _CF_forwarding_prep_0 + 120
	5   MySdk                             0x000000010b23a32e $s7MySdk0aB13InitilizationC11viewDidLoadyyF + 2526
a
So if you check https://github.com/abhishekdewan101/Scout/blob/e9fa218a6dda1cbb2080547c39f6fd1779405faa/core/src/commonMain/kotlin/com/abhishek101/core/di/RepoModule.kt#L28 you and I are creating the dependencies the same way. however my Repository implementation only implement the repository interface and not Koin component. perhaps that's the issue ? When it gets compiled into object c it has multiple protocols associated with it and if doesn't know how to resolve for it. Worth a try to remove the KoinComponent And try
1
e
@Abhishek Dewan I believe you are correct that it is the issue. If I remove the KoinComponent interface, I can then access it via your protocol get extension. Thank you both @Abhishek Dewan and @russhwolf!