ubuntudroid
11/01/2023, 3:16 PMCasey Brooks
11/01/2023, 3:34 PMBallastDebuggerClientConnection
instance for both VMs? If not, you’ll probably end up with 2 connections instead of 2 VMs in the same connectionubuntudroid
11/01/2023, 3:42 PMCasey Brooks
11/01/2023, 7:00 PMubuntudroid
11/01/2023, 7:02 PMCasey Brooks
11/01/2023, 7:06 PMubuntudroid
11/01/2023, 7:07 PMCasey Brooks
11/01/2023, 7:11 PMubuntudroid
11/01/2023, 7:12 PMCasey Brooks
11/02/2023, 5:40 PMBackstackChanged
event does get updated with every change to the Router state, but it is not itself intended to be considered State because it’s not actually fully in sync with the state. Only the VMs StateFlow can accurately represent the state.
The order of events using this redirect is causing the issue. Here’s the general sequence:
1. The app loads, and an Input for the initial route is sent
2. The Input gets processed and a new State is generated, which includes that route. Following the state change, a BackstackChanged
event is also sent to the EventHandler.
3. The Compose UI immediately updates, and it loads the screen corresponding to the route.
4. In parallel to the UI, the EventHandler eventually receives the BackstackChanged
event and sends an Input to redirect to the login screen. This redirect is working fine. But notably, this event is handled at some point after the UI state has already changed. As a result, there was a brief time when the router state shows the initial route as the current route, and during that time the screen gets briefly displayed and its VM starts running.
5. That VM sends an Input to initialize itself, which fetches data from a Repository in a sideJob. But since the user is not authenticated, an error is thrown by the repository, because it doesn’t have any user credentials.
6. Shortly after this, the redirect is processed and the user is sent back to the Login screen. This cancels the coroutineScope that the screen’s VM was running in, and I believe it’s getting cancelled before the VM can notify the debugger that the sideJob failed. Thus, the debugger appears to hang, because it never received a notification that the sideJob completed with an error.
The problem is that the UI is still running and displaying UI without caring whether the user is authenticated or not. What should be done instead is include the authentication/redirection logic directly in the Compose UI, not an EventHandler. This will allow you to actually hide UI that requires auth (and thus prevent those VMs from running at all), while also handling the redirect.public object AuthRequired : RouteAnnotation
enum class Screen(
routeFormat: String,
override val annotations: Set<RouteAnnotation> = emptySet(),
): Route {
AuthenticatedRoute("/pastes", annotations = setOf(AuthRequired)),
Login("/login"),
;
override val matcher: RouteMatcher = RouteMatcher.create(routeFormat)
}
routerState.renderCurrentDestination(
route = { screen ->
val displayRoute = @Composable {
when(screen) {
Screen.Pastes -> {
PastesScreen.Content(router)
}
Screen.Login -> {
LoginScreen.Content(router)
}
}
}
val screenRequiresAuth = AuthRequired in annotations
if(screenRequiresAuth) {
val isAuthenticated = remember(screen) {
userRepository.isAuthenticated
}
if(isAuthenticated) {
displayRoute()
} else {
LaunchedEffect(screen) {
router.trySend(RouterContract.Inputs.GoToDestination(Screen.Login.directions().build()))
}
}
} else {
displayRoute()
}
},
notFound = {
// TODO add "not found" screen
}
)
ubuntudroid
11/02/2023, 6:03 PMCasey Brooks
11/02/2023, 6:37 PMubuntudroid
11/02/2023, 7:03 PM