David
02/05/2025, 9:15 PMactual typealias PlatformDateTime = NSDate
, but I cannot do actual typealias PlatformUByte = UInt8
, why? I want to avoid the Swift side needing to use KotlinUByteArray. I already achieved similar thing in the past with NSDate and NSData, but not seeing the way with UByteMoussa
02/06/2025, 6:45 AMNSDate
(and NSData
) are Objective-C types, and Kotlin Multiplatform has special handling for bridging between Kotlin types and Objective-C types.
UInt8
(and other Swift/Objective-C numeric types) are bridged more directly, and the bridging for unsigned types is more complex. You can't directly typealias
a Kotlin UByte
to a Swift/Objective-C UInt8
in the same way.
Why it doesn't work:
• Objective-C Bridging: Kotlin Multiplatform's interop with Objective-C is highly optimized. It recognizes NSDate
and NSData
as special cases and handles their conversion smoothly. This is because they are fundamental Foundation types.
• Direct Type Mapping: For other types, especially numeric types, the mapping is often more direct. UByte
in Kotlin is an unsigned 8-bit integer, but the way it's represented at the binary level and how Swift/Objective-C handles UInt8
is not a perfect match for a direct typealias
. The compiler and runtime need to perform conversions.
• Unsigned Type Complexity: Unsigned types are handled slightly differently in Swift/Objective-C and Kotlin. The bridging mechanism has to account for these differences, which makes a simple typealias
impossible.
How to navigate this:
• Using expect/actual
Functions: This is the cleanest and most idiomatic Kotlin Multiplatform approach. Define an expect
function in your common code and provide actual
implementations for each platform.
// in commonMain
expect fun convertToPlatformUByteArray(byteArray: UByteArray): Any // Expect an Any type
// iosMain
actual fun convertToPlatformUByteArray(byteArray: UByteArray): NSData {
val data = NSMutableData()
for (byte in byteArray) {
data.append([byte], 1) // Append each byte
}
return data
}
// Other platforms (if needed)
actual fun convertToPlatformUByteArray(byteArray: UByteArray): ByteArray { // Or a different type
return byteArray.toByteArray() // Example for JVM
}
// Usage (common code)
val uByteArray = ubyteArrayOf(1u, 2u, 3u)
val platformByteArray = convertToPlatformUByteArray(uByteArray)
You can also check Kotlin
source code of how they implemented the above on https://github.com/JetBrains/kotlin/blob/48fa3bda57e610d7376a1cba649c7da6387554f9/libraries/stdlib/src/kotlin/io/encoding/Base64.kt#L821C28-L821C53
• Using Intermediate Type: You could create an intermediate type that is common across platforms and then convert to/from it. This is more complex but might be useful in certain scenarios.
// commonMain
data class ByteArrayWrapper(val bytes: ByteArray)
expect fun wrapUByteArray(byteArray: UByteArray): ByteArrayWrapper
// iosMain
actual fun wrapUByteArray(byteArray: UByteArray): ByteArrayWrapper {
return ByteArrayWrapper(byteArray.toByteArray()) // Convert to regular ByteArray
}
// Usage
val uByteArray = ubyteArrayOf(1u, 2u, 3u)
val wrapper = wrapUByteArray(uByteArray)
val swiftData = wrapper.bytes.toNSData() // Swift extension to convert ByteArray to NSData
Hope this helps