I'm trying to call Swift/Objective-C code from Kotlin in a multiplatform project. There are no problems with calls to platform code. But when I'm trying to call some library (or framework, not sure how it is properly called as I'm not an iOS dev) it fails. Docs states that it is possible to call Objective-C code and Swift if it is properly exported:
Kotlin/Native provides bidirectional interoperability with Objective-C. Objective-C frameworks and libraries can be used in Kotlin code if properly imported to the build (system frameworks are imported by default). See e.g. "Using cinterop" in Gradle plugin documentation. A Swift library can be used in Kotlin code if its API is exported to Objective-C with @objc. Pure Swift modules are not yet supported.
But it does not say anything about how can I import them properly. It only point to gradle plugin description that describes old version of gradle plugin. So it does not work for me. Finally I figured out something might be the way to import Objective-C code:
fromPreset(presets.iosX64, 'ios') {
compilations.main.outputKinds('FRAMEWORK')
compilations.main {
cinterops {
firebase {
def pods = '${System.getProperty("user.home")}/Projects/kmpp/iosApp/Pods/'
includeDirs '${pods}Firebase/CoreOnly/Sources',
'${pods}FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers'
}
}
}
}
Build runs without failures, but it does not import anything. What am I doing wrong? Is it possible to import such a lib at all?
UPD:
here I found an example of usage cinterop
tool like this:
cd samples/gitchurn
../../dist/bin/cinterop -def src/main/c_interop/libgit2.def \
-compilerOpts -I/usr/local/include -o libgit2
It looks like cinterop
tool should be in /dist/bin/
folder in my projects but there is no such folder. Where do I get cinterop
tool ?
I ended up with this cinterops
section in build.gradle
fromPreset(presets.iosX64, 'ios') {
// This preset is for iPhone emulator
// Switch here to presets.iosArm64 (or iosArm32) to build library for iPhone device
compilations.main {
outputKinds('FRAMEWORK')
cinterops {
firebase {
defFile "$projectDir/src/iosMain/cinterop/firebase.def"
includeDirs {
allHeaders "$projectDir/../iosApp/Pods/FirebaseCore/Firebase/Core/Public",
"$projectDir/../iosApp/Pods/FirebaseDatabase/Firebase/Database/Public"
}
compilerOpts "-F$projectDir/../iosApp/Pods/Firebase -F$projectDir/../iosApp/Pods/FirebaseCore -F$projectDir/../iosApp/Pods/FirebaseDatabase"
linkerOpts "-F$projectDir/../iosApp/Pods/Firebase -F$projectDir/../iosApp/Pods/FirebaseCore -F$projectDir/../iosApp/Pods/FirebaseDatabase"
}
}
}
}
end this .def
file:
language = Objective-C
headers = FirebaseCore.h FirebaseDatabase.h
What's going on here? Cocopods frameworks are placed in Pods
directory in your Xcode project. Navigating this folder a bit, you'll find what you need. I'm not sure if there is some standard but firebase place main header file in Public
folder. And it contains references to other header files it needs... So you specify these file names in your .def
file in the headers section.
Next, you need to specify where to look for these files and others referenced by them. You can do it in the .def
file in includeDirs
or in build.gradle
file. I prefer to use the build.gradle file as it can use variables. So you specify the path to these Public
folders. (This is enough for kotlin to see library API, but in order to be able to run the app you need to compile and link this library...)
Then the compiler and linker need to know where library/framework is. So you specify path to root folder of the framework in compilerOpts
and linkerOpts
prefixing them with -F
if it is a framework or -L
if it is a library.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With