Hi! I have a sort of splash/authentication screen ...
# compose-destinations
d
Hi! I have a sort of splash/authentication screen at the root of the NavGraph, and a HomeScreen that appears afterwards... for some reason even though I put:
Copy code
val context = LocalContext.current
    BackHandler(enabled = true) {
        if (!navigator.popBackStack()) {
            (context as MainActivity).finish()
        }
    }
in my HomeScreen composable, it takes a bunch of back presses to exit the app... how do I handle this in compose directions?
r
Also doesn't seem related to compose destinations directly. What usually helps me is logging the contents of the back stack.
s
Probably does not have something special to do with compose-destinations here, but you can follow the suggestions that androidx.navigation suggests in the first place?
d
you can follow the suggestions that androidx.navigation suggests in the first place?
Which suggestions are you referring to?
logging the contents of the back stack.
Yeah, I guess I'll have to do that, I was just wondering if this is maybe a known problem...
Rafael himself has also done this https://github.com/raamcosta/compose-navigation-login which you can take inspiration from perhaps?
r
> logging the contents of the back stack.
Yeah, I guess I'll have to do that, I was just wondering if this is maybe a known problem...
I usually always have this on debug builds at least. Really helps me understand whats going on. Sometimes there's something I wasn't expecting 🙂
The API I use to get the back stack is restricted, but in debug builds is fine? 😅
d
How do you do it?
r
Copy code
@SuppressLint("RestrictedApi")
@Composable
fun LogBackStack(navController: NavController) {
    LaunchedEffect(navController) {
        navController.currentBackStack.collect {
            it.print()
        }
    }
}

fun Collection<NavBackStackEntry>.print(prefix: String = "stack") {
    val stack = toMutableList()
        .map {
            val route = it.route()
            val args = runCatching { route.argsFrom(it) }.getOrNull()?.takeIf { it != Unit }?.let { "(args={$it})" } ?: ""
            "$route$args"
        }
        .toTypedArray().contentToString()
    println("$prefix = $stack")
}
👍🏼 1
you can remove the args part if that becomes too noisy in the logs and you don't care about that.
d
Thanks! I'll look into all these points... they should be pretty helpful to finally find this problem!
Just wondering though... where's the best place to put that logger @Rafael Costa?
r
Probably above DestinationsNavHost call
👍🏼 1
d
image.png
argsFrom is red...?
r
hmm.. that is from v2 😛
if you don't care about nav args in the log, remove that line
actually, let me check how it is on v1
Copy code
val stack = toMutableList()
        .map { it.route().route }
you won't have args, but that is probably fine 😛
d
What's v2, Compose Directions? Also, btw, it might be nice from Kotlin 1.9 to start generating `data object`s for destination objects... the toString() there is cleaner.
r
v2 Compose Destinations
and yes, on v2 they are data objects 🙂
v2 is still not out, but I'm actively working on it
🎉 1
🚀 1
d
Interesting, after going directly to the deep link (the first log printed), it prints another time that it went to the home activity, and auth screen (I changed the home to be the root and call auth from there), and then prints another line that it goes back to home... what does it mean when it prints another line?
r
not sure what you mean, can you paste here the logs?
d
Where should I put the authentication code if I'm using deep links with DestinationsNavHost, if I put it in the home activity, it won't go through authentication when using deep links... maybe that's my problem?
I can maybe post the logs in a DM if still necessary... but maybe this thing about authentication might be causing the problem.
r
> it takes a bunch of back presses to exit the app At least now you should be able to se what happens with each of these back presses
d
That works now... when I launch the authentication screen from the home screen instead of making it the root of the graph, but I still have the second problem
r
but yes, check the links @Stylianos Gakis posted here. I think you may find them helpful. I think they specifically say that login screens should NOT be the start destination. Then you have my github repo where I use a wrapper to show the login UI in any screen and make login not an actual screen registered in any nav graph. (if I remember correctly). I don't have a lot of context of your issue though, so only you can figure it out 😄
d
So it has to be outside of the nav graph then? There's no place to put code that will be called before navigating to any screen (is that maybe the wrappers param in the Destination annotation?)
r
nevermind I actually do navigate to a Login screen in this repo 😛
so it is part of the nav graph, but it is not the start destination!
s
You got some options yeah. The one thing that is for sure is that it has not not be the start destination. If it’s part of your graph, when you navigate to it, as you do it you gotta pop the entire backstack before you go there so that if you press back you end up outside of your app instead of back to where you came from If it’s outside for your NavHost completely, that could theoretically work too I think, but then you are outside of navigation land, go crazy If it’s a wrapper around all of your routes that you do want to be behind authentication, then deep links are easy to support, as going back will already just take you out of the app, since a deep link only creates the destination you are going to (if you do not do navigateUp()`. But non-deep link scenarios may get more tricky as then if you come back form process death into a deep nav backstack, what do you do if you are logged out? Pressing back would start popping that backstack, and all the routes would show the same login screen, not a good UX. You’d have to do something else but maybe there’s a nice solution that I am not thinking of here.
1
d
If it’s part of your graph, when you navigate to it, as you do it you gotta pop the entire backstack before you go there so that if you press back you end up outside of your app instead of back to where you came from
It's not an authentication that a user does, but rather a type of loading screen that does authentication with credentials already provided. If creds are invalidated, then it goes to another activity anyways...
s
If it’s a completely different activity, then you don’t need any of this. At the onCreate of your activity just observe those credentials, and simply finish activity and navigate to the other one. We do exactly this here https://github.com/HedvigInsurance/android/blob/53931cb97c25b2f2cf94b213cfcc7b6ddb[…]n/kotlin/com/hedvig/app/feature/loggedin/ui/LoggedInActivity.kt And we rely on the splash APIs to show that while we are still evaluating the first situation of if they are logged in or not. https://github.com/HedvigInsurance/android/blob/53931cb97c25b2f2cf94b213cfcc7b6ddb[…]n/kotlin/com/hedvig/app/feature/loggedin/ui/LoggedInActivity.kt Then we do nothing on the NavHost itself regarding authentication. This is something I want to change soon anyway since I do not want the extra activity, but since you are already using it, might as well take the easy road for now.
d
Interesting... thanks for all the links and examples! All this is very helpful!