Reactive State Management
Reactive State Management
RideEase utilizes the Model-View-ViewModel (MVVM) architectural pattern combined with Android LiveData to handle asynchronous data streams. This reactive approach ensures that the UI automatically reflects the current state of the application—such as authentication status, loading states, or error messages—while remaining decoupled from the underlying business logic.
Core Architecture
The state management lifecycle in RideEase follows a unidirectional flow:
- View (Activity/Fragment): Triggers an action (e.g., clicking a "Login" button).
- ViewModel: Processes the action, interacts with Firebase services, and updates internal
MutableLiveData. - LiveData: Emits the updated state to the View.
- View: Observes the change and updates the UI accordingly.
Observable State Components
AuthViewModel
The AuthViewModel is the primary hub for managing authentication and profile state. It exposes the following LiveData streams:
| LiveData Method | Type | Description |
| :--- | :--- | :--- |
| getAuthResult() | LiveData<AuthResult> | Emits an object containing the success status and the userId upon completion of auth tasks. |
| getErrorMessage() | LiveData<String> | Streams human-readable error messages from Firebase or validation logic. |
| getUserProfile() | LiveData<User> | Provides the current user's profile details (name, email, phone) once retrieved from Firestore. |
UserDetailsViewModel
Specialized for handling phone-based authentication and verification:
| LiveData Method | Type | Description |
| :--- | :--- | :--- |
| getVerificationId() | LiveData<String> | Stores the Firebase Phone Auth verification ID needed for OTP submission. |
| getAuthResult() | LiveData<Boolean> | Simple success/failure flag for phone verification completion. |
Implementation Usage
Observing State in the View
Activities subscribe to these streams during their onCreate lifecycle. This allows the UI to stay in sync with the data without manual polling.
// Example usage in LoginActivity.java
viewModel = new ViewModelProvider(this).get(AuthViewModel.class);
// Observe authentication success/failure
viewModel.getAuthResult().observe(this, authResult -> {
if (authResult.isSuccess()) {
// Handle navigation or local state updates
navigateToHome(authResult.getUserId());
}
});
// Observe error messages for UI feedback
viewModel.getErrorMessage().observe(this, error -> {
Toast.makeText(this, error, Toast.LENGTH_LONG).show();
hideLoading(); // Reset UI state on error
});
Triggering State Changes
State changes are initiated by calling public methods on the ViewModels. These methods are non-blocking and return void, as the result is delivered via the observed LiveData.
// Sign up a new user
viewModel.signUpWithEmail(email, password, "rider");
// Update profile details
viewModel.updateUserDetails(userId, "John Doe", "+1 1234567890");
Handling Asynchronous States
RideEase uses specific patterns to manage the transition between different UI states during long-running operations:
- Loading State: Before calling a ViewModel method, the Activity usually triggers a "Loading" UI (e.g., Lottie animations). The ViewModel updates
ErrorMessageorAuthResultonce the background task (Firebase/Firestore) completes, signalling the Activity to hide the loading indicator. - Validation State: ViewModels perform pre-flight validation. If input (like an email or phone number) is invalid, the
ErrorMessageLiveData is updated immediately, allowing the UI to react without making a network request. - Lifecycle Awareness: Because RideEase uses
LiveData, observers are automatically cleaned up when an Activity is destroyed. This prevents memory leaks and ensures that UI updates are only attempted when the Activity is in an active state (STARTEDorRESUMED).
Data Models
State is primarily encapsulated in the following models:
AuthResult: A wrapper containing abooleansuccess flag and aStringuserId.User: A POJO representing the Firestore user document, includinguserType,fullName, andisPhoneVerified.