Using Kotlinx_datetime in Swift

 Published On December 24, 2020

Everyone loves cross-platform solutions, and almost all platforms have a web engine that can parse JavaScript; however, some Swift/Kotlin lovers like me just don’t like JavaScript itself for various reasons, for example it can result really, really bad code, mostly when you are in a team with developers of different skill levels. Thankfully we have 2 potential solutions: Swift on Android, and Kotlin Multiplatform Mobile. Today we are going to talk about a specific problem in KMM, and a potential solution.

Using Kotlinx_datetime in Swift

There is a component kotlinx-datetime that works for both platform in KMM, but it is an actual implementation, i.e. it’s not bridged to Date in iOS/Swift. So what if you want to pass it from/to a KMM library?

First solution is very straightforward: we only use kotlinx-datetime in KMM, and in APIs, we use timestamp instead. Depend on your use case, this may not be a bad approach.

Another solution is to use the Swift version Kotlinx_datetime* in your APIs. Of course, in Swift you mostly want to convert it from/to Date, as you don’t want to write your code with Kotlinx_datetime*.

KMM to Swift

This is very simple. Suppose we have:

object SharedTest {
    fun testOut(): Instant {
        val now = Clock.System.now()
        println("now: " + now)
        return now
    }
}

Which gives us a Kotlinx_datetimeInstant in Swift; and we can simply convert it like this:

let now = SharedTest().testOut()
let date = Date(timeIntervalSince1970: TimeInterval(now.epochSeconds))

Swift to KMM

This is surprisingly more problematic then I expected. Kotlinx_datetimeInstant() doesn’t compile, as there’s no init in Kotlinx_datetimeInstant. The developer explained why it’s like this here, and offered several solutions. I found the 3rd hacky one is the simplest:

Force the companion object to be included in the framework. For example, upon adding the following code to Platform.kt in iosMain in a sample KMM project, the companion object was included in the headers:

> fun Instant.Companion.dummy(): Nothing = TODO()

After doing this, you can keep writing your Swift sample code like this:

let dateX = Kotlinx_datetimeInstant.Companion().fromEpochMilliseconds(epochMilliseconds: Int64(date.timeIntervalSince1970) * 1000)
SharedTest().testIn(date: dateX)

And of course, you need to have this in your KMM SharedTest:

    fun testIn(date: Instant) {
        println("input: " + date)
    }

One more thing

Since iOS is using Double as timestamp and Android is using Int64, some information may loss and it may affect the logic of your shared library. Please be careful.


Tags: KMM Kotlin Swift

Comments:

comments powered by Disqus