raamcosta / compose-destinations

Annotation processing library for type-safe Jetpack Compose navigation with no boilerplate.
https://composedestinations.rafaelcosta.xyz
Apache License 2.0
3.14k stars 129 forks source link

Crash when using Parcelable class as navigation argument #653

Open SkashEU opened 1 week ago

SkashEU commented 1 week ago

I'm experiencing a crash when using the Android Parcelable class as a navigation argument in the library. It seems that the generated NavType class code has a bug. Specifically, the function parseValue is called twice: once with the correct value and once with the key as the value, which leads to the crash.

Im using the version: 2.1.0-beta09

Steps to Reproduce:

  1. Define a Parcelable class to be used as a navigation argument.
  2. Use the Parcelable class as a navigation argument in the library.
  3. Observe the crash during navigation.

Expected Behavior:

The navigation should succeed without any crashes, with the parseValue function being called correctly with the intended value.

Actual Behavior:

The parseValue function is called twice:

  1. The first call is with the correct value.
  2. The second call is with the key as the value, causing the crash.
raamcosta commented 1 week ago

Hi 👋

This is definitively not an issue general to parcelable nav arguments given that I have plenty of working examples with them.

Can you share more about how the class looks like and how you are navigating? As a side note, the function “parseValue” is not called by us, official library is orchestrating that.

raamcosta commented 1 week ago

Also, add the crash stacktrace if possible 🙏

SkashEU commented 1 week ago

Hey, thanks for the fast reply!

Alright, thanks for the info. That's good to know. Ye I tried it with multiple different classes but I can surely share an example.

@Parcelize
    actual class Filter actual constructor(
        val selectedFilterTagIds: List<Int>
    ) : Parcelable

That's my target composable

@Destination<RecipeCategoryGraph>(
    start = true,
    visibility = CodeGenVisibility.INTERNAL
)
@Composable
internal fun RecipeCategoryScreen(
    filter: Filter,
    navigator: DestinationsNavigator,
    viewModel: RecipeCategoryViewModel = viewModel { RecipeCategoryViewModel(filter) }
)

That's my navigate call

    RecipeCategoryNavGraph(
                        navArgs = RecipeCategoryScreenDestinationNavArgs(
                            Filter(result.value.selectedTagIds)
                        )
                    )
SkashEU commented 1 week ago

Also, add the crash stacktrace if possible 🙏

Ye, sure:

java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
    at java.util.Collections$SingletonList.get(Collections.java:5042)
    at com.ramcosta.composedestinations.navargs.parcelable.DefaultParcelableNavTypeSerializer.fromRouteString(DefaultParcelableNavTypeSerializer.kt:28)
    at com.ramcosta.composedestinations.navargs.parcelable.DefaultParcelableNavTypeSerializer.fromRouteString(DefaultParcelableNavTypeSerializer.kt:19)
    at com.ramcosta.composedestinations.generated.recipecategory.navtype.RecipeSearchTypeNavType.parseValue(RecipeSearchTypeNavType.kt:33)
    at com.ramcosta.composedestinations.generated.recipecategory.navtype.RecipeSearchTypeNavType.parseValue(RecipeSearchTypeNavType.kt:16)
    at androidx.navigation.NavType.parseAndPut(NavType.kt:95)
    at androidx.navigation.NavDeepLink.parseArgument(NavDeepLink.kt:361)
    at androidx.navigation.NavDeepLink.getMatchingPathArguments(NavDeepLink.kt:261)
    at androidx.navigation.NavDeepLink.getMatchingArguments(NavDeepLink.kt:194)
    at androidx.navigation.NavDestination.matchDeepLink(NavDestination.kt:403)
    at androidx.navigation.NavDestination.matchDeepLink(NavDestination.kt:381)
    at androidx.navigation.NavGraphNavigator.navigate(NavGraphNavigator.kt:88)
    at androidx.navigation.NavGraphNavigator.navigate(NavGraphNavigator.kt:59)
    at androidx.navigation.NavController.navigateInternal(NavController.kt:271)
    at androidx.navigation.NavController.navigate(NavController.kt:2040)
    at androidx.navigation.NavController.navigate(NavController.kt:1973)
    at androidx.navigation.NavController.navigate(NavController.kt:2400)
    at com.ramcosta.composedestinations.navigation.DestinationsNavController.navigate(DestinationsNavController.kt:33)
    at com.ramcosta.composedestinations.navigation.DestinationsNavigator$DefaultImpls.navigate$default(DestinationsNavigator.kt:52)
    at com.example.RecipesScreenKt$RecipesScreen$1$1.invoke(RecipesScreen.kt:53)
    at com.example.RecipesScreenKt$RecipesScreen$1$1.invoke(RecipesScreen.kt:49)
    at com.ramcosta.composedestinations.result.ResultRecipientImpl.handleResultIfPresent(ResultRecipientImpl.kt:66)
    at com.ramcosta.composedestinations.result.ResultRecipientImpl.access$handleResultIfPresent(ResultRecipientImpl.kt:17)
    at com.ramcosta.composedestinations.result.ResultRecipientImpl$onNavResult$1$observer$1.onStateChanged(ResultRecipientImpl.kt:36)
    at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.jvm.kt:320)
    at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.jvm.kt:198)
    at com.ramcosta.composedestinations.result.ResultRecipientImpl.onNavResult$lambda$2(ResultRecipientImpl.kt:48)
    at com.ramcosta.composedestinations.result.ResultRecipientImpl.$r8$lambda$QiPsutwrk8k00_u_HUuef2cXYxg(Unknown Source:0)
    at com.ramcosta.composedestinations.result.ResultRecipientImpl$$ExternalSyntheticLambda0.invoke(Unknown Source:6)
    at androidx.compose.runtime.DisposableEffectImpl.onRemembered(Effects.kt:83)
    at androidx.compose.runtime.CompositionImpl$RememberEventDispatcher.dispatchRememberObservers(Composition.kt:1364)
    at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:992)
    at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:1013)
    at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:678)
    at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:578)
    at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:41)
    at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
    at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
    at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1397)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1408)
    at android.view.Choreographer.doCallbacks(Choreographer.java:1008)
    at android.view.Choreographer.doFrame(Choreographer.java:934)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1382)
    at android.os.Handler.handleCallback(Handler.java:959)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loopOnce(Looper.java:232)
    at android.os.Looper.loop(Looper.java:317)
    at android.app.ActivityThread.main(ActivityThread.java:8501)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)