Open JustJerem opened 2 years ago
Hi 👋
Thank you for the kind words, glad you like it!
I believe this can be due to ksp task running before the build config class is created when you build the app. But I see how good this could be, I’ll leave this open as a future enhancement and I’ll try to check if I can solve it somehow 🤔
In the meantime, one thing you can do is, right before calling DestinationsNavHost, you copy your root NavGraph (since it is a data class) and reach to the Destination you want to add the deep link, then call this (line 112 - withDeepLink).
This will create a copy of the generated Destination with the passes in deep link. And this way you can set deep links at runtime.
Remember you have to change the NavGraph you pass to DestinationsNavHost, and replace the destination with this copy of itself in the destinations
list that the NavGraph contains. If you want I can show you an example later on.
This method was not intended for this specific use case but I believe it should work, but please let me know 🙂
Hiya! @raamcosta Can you show us an example please? I don't quite understand what you mean.
I have an app with multiple build flavors, and the scheme for the deeplinks is based on the build.FLAVOR.
I wrote this code based on your answer. Works for a little bit, until I open a notification with a deeplink. Then it throws the following exception:
java.lang.IllegalArgumentException: Registering multiple navigation graphs with same route ('newRoot') is not allowed.
const val GENERAL_ALERT_DEEPLINK_URL = "${BuildConfig.FLAVOR}://generalAlert"
const val ALERT_DEEPLINK_URL = "${BuildConfig.FLAVOR}://alert/{alertId}"
private var navGraph: NavGraph? = null
internal fun regenerateRootNavGraph(): NavGraph {
if (navGraph == null) {
val newRoot = NavGraphs.root.copy(route = "newRoot")
val destinations = newRoot.destinations.toMutableList()
destinations.remove(SingleAlertDetailScreenDestination)
destinations.remove(GeneralAlertDetailsDestination)
SingleAlertDetailScreenDestination.withDeepLink {
uriPattern = ALERT_DEEPLINK_URL
}.routedIn(newRoot)
GeneralAlertDetailsDestination.withDeepLink {
uriPattern = GENERAL_ALERT_DEEPLINK_URL
}.routedIn(newRoot)
navGraph = newRoot.copy(destinations = destinations)
}
return navGraph!!
}
Hi, you can try adding the code below to your build.gradle, it should make the BuildConfig class available to KSP.
ksp {
allowSourcesFromOtherPlugins = true
}
Hi, you can try adding the code below to your build.gradle, it should make the BuildConfig class available to KSP.
ksp { allowSourcesFromOtherPlugins = true }
I get this build error when using this option:
Circular dependency between the following tasks:
:app:kaptDebugKotlin
\--- :app:kaptGenerateStubsDebugKotlin
\--- :app:kspDebugKotlin
\--- :app:kaptDebugKotlin (*)
@cvb941 - Where you able to get it working? If so, mind sharing a few snippets of the code?
Sorry, I just enabled that option and it worked, nothing special. I am not using kapt though.
@ryanholden8 Did you find a solution or maybe a workaround? I think my project requires kapt for Hilt, and ksp for destinations. I also need to set ksp. allowSourcesFromOtherPlugins to true because we have build flavors and we need deeplinks based on build flavors.
I feel like there must be a way to have ksp_gradleTask.dependsOn(BuildConfig_gradleTask), but I'm not very familiar with ksp and gradle.
@Miiel - We did not, we duplicated the references. Not ideal but not much of a choice.
@raamcosta I have many issues with this. Did you solve it?
How can I help you to solve it to send Pull Request?
The way I solved this was to register a single pattern as documented, using an app scheme, for example:
Screen @Destination annotation
deepLinks = [
DeepLink(
uriPattern = "myapp://groups/join?token={token}",
),
]
Then, in the MainActivity, I used traditional url- handling to transform environment (prod, dev, acc) specific https urls in to the registered myapp:// pattern and feed that into the navigator
MainActivity kotlin:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberAnimatedNavController()
DeeplinkHandler(navController)
AppNavigation(
navController = navController,
navGraph = NavGraphs.root,
startRoute = viewModel.startRoute,
)
}
deeplinkManager.onNewIntent(intent) // important!
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
this.intent = intent
deeplinkManager.onNewIntent(intent)
}
DeeplinkManager
@Singleton
class DeeplinkManager @Inject constructor(
private val deeplinkParser: DeeplinkParser,
) {
private val _deeplinkFlow = MutableStateFlow(Uri.EMPTY)
val deeplinkFlow: Flow<Uri> = _deeplinkFlow
fun onNewIntent(intent: Intent?) {
Napier.d {
"onNewIntent received with data ${intent?.data} and extras ${
intent?.extras?.keySet()?.toList()
}"
}
if (intent == null) return
deeplinkParser.parseIntent(intent)?.let {
_deeplinkFlow.value = it
}
}
fun onDeeplinkHandled() {
_deeplinkFlow.value = Uri.EMPTY
}
}
DeeplinkParser: A class transforming https://<dev | acc | prod>.mycompany.com/join?token=ABCDEF
to myapp://join/token={token}
DeeplinkHandler
@Composable
private fun DeeplinkHandler(navController: NavHostController) {
val deeplink by deeplinkManager.deeplinkFlow.collectAsStateWithLifecycle(
initialValue = Uri.EMPTY
)
if (deeplink != Uri.EMPTY) {
Napier.d { "Navigating to deeplink $deeplink" }
navController.navigate(deeplink)
deeplinkManager.onDeeplinkHandled()
}
}
A bit late to the party, but just wanted to share a workaround that worked for me. Might be helpful to some folks out there.
I've basically used this suggestion:
ksp { allowSourcesFromOtherPlugins = true }
but with this extra step:
tasks.withType<com.google.devtools.ksp.gradle.KspTaskJvm> {
mustRunAfter("generateDebugBuildConfig")
}
This little block makes sure that BuildConfig
would've been generated by the time KSP starts to do its thing. Bear in mind that, depending on your particular case, there might be more tasks that need to be added to mustRunAfter
.
Of course, having an elegant out-of-the-box solution would be way better, but this is not too ugly for a workaround. Hope. this helps!
@raamcosta is it possible to improve error message in this situation? Error like following is kind of misleading.
java.lang.IllegalArgumentException: Deep link null can't be used to open destination Destination(0xa4735ba4) route=verify_email_username/{email}/{code}.
Following required arguments are missing: [email, code]
at androidx.navigation.NavDestination.addDeepLink(NavDestination.kt:355)
at androidx.navigation.compose.NavGraphBuilderKt.composable(NavGraphBuilder.kt:104)
at androidx.navigation.compose.NavGraphBuilderKt.composable$default(NavGraphBuilder.kt:78)
at com.ramcosta.composedestinations.spec.DestinationStyleKt.addActivityDestination(DestinationStyle.kt:158)
at com.ramcosta.composedestinations.DefaultNavHostEngine.composable(DefaultNavHostEngine.kt:123)
at com.ramcosta.composedestinations.DestinationsNavHostKt.addNavGraphDestinations(DestinationsNavHost.kt:115)
at com.ramcosta.composedestinations.DestinationsNavHostKt.access$addNavGraphDestinations(DestinationsNavHost.kt:1)
at com.ramcosta.composedestinations.DestinationsNavHostKt$addNestedNavGraphs$1$1$1.invoke(DestinationsNavHost.kt:142)
at com.ramcosta.composedestinations.DestinationsNavHostKt$addNestedNavGraphs$1$1$1.invoke(DestinationsNavHost.kt:141)
Does anyone know if this is still the case?
Does anyone know if this is still the case?
@raamcosta Is it supposed to be working? Haven't tried since I've put in place the workaround I've mentioned earlier
It is still the case, facing this
Hello Rafael, First of all, I would like to thank you for this great library. A real pleasure to use every day ! 👏
My issue is that I trying to use DeepLinks with a changing configuration, thanks to BuildConfig. The application compiles, but it crashes when launching on the phone with this error:
java.lang.IllegalStateException: The NavDeepLink must have an uri, action, and/or mimeType.
And indeed, looking in the generated code, nothing appears in the Deeplinks variable.
This works perfectly if I hardcode the URL in the right field, but when I pass it in the BuildConfig, it doesn't generate. And I need it because our application has 4 environments.
Current state :
DashboardScreen.kt
What I try to achieve :
build.gradle
DashboardScreen.kt
Is there a solution ? Sorry if I missed something in the documentation.