Kotlin/Native — Use Kotlin In C and Apple Framework [Part 2] | by Debanshu Datta | Sep, 2023
In the previous article, we embarked on a journey into the world of Kotlin/Native, uncovering its capabilities in compiling Kotlin code to native binaries, free from the constraints of virtual machines. We explored the architecture of Kotlin/Native, delving into its LLVM-based backend and the crucial role played by the runtime implementation in executing platform-specific code.
In the current part, we will focus on how to use Kotlin libraries and other functionalities, in C language and in Apple Framework.
Kotlin/Native compiler can produce a dynamic library out of the Kotlin code. A dynamic library often comes with a header file, a .h
file, which we will use to call the compiled code from C.
Using Ktor Client to make API Calls from Kotlin code, convert this to the dynamic library and use the same in our C language.
- We will start by creating a new
Native Application
via IntelliJ. Now we will go tobuild.gradle.kts
and change the following. We have added thesharedLib
which will get Gradle to build a dynamic library. Then add the required dependenciesnativeMain
for making API calls, coroutines, etc.
plugins {
kotlin("multiplatform") version "1.8.0"
}
repositories {
mavenCentral()
}kotlin {
val ktorVersion = "2.2.2"
macosArm64("native") { // on Apple Silicon macOS
// linuxX64("native") { // on Linux
// macosX64("native") { // on x86_64 macOS
// mingwX64("native") { // on Windows
binaries {
sharedLib {
baseName = "native" // on Linux and macOS
// baseName = "libnative" // on Windows
}
}
sourceSets {
val nativeMain by getting{
dependencies {
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
implementation("io.ktor:ktor-client-curl:$ktorVersion")
}
}
val nativeTest by getting
}
}
}
tasks.wrapper {
gradleVersion = "7.3"
distributionType = Wrapper.DistributionType.ALL
}
- We will now make
api
a new directory insidesrc/nativeMain/kotlin
. We will start by creating a client fileclient.kt
. Not explaining much since these are basic Kotlin and Ktor.
package api
import io.ktor.client.HttpClient
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json
import io.ktor.client.engine.curl.Curlval networkClient:HttpClient = HttpClient(Curl) {
install(ContentNegotiation){
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
})
}
}
- We will then create a new file
postAPI.kt
package api
import io.ktor.client.call.*
import io.ktor.client.request.*class PostAPI {
suspend fun getPosts(): String {
val response = networkClient.get("https://jsonplaceholder.typicode.com/posts/1")
return response.body<String>().toString();
}
}
- Finally, to consume this use case class
GivePostUsecase.kt
, just a wrapperrunBlocking
to call the suspending function. We can put it inside a new directoryapi/usecases
for better code formatting.
package api.usecase
import api.PostAPI
import kotlinx.coroutines.runBlockingclass GetPostUseCase {
fun getPostDirect() : String {
return runBlocking {
PostAPI().getPosts()
}
}
}
- We are done with the Kotlin coding part. Furthermore, we will convert this to a dynamic library and consume this in the C program. Besides, we need to run the command.
./gradlew linkNative
We will find a new library libnative.dylib
and a new file libnative_api.h
pop up inside build/bin/debugShared
.
The build generates the following files under the build/bin/debugShared
folder, depending on the host OS:
- macOS:
libnative_api.h
andlibnative.dylib
A file with the DYLIB file extension is a Mach-O (Mach Object) dynamic library file that an application references during runtime to perform certain functions on an as-needed basis. - Linux:
libnative_api.h
andlibnative.so
. A file with the.SO
extension is a shared library file. SO file is a shared library file used on Android and Linux operating systems. - Windows:
libnative_api.h
,libnative_symbols.def
andlibnative.dll
. A DLL is a library that contains code and data that can be used by more than one program at the same time.
If we dive deep into the generated libnative_api.h
file, we will notice that it is the mapping of the Kotlin Code to C Language. Kotlin uses the libnative_
prefix for all declarations in the created libnative_api.h file. As C does not support namespaces, the Kotlin/Native compiler creates lengthy names to prevent potential conflicts with other symbols already present in the native project.
- We use this library in the C program.
extern libnative_ExportedSymbols* libnative_symbols(void);
Theextren
keyword in C is used to declare a variable as a global variable, such that a variable declared in another scope of the same file or another file can be accessed from anywhere in the program. The function libnative_symbols allows us to open the way from the native code to the Kotlin/Native library, an entry point for us. The library name is used as a prefix for the function name. We don’t have the concept of class and object, so to call a function is via references. Thislibnative_kref_api_usecases_GetPostUseCase
is already defined in the mapping(.h file), using which we refer forward.
#include <stdio.h>
#include "libnative_api.h"int main () {
libnative_ExportedSymbols* lib = libnative_symbols();
libnative_kref_api_usecases_GetPostUseCase br = lib->kotlin.root.api.usecases.GetPostUseCase.GetPostUseCase();
const char* pr = lib->kotlin.root.api.usecases.GetPostUseCase.getPostDirect(br);
printf("%s",pr);
}
- Finally, run the commands
clang main.c libnative.dylib
(for Mac),gcc main.c libnative.so
(for Linux). This command generates oura.out
file and runs the command./a.out
to execute the program. Note: Make sure that bothlibnative.dylib
andlibnative_api.h
are in the same directory asmain.c
Kotlin/Native also helps with the interoperability with Apple Framework for macOS and iOS. These frameworks consist of all the binaries and requirements needed for interop with Objective-C and Swift. The implementation of the code is similar to our dynamic library. All other dependencies and configurations are mostly the same only a change in dependency of the client used. We have to define the framework we want to build for in our case it isiosSimulatorArm64
, just for testing purposes. The only difference we find in our build.gradle.kts
kotlin {
val ktorVersion = "2.2.2"
iosSimulatorArm64("native") {
binaries {
framework {
baseName = "apidemo"
}
}
sourceSets {
val nativeMain by getting{
dependencies {
....
implementation("io.ktor:ktor-client-darwin:$ktorVersion")
}
}
val nativeTest by getting
}
}
}
We are done with the Kotlin coding part. We will convert this to a dynamic library, and we will consume this in the C program. Furthermore, we need to run the command.
./gradlew linkNative
These will build new build/bin/native/debugFramework
and build/bin/native/releaseFramework
Simply import this framework to our iOS Simulator App and start working with our app. We are not using showing since it is out of the scope of this discussion.
The series goes over Kotlin/Native, which is an LLVM backend for the Kotlin compiler, runtime implementation, and native code generation facility that uses the LLVM toolchain. It compiles Kotlin code into native binaries, making it ideal for platforms where virtual machines are not desirable or possible, or for producing a reasonably-sized self-contained program that does not require additional execution runtime. The series also goes over the Kotlin/Native architecture, which includes source code, Intermediate Representation (IR), a JVM-based environment, a compiler frontend, a compiler backend, and runtime implementation. It shows how to use the C Library in Kotlin code, convert Kotlin/Native code to a dynamic library and use it in C, and interact with the Apple Framework.
For any doubts and suggestions, you can reach out on my Instagram, or LinkedIn. Follow me for Kotlin content and more. Happy Coding!
I will well appreciate one of these 👏