← All shorts
52Kotlin 1.9+2 min read

cinterop: Calling Platform C/Objective-C APIs

cinterop generates Kotlin bindings for C and Objective-C libraries, letting shared iOS code call system frameworks directly.

There is no cross-platform abstraction for CoreLocation. Nothing to point expect/actual at. Kotlin/Native's cinterop tool closes the gap by generating Kotlin bindings straight from a C or Objective-C header, so iosMain code calls the framework directly instead of routing through a hand-written Swift shim.

#The old way

Getting the device's location meant a Swift LocationManager class wrapping CLLocationManager, then exposing a narrow closure-based API back across the bridge so Kotlin could reach it. That translation layer existed for exactly one reason: Kotlin had no way to see CoreLocation itself.

#The new way

Kotlin/Native ships pre-generated bindings for Apple's own frameworks under the platform.* package. No .def file required:

import platform.CoreLocation.CLLocationManager
import platform.CoreLocation.CLLocationManagerDelegateProtocol
import platform.CoreLocation.kCLLocationAccuracyBest
import platform.darwin.NSObject

class IosLocationProvider : NSObject(), CLLocationManagerDelegateProtocol {
    private val manager = CLLocationManager().apply {
        desiredAccuracy = kCLLocationAccuracyBest
        delegate = this@IosLocationProvider
    }

    fun start() {
        manager.requestWhenInUseAuthorization()
        manager.startUpdatingLocation()
    }
}

For a vendored or third-party C library, you write the .def file yourself and register it per target:

kotlin {
    iosArm64 {
        compilations.getByName("main") {
            cinterops {
                create("mylib") {
                    defFile(project.file("src/nativeInterop/cinterop/mylib.def"))
                }
            }
        }
    }
}

The .def file lists headers = mylib.h plus any compilerOpts/linkerOpts. That is the whole file. cinterop reads the header at build time and generates a full Kotlin API from it.

#Why it matters

  • iosMain code can call CoreLocation, CoreBluetooth, or any Apple framework with real Kotlin types, not string-typed reflection
  • Custom C libraries become first-class Kotlin dependencies instead of requiring a Swift wrapper module
  • The generated bindings are regenerated from the header on every build, so they can't silently drift from the actual library API

#Gotcha

The class has to extend NSObject. Implementing the generated *Protocol interface is not enough — a plain Kotlin class that implements CLLocationManagerDelegateProtocol without subclassing NSObject fails to compile. The Objective-C runtime expects delegate callbacks to land on an NSObject-derived instance, and cinterop enforces that at the type level rather than at runtime.

kmpkotlincross-platform

Related shorts