Status Update
Comments
jb...@google.com <jb...@google.com>
do...@google.com <do...@google.com> #2
I was able to reproduce this. The runtime error when tapping "Login2" is:
java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri=android-app://androidx.navigation/com.example.hellonavigation.RegisterUser/ } cannot be found in the navigation graph ComposeNavGraph(0x0) startDestination={Destination(0x21b61f8) route=com.example.hellonavigation.LoginSelector}
It looks like the deeplink request URI is missing a trailing / to indicate that the value of email
is an empty string.
cl...@google.com <cl...@google.com> #3
Base Navigation does not allow empty strings for path parameters, because path parameters are required.
val route = "example.com/{firstName}/{lastName}"
val firstName = ""
val lastName = "Doe"
navController.navigate("example.com/$firstName/$lastName")
In the example, you'd be navigating with example.com//Doe
, which is not a valid URL.
In safe args, email
is non-nullable and therefore a path parameter.
data class RegisterUser(val email: String)
In alignment with base navigation, email
cannot be an empty string.
Though maybe we could improve the error messaging.
st...@warting.se <st...@warting.se> #4
And with that it seems like
val androidUri = android.net.Uri.parse("android-app://androidx.navigation/com.example.hellonavigation.RegisterUser//Doe")
androidUri.pathSegments // {'com.example.hellonavigation.RegisterUser', 'Doe'}
androidUri.path!!.split("/") // {'', 'com.example.hellonavigation.RegisterUser', '' , 'Doe'}
cl...@google.com <cl...@google.com> #5
I understand the desire for support on empty strings in path. When originally debating support for this in safe args, the sources you linked were part of our references.
Re: //
was valid path. Would you like to elaborate on how you got your conclusion?
Furthermore Uri.getPathSegments
ignores the //
and does not include ""
in its return value. However I couldn't find an open bug on this stating that the omission was unintended.
With that said, in the spirit of type safety I think it makes sense to support empty strings for path parameter since it is a valid String value. Type safety implementation delegates to deeplinks, so this support will apply to String routes as well.
st...@warting.se <st...@warting.se> #6
path-abempty = *( "/" segment )
ap...@google.com <ap...@google.com> #7
Branch: androidx-main
commit 77e7bde966e9c9efc0d2f3a1afb0f697802cf57c
Author: Clara Fok <clarafok@google.com>
Date: Tue Jun 18 11:20:37 2024
Add support for empty path strings
Previously, empty strings in path arguments are not allowed since path arguments are considered as required, and Uri.getPathSegments() does not return empty strings.
However, support for empty path strings is added since empty string is a valid string value in the world of type safety apis.
Test: ./gradlew navigation:navigation-common:cC
Test: ./gradlew navigation:navigation-runtime:cC
Bug: 339481310
Relnote: "Navigation now supports navigating with empty strings in path arguments."
Change-Id: Ic5dbdd3b4786f62f4544a70b6786a269da8a37cc
M navigation/navigation-common/src/androidTest/java/androidx/navigation/NavDeepLinkTest.kt
M navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt
M navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
cl...@google.com <cl...@google.com> #8
Fixed internally and will be available in navigation 2.8.0-rc01
pr...@google.com <pr...@google.com> #9
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.navigation:navigation-common:2.8.0-beta04
androidx.navigation:navigation-runtime:2.8.0-beta04
Description
Version used: 2.8.0-alpha08
If passing an empty string as an safe argument the destination does not exists
@Serializable
object LoginSelector
@Serializable
data class RegisterUser(val email: String)
NavHost(
navController = navController,
startDestination = LoginSelector,
modifier = modifier
) {
composable<LoginSelector> {
Button(onClick = {
navController.navigate(RegisterUser("foo")) // Works!
}) {
Text(text = "Login1")
}
Button(onClick = {
navController.navigate(RegisterUser("")) // Doesn't work. Destination does not exists
}) {
Text(text = "Login2")
}
Button(onClick = {
navController.navigate(RegisterUser(null)) // works if making email nullable
}) {
Text(text = "Login3")
}
}
composable<RegisterUser> { backStackEntry ->
val emailLogin: RegisterUser = backStackEntry.toRoute()
Text(text =emailLogin.email)
}
}