Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

KMP Navigation error: could not find any NavType for argument SearchType (works on Android, crashes on iOS in Xcode)

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

  • Both SearchBoxScreen and SearchType are marked @Serializable.
  • On Android, SearchType is correctly passed through navigation as an argument.
  • On iOS, it looks like the enum is not being registered as a supported NavType.

My question

  • Why does SearchType work as a navigation argument on Android but fails on iOS in a KMP project?
  • Do I need to define a custom NavType for enums in Kotlin Multiplatform? If so, how should I implement it?
like image 745
Pawandeep Singh Avatar asked Oct 29 '25 18:10

Pawandeep Singh


1 Answers

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.

like image 146
BenjyTec Avatar answered Nov 01 '25 09:11

BenjyTec