I'm fairly new to KMM/Swift and am struggling to cast sealed classes.
I have the following class:
data class DataPointState(val id: Long? = null){
sealed class MultiSelectOptions<T> {
data class Strings(val options: List<UserGeneratedString>) :
MultiSelectOptions<List<UserGeneratedString>>()
data class Lifts(val options: List<Lift>) : MultiSelectOptions<List<Lift>>()
}
data class EditState(
val value: InputData? = null
) {
sealed class InputData {
data class MultiSelectOptions(val list: DataPointState.MultiSelectOptions<*>) :
InputData()
//More data classes here
}
}
On the Android side, I can type check/cast my Input data to MultiSelectOptions, and then type check/cast to Lifts or Strings:
when (value) {
...
is DataPointState.EditState.InputData.MultiSelectOptions -> FlowRow(
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
value.list.let {
when (it) {
is DataPointState.MultiSelectOptions.Lifts -> it.options.forEach {
...
}
is DataPointState.MultiSelectOptions.Strings -> it.options.forEach {
...
}
}
}
}
But when I try to do the equivalent in Swift I get a warning Cast from 'DataPointStateMultiSelectOptions<AnyObject>' to unrelated type 'DataPointStateMultiSelectOptionsLifts' always fails
switch value {
case let value as DataPointState.EditStateInputDataMultiSelectOptions:
switch value.list {
case let list as DataPointStateMultiSelectOptionsLifts :
Text("test")
default:
EmptyView()
}
}
Any help figuring this out is greatly appreciated!
Swift doesn't have an equivalent of the star project you used in MultiSelectOptions<*>
.
It turns out MultiSelectOptions<*>
got translated to MultiSelectOptions<AnyObject>
, kind of similar to MultiSelectOptions<Any>
in Kotlin. As you may know, MultiSelectOptions<Any>
is unrelated to MultiSelectOptions.Lifts
, so that's why you got the warning.
As far as I know, Kotlin interops with Swift by turning its generics to Objective-C's lightweight generics first. At this point, MultiSelectOptions<*>
is turned into MultiSelectOptions<id>
. And finally it gets imported into Swift as MultiSelectOptions<AnyObject>
.
Objective-C's lightweight generics are also "erased" similar to how Kotlin's are. There is no runtime mechanism to enforce that MultiSelectOptions<AnyObject>
can't be a MultiSelectOptions<[Lift]>
.
If you cast to AnyObject
first, and then cast to Lift
, you can bypass Swift's strict enforcement of generic types.
switch value.list as AnyObject {
case let list as DataPointStateMultiSelectOptionsLifts:
Text("test")
default:
EmptyView()
}