layout | title | permalink |
---|---|---|
page |
Add State-based navigation |
/rfc/0010-add-state-based-navigation |
- Start date: 2023-10-17
- RFC PR: 4126
Following the acceptance of 0009 - Remove Interactors and Controllers, Fenix should have a method of navigation that is tied to the lib-state
model to provide a method of handling navigation side-effects that is consistent with architectural goals.
Currently, methods of navigation throughout the app are varied. The SessionControlController
provides 3 examples alone:
HomeActivity::openToBrowserAndLoad
- Calling a
NavController
directly - Callbacks like
showTabTray()
To move to a more consistent Store
model, we need a way for features to fire Action
s and have that result in navigation. This has the added benefit of decoupling our business logic from Android platform implementation details.
There are two cases to consider for navigation state: the currently displayed fragment and transient UI like CFRs, dialogs, etc.
For screens: add a Screen
property to AppStore
and react to changes by observing them in a navigation AbstractBinding
hosted by the HomeActivity
. This is roughly equivalent to the Navigator
implementation in Focus.
Handling this State in AppStore should allow us to have a consistent touch point for all navigation since this Store is globally accessible.
For transient state: For now, I am proposing to implement feature- or screen-specific middleware that consume actions to directly tie them to their side-effects.
One big alternative consideration is: do we want to track nav State in the Store at all? It is potentially error-prone or repetitious of other code (nav-graph and the code that actually invokes the NavController). The main alternative would be to handle navigation in middleware as a reaction to Action
s, but options for this come with there own set of challenges:
- Global navigation middleware attached to the AppStore. This is made more difficult because we do not have access to an
Activity
context (to retrieve aNavController
from) when instantiating theAppStore
inComponent
s. We could create a mutable getter/setter for the Middleware's NavController, but this carries risks for things like race conditions, public mutability, and null accesses if the Middleware held a dangling reference to a null activity after destructive lifecycle events. - Individual navigation middleware attached to each Fragment's Store. This carries risk of repetition and boilerplate.
- Focus handles some cases of this similarly to screens. There are examples in of CFRs in Focus' AppState for example. This is potentially more in line with the proposal to add a
Screen
property, but it carries its own risks:- State must be consumed correctly, or configuration changes can cause effects like dialogs or CFRs to erroneously re-display.
- It suffers from the main drawback of the
Screen
proposal, in that we introduce additional code to track what should be treated as a side-effect (and therefore handled by middleware).