https://kotlinlang.org logo
#touchlab-tools
Title
# touchlab-tools
j

Jan

11/13/2023, 10:33 AM
Hello everyone, I already posted my question in a different channel and received a suggestion that this one is more suitable for it. Also thanks to @Jon Bailey for providing feedback! I'm Jan Bej-Radzikowski and I'm Android Developer in Payback. Recently we decided to check out the SKIE library, but we stumbled across a bug that stopped us from making further progress. We wanted to expose a repository to iOS that returns a Flow. This is a code that we have in a shared module inside Android project. The issue is that on the iOS side, our Flow doesn't simply convert to the
SkieSwiftFlow<FirstLevel<List<SomeItem>>>
but instead we're getting
SkieSwiftFlow<FirstLevel<NSArray>>
Android code:
Copy code
fun getFlowTwoLevels(): Flow<FirstLevel<List<SomeItem>>>

sealed class FirstLevel<out T> {
    data class Success<T>(
        val secondLevel: SecondLevel<T>
    ): FirstLevel<T>()
}

sealed class SecondLevel<out T> {
    data class Success<T>(
        val result: T,
    ): SecondLevel<T>()

    data class Error(val error: String): SecondLevel<Nothing>()
}
If you'd like to reproduce the bug, please use the repo linked below. It contains Android and iOS demo projects. https://github.com/JasiekRadzik/KMP_SKIE_NSArray/tree/main
f

Filip Dolník

11/13/2023, 10:38 AM
Hi! The transformation of
List<SomeItem>
to
NSArray
in generics is an expected behavior / limitation of the Obj-C to Swift interop. You would get the same result if your function returned just
FirstLevel<List<SomeItem>>
without the Flow (and even without SKIE). The problem is that Obj-C generics require the type argument to be a reference type which Swift.Array (
[SomeItem]
) is not. Note that the type is not coverted to
List<SomeItem>
because that’s a Kotlin only type that doesn’t exist in Obj-C / Swift and instead is translated by the Kotlin compiler to NSArray.
j

Jon Bailey

11/13/2023, 11:04 AM
Thanks for putting that in a much better way and more succinctly than I did! It's annoying that we don't have a KotlinList like we have KotlinBoolean for similar cases with Boolean/Bool
f

Filip Dolník

11/13/2023, 11:12 AM
Unfortunately, that wouldn’t solve the issue either. List in Kotlin is an interface, and those loose generics completely - so you would just end up with
KotlinList
instead of
NSArray
which is actually worse. The Kotlin compiler could in theory export it as a class instead but there are some other problems with this approach. Mainly that Swift does not support covariance for custom generic types which is a big problem for collection types. (Swift collection types like
Array
are covariant because they have special support in the Swift compiler which the Kotlin compiler cannot use.)
j

Jacob Ras

11/14/2023, 12:13 PM
Would it work if you did something like this?
Copy code
fun getFlowTwoLevels(): Flow<FirstLevel<SomeItemList>>

class SomeItemList(val items: List<SomeItem>)
f

Filip Dolník

11/14/2023, 12:16 PM
yeah, this should preserve types and you might even get away with:
Copy code
fun getFlowTwoLevels(): Flow<FirstLevel<CustomList<SomeItem>>>

class CustomList<T>(val items: List<T>)
j

Jacob Ras

11/14/2023, 12:16 PM
Reminds me a lot of the Compose approach to maintain stability 🙂 Thanks!
j

Jan

11/14/2023, 2:00 PM
Thanks @Filip Dolník, appreciate the help! I'll try that approach