Howdy! For those of you who have dealt with dates in KMP - what do you think is the best practice fo...
i
Howdy! For those of you who have dealt with dates in KMP - what do you think is the best practice for handling dates between shared, Android and iOS? For example, let's say you're calling from iOS into the shared module, is it better to do: a)
someRepository.getData(forDate: "2023-03-30")
b)
someRepository.getData(forDate: Date() /*or whatever kind of Date object shared can work with*/)
And then the same when you get the data back, does it make more sense to try and set up some actual/expect
KMPDate
class or just work with plain String and parse them on each platform? Thanks in advance! P.S. I'd really like to avoid using any libraries for this, if possible.
i
Thank you! I'm aware of this library - is it really a no-brainer for dates, just like ktor is for networking?
c
I think so – it’s essentially a first party library and uses the
java.time
APIs under the hood on JVM-based targets anyway
I have a bit less confidence in the iOS actuals, but I have to assume that jetbrains has actually tested it decently well πŸ˜„
i
Haha, yes, that's fair - thanks again! I haven't realized at first that it was developed by JetBrains, which definitely is reassuring. The app I'm working on is all about dates and scheduling, so I'm just very picky about introducing dependencies πŸ˜…
l
It's still kind of early for kotlinx-datetime. There's no arbitrary date format support, for example (only supports ISO8601), but it's been good enough in my experience so far.
e
you'll need to use platform-specific APIs for things like timezone handling
s
I concur, I'm using it on a semi big project and I'm generally happy though it's not as fully featured as Java time. The best advice I have is to try to keep as much date handling to common code (using expect/actual utilities where needed e.g formatting) I then pretty much exclusively expose pre-formatted dates as strings to my UI layer to avoid having to deal with dates in iOS. This doesn't work in 100% of situations but it's working well for me as a rule of thumb
i
Thanks guys! That was some very interesting and useful insight! Sam, just to make sure I fully understand your approach - so you do use
kotlinx-datetime
library, but keeping it to commonMain as much as possible, and for communication/interface from iOS/Android to shared and back from shared to iOS/Android you're just using plain strings? I mean, if I understood you correctly, this actually makes a lot of sense to me, because I treat my shared module very much like a backend of the app (or "mid-end" in the big picture, haha πŸ˜…). So just like you prepare your dates as strings before a call to an API and parse the string-y response back to date objects, I don't see why this same approach wouldn't be very solid here. Yes, the drawback is handling dates/formatting twice (on iOS and Android separately), but you have the full flexibility.
s
Sorry, missed this. Yes, so I use an MVVM architecture where each screen has a dedicated VM (defined in Kotlin and used by both platforms), these VMs format the dates and surface them to the UI. I have expect/actual utils to do the formatting from common code which I can send if it's useful? There are exceptions to this, date pickers are a good example where a proper date object is needed, but in for the vast majority of screens this works well
i
No worries Sam! And thank you for the further clarification. If you don't mind sharing an example of how you handle dates/formatting, that would be very helpful. Thanks again!
s
Unfortunately I can't find the sources to credit these snippets, but here they are anyway
Copy code
// Common source set
expect fun LocalDateTime.format(format: String): String

fun Instant.format(format: String) = toLocalDateTime(TimeZone.currentSystemDefault()).format(format)

// Android source set
import java.time.format.DateTimeFormatter
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.toJavaLocalDateTime

actual fun LocalDateTime.format(
    format: String,
): String = DateTimeFormatter.ofPattern(format).format(this.toJavaLocalDateTime())

// iOS source set
import platform.Foundation.*
import kotlinx.datetime.*

actual fun LocalDateTime.format(format: String): String {
    val dateFormatter = NSDateFormatter().apply {
        dateFormat = format
    }
    return dateFormatter.stringFromDate(
        toNSDate(NSCalendar.currentCalendar)
            ?: throw IllegalStateException("Could not convert kotlin date to NSDate $this")
    )
}
i
Thank you so much Sam!