I’m working on a Kotlin Multiplatform (KMP) project with navigation. I have the following setup:
@Serializable
data class SearchBoxScreen(
val searchType: SearchType,
) : Screen()
@Serializable
enum class SearchType(val value: String) {
STATIONS("Stations"),
STATION("Station"),
ALL("All")
}
Nav host:
NavHost(
navController = navCtl,
modifier = Modifier.padding(padding),
startDestination = Screen.HomeScreen,
enterTransition = { fadeIn(animationSpec = tween(0)) },
exitTransition = { fadeOut(animationSpec = tween(0)) },
popEnterTransition = { fadeIn(animationSpec = tween(0)) },
popExitTransition = { fadeOut(animationSpec = tween(0)) }
) {
composable<SearchBoxScreen> { backStackEntry ->
val args = backStackEntry.toRoute<SearchBoxScreen>()
SearchScreen(args = args, navController = navCtl)
}
}
When I run the project on Android, navigation works fine. But when I run the same project in Xcode (iOS), the app crashes with this error:
Uncaught Kotlin exception: kotlin.IllegalArgumentException:
Route org.example.project.presentation.navigation.Screen.SearchBoxScreen
could not find any NavType for argument searchType of type SearchType - typeMap received was {}
What I’ve tried
My question
Yes, you will need a custom converter to create a NavType for your enum.
First, create a generic anonymous class like below:
inline fun <reified T : Any> serializableType(
isNullableAllowed: Boolean = false,
json: Json = Json,
) = object : NavType<T>(isNullableAllowed = isNullableAllowed) {
override fun put(bundle: SavedState, key: String, value: T) {
bundle.write { putString(key, json.encodeToString(value)) }
}
override fun get(bundle: SavedState, key: String): T? {
return json.decodeFromString<T?>(bundle.read { getString(key) })
}
override fun parseValue(value: String): T = json.decodeFromString(value)
override fun serializeAsValue(value: T): String = json.encodeToString(value)
}
Then, on each destination in your NavHost where you are passing in an enum via arguments, register a typeMap:
composable<SearchBoxScreen>(
typeMap = mapOf(
typeOf<SearchType>() to serializableType<SearchType>()
)
) { backStackEntry ->
val args = backStackEntry.toRoute<SearchBoxScreen>()
SearchScreen(args = args, navController = navCtl)
}
Then, your code should work both on Android and iOS.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With