
Kotlin/Native provides indirect interoperability with Swift through Objective-C.
This is what the documentation says.
While developing my Kotlin Multiplatform library — HealthKMP — I thought after assembling and publishing it would be ready to use in Swift right away. I was wrong.
Problem 1: kotlin.Result
kotlin.Result is an inline class which is not supported in Swift/Objective-C, and as a result we get
Any?
here.
Inlined kotlin.Result
interface HealthManager { fun isAvailable(): Result<Boolean> }
As a possible solution we can create our own wrapper on top of kotlin.Result:
sealed interface KmpResult<out T> { data class Success<T>(val value: T) : KmpResult<T> data class Failure(val error: Throwable) : KmpResult<Nothing> } inline fun <T> kmpResult(block: () -> T): KmpResult<T> { return try { KmpResult.Success(block()) } catch (e: Throwable) { KmpResult.Failure(e) } } interface HealthManager { fun isAvailable(): KmpResult<Boolean> }
And in Swift we would get:

let result = health.isAvailable() switch(result) { case let success as KmpResultSuccess<KotlinBoolean>: let isAuthorized : KotlinBoolean? = success.value print("Success \(isAuthorized ?? false)") case let failure as KmpResultFailure: print("Failure \(failure.error.description())") default: print("Unknown result") }
But even if we specify that method returns not nullable result, we still get nullable value in success case.
I prefer another solution. Let’s create a wrapper for iosMain / appleMain targets:
interface HealthManager { fun isAvailable(): Result<Boolean> } class SwiftHealthManager( private val manager: HealthManager, ) { @Throws(Throwable::class) fun isAvailable(): Boolean { return manager.isAvailable().getOrThrow() } }
And in Swift we would have the correct return type:

Inlined return type
do { let isAvailable : Bool = try health.isAvailable() print("Success \(isAvailable)") } catch { print("Failure \(error)") }
Problem 2: kotlinx.datetime
I am using kotlinx.datetime.Instant to specify a date range. But when it comes to creating its instance in Swift, it doesn’t look like I expected it to be.

I want to continue using Date
in Swift instead of managing a new type Kotlinx_datetimeInstant
.
We can extend the existing wrapper in iMain / appleMain targets to accept NSDate:
class SwiftHealthManager( private val manager: HealthManager, ) { @Throws(Throwable::class) suspend fun readData( startTime: NSDate, endTime: NSDate, type: HealthDataType, ): List<HealthRecord> { return manager.readData( startTime = startTime.toKotlinInstant(), endTime = endTime.toKotlinInstant(), type = type, ).getOrThrow() } }

And now we could create Date
in Swift as usual:
do { let records : [HealthRecord] = try await health.readData( startTime: Calendar.current.date(byAdding: .day, value: -7, to: Date())!, endTime: Date.now, type: HealthDataTypeWeight(), ) print("Success \(records.count)") } catch { print("Failure \(error.localizedDescription)") }
Problem 3: Extensions
Kotlin extensions are exported in Swift, but in order to access them we need to know the file name, they don’t behave as usual Swift extensions.

And this is where SKIE comes to help.
SKIE is a tool for Kotlin Multiplatform development that enhances the Swift API published from Kotlin.
Intallation
libs.versions.toml
:
[versions] skie = "0.10.2" [libraries] [plugins] skie = { id = "co.touchlab.skie", version.ref = "skie" }
2. project build.gradle.kts
:
plugins { alias(libs.plugins.skie) apply false }
3. KMP module build.gradle.kts
:
plugins { // other plugins alias(libs.plugins.skie) }
And that’s it, after assembling we will get Swift friendly code.
Skie result
Now we can access Kotlin extensions in Swift:

Swift extension itself:
extension Date { public func toKotlinInstant() -> Kotlinx_datetimeInstant }
The original Kotlin extension:
fun NSDate.toKotlinInstant(): Instant = toKotlinInstant()
Skie is not only for extensions, see here for supported Kotlin features.
Job Offers
Final thoughts
Building multiplatform library using Kotlin becomes easy nowadays, we can have shareed common Kotlin code and platform specific code for each target, publish our library to maven central Kotlin Multiplatform library or as Swift library to Swift Package Manager.
Check out HealthKMP — Kotlin Multiplatform library for Apple HealthKit on iOS / watchOS and Google Fit / Health Connect on Android.
This article was previously published on proandroiddev.com.