-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Architectural Revamp #99
base: develop
Are you sure you want to change the base?
Conversation
This commit refactors the initialization of viewState in components by extracting it from the `onStart` method and assigning it directly to the `viewState` property. This change aims to improve clarity and maintainability of the code. Additionally, a new `whenStarted` extension function is introduced in the BaseComponent class. This function allows components to perform actions when their lifecycle starts while providing a StateFlow representation of their viewState. This further simplifies the initialization process and improves readability.
…avEntry interfaces These changes simplify the codebase by removing unnecessary abstractions.
WalkthroughThe pull request introduces a series of changes across multiple files, primarily focusing on the workflows, navigation components, and view models within both Android and iOS applications. Key modifications include updates to GitHub Actions workflows for Android releases, which now feature enhanced conditional checks and clearer comments regarding project setup tasks. The Android build configuration has been refined with the addition of a new In the UI layer, significant refactoring has occurred, particularly in the navigation structure, with new composable functions introduced for home and profile navigation. The architecture has shifted from MVVM to an MVI-like structure, emphasizing a more streamlined approach to state management and UI interactions. The README has been updated to reflect these architectural changes. Additionally, several components, such as Sequence Diagram(s)sequenceDiagram
participant User
participant App
participant Navigation
participant ViewModel
User->>App: Launch App
App->>Navigation: Initialize Navigation
Navigation->>ViewModel: Load Initial State
ViewModel-->>Navigation: Provide State
Navigation->>User: Display Home Screen
User->>Navigation: Select Profile Tab
Navigation->>ViewModel: Update State
ViewModel-->>Navigation: Provide Updated State
Navigation->>User: Display Profile Screen
User->>Navigation: Click Logout
Navigation->>ViewModel: Handle Logout
ViewModel-->>Navigation: Update State for Login
Navigation->>User: Display Login Screen
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 22
🧹 Outside diff range and nitpick comments (139)
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstScreenNavigation.kt (1)
5-7
: Consider adding KDoc documentationAdding documentation would improve maintainability by clarifying the purpose and usage of this navigation action.
+/** + * Navigation actions available from the first screen. + * + * @property toSecond Lambda to execute navigation to the second screen + */ data class FirstScreenNavigation( + /** Triggers navigation to the second screen */ val toSecond: () -> Unit, ) : NavigationActionsshared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreenNavigation.kt (1)
5-7
: Consider adding KDoc documentationTo improve maintainability and make the navigation flow clearer for other developers, consider adding KDoc documentation explaining:
- The purpose of this navigation class
- When and how the
pop
action should be used- Any specific considerations for the third screen's navigation
Example:
+/** + * Navigation actions for the third screen. + * + * @property pop Navigates back to the previous screen in the navigation stack + */ data class ThirdScreenNavigation( val pop: () -> Unit, ) : NavigationActionsshared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostNavigation.kt (3)
5-7
: Add KDoc documentation for better maintainabilityConsider adding documentation to explain:
- The purpose of this navigation class
- When and how
toLogin
should be used- Example usage in the context of signed-in state management
Here's a suggested documentation:
+/** + * Navigation actions for the signed-in navigation host. + * Handles navigation events when user is in authenticated state. + * + * @property toLogin Lambda to trigger navigation to login screen, + * typically used when user needs to re-authenticate + */ data class SignedInNavHostNavigation( val toLogin: () -> Unit, ) : NavigationActions
6-6
: Consider a more descriptive name for the navigation actionThe current name
toLogin
could be more specific about the action's purpose in the signed-in context, such asnavigateToReauthentication
orsignOutAndNavigateToLogin
.data class SignedInNavHostNavigation( - val toLogin: () -> Unit, + val navigateToReauthentication: () -> Unit, ) : NavigationActions
3-7
: Implementation aligns well with the architectural revampThe implementation follows the PR objectives by:
- Using simple lambda-based navigation instead of Navigator classes
- Keeping the navigation actions focused and specific to the signed-in state
- Implementing the NavigationActions interface for consistency
Consider documenting this pattern in the project's architecture guidelines as an example of the new navigation approach.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostNavigation.kt (1)
5-7
: Consider adding KDoc documentationTo improve maintainability and clarify the navigation flow, consider adding documentation that explains:
- The purpose of this navigation class
- When and how the
toLogin
action should be used- Any specific considerations for handling the navigation transition
+/** + * Navigation actions for the Profile feature navigation host. + * Handles navigation transitions related to profile management. + */ data class ProfileNavHostNavigation( + /** Triggers navigation to the login screen */ val toLogin: () -> Unit, ) : NavigationActionsshared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/navigation/NavigationActions.kt (1)
3-7
: Consider defining a base contract for NavigationActionsWhile the marker interface approach provides flexibility, consider defining a minimal base contract (e.g.,
back()
method) to ensure consistent behavior across implementations. This would help establish a common navigation pattern while still allowing for specific navigation actions per screen.Example addition:
interface NavigationActions { + /** + * Navigates back in the navigation stack. + * @return true if navigation was handled, false if there was nothing to navigate back to + */ + fun back(): Boolean }shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginScreenNavigation.kt (2)
5-7
: LGTM! Consider adding KDoc documentation.The implementation aligns well with the architectural revamp, particularly the transition to NavigationAction lambdas. The internal visibility provides good encapsulation.
Add KDoc documentation to improve maintainability:
+/** + * Navigation actions for the login screen. + * + * @property toSignedIn Lambda to execute navigation to signed-in state + */ internal data class LoginScreenNavigation( val toSignedIn: () -> Unit, ) : NavigationActions
5-7
: Consider using a sealed interface for navigation actions.For better type-safety and to make navigation actions more explicit, consider using a sealed interface pattern.
Here's a suggested implementation:
-internal data class LoginScreenNavigation( - val toSignedIn: () -> Unit, -) : NavigationActions +internal sealed interface LoginScreenNavigation : NavigationActions { + fun toSignedIn() + + data class Implementation( + private val toSignedInAction: () -> Unit + ) : LoginScreenNavigation { + override fun toSignedIn() = toSignedInAction() + } +}This pattern:
- Makes navigation actions more explicit
- Provides better type-safety
- Allows for easier testing and mocking
- Maintains compatibility with the new architecture
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/domain/SyncDataUseCase.kt (2)
12-12
: Document the rationale for the delay durationWhile reducing the delay from 10s to 2s improves the development experience, consider adding a comment explaining why this specific duration was chosen.
+ // 2 second delay to simulate network latency while keeping tests reasonably fast delay(2.seconds)
Line range hint
7-14
: Class structure aligns well with architectural goalsThe implementation demonstrates good practices aligned with the PR's architectural objectives:
- Uses constructor injection via Koin's
@Factory
annotation- Properly encapsulated as
internal
- Supports testing through clear mock behavior
shared/feature/src/iosMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/RootNavHostDefaults.ios.kt (1)
12-17
: Consider future-proofing the navigation stack initializationThe current implementation assumes only two navigation destinations (Home and Profile). As the application grows, this might need to be extended. Consider making the implementation more scalable:
actual fun getInitialStack( initialConfig: SignedInConfig, ): List<SignedInConfig> = when (initialConfig) { - is SignedInConfig.Home -> listOf(SignedInConfig.Profile(), initialConfig) - is SignedInConfig.Profile -> listOf(SignedInConfig.Home(), initialConfig) + is SignedInConfig.Home -> buildList { + // Add all other tabs first + add(SignedInConfig.Profile()) + // Add any future tabs here + // Selected tab goes last + add(initialConfig) + } + is SignedInConfig.Profile -> buildList { + // Add all other tabs first + add(SignedInConfig.Home()) + // Add any future tabs here + // Selected tab goes last + add(initialConfig) + } }This structure makes it clearer where to add new navigation destinations and maintains the iOS requirement of having all tabs pre-initialized.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstEvent.kt (1)
6-8
: Consider adding KDoc documentation.Adding documentation would improve maintainability by explaining the purpose of each event and when it should be used.
+/** + * Represents UI events that can be emitted by the FirstComponent. + */ sealed class FirstUiEvent : UiEvent { + /** + * Event to show a toast message to the user. + * @property text The localized text to display in the toast + */ data class ShowToast(val text: StringDesc) : FirstUiEvent() }iosApp/iosApp.xcodeproj/project.pbxproj (2)
31-32
: Navigation structure aligns with architectural goals.The addition of
ProfileTabNavigationView
andHomeTabNavigationView
supports the PR's objective of streamlining navigation logic and eliminating the need for separate Navigators.Consider documenting the navigation flow between these components to help maintain the architectural vision.
Also applies to: 100-101
566-592
: Consider adding UI tests for new features.While the project has a UI test target set up, I notice that the new Profile and Picker features don't have corresponding UI tests in the
iosAppUITests
target.Consider adding UI tests for the new features to ensure proper integration and behavior. I can help generate the test code if needed.
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ext/DecomposeValueExt.kt (2)
36-43
: Function formatting enhances readability!The reformatting with trailing commas improves maintainability and git diff clarity. Consider adding a code example in the documentation to demonstrate typical usage patterns, especially in the context of the new architectural changes.
Example documentation addition:
/** * ... * Example usage with a Component: * ``` * class MyComponent { * private val _state = MutableValue(initialState) * val state = _state.asStateFlow(viewModelScope) * } * ``` */
Line range hint
1-43
: Consider documenting architectural migration patternsThese extension functions play a crucial role in the new architecture's state management. Consider adding migration guides or documentation that shows how these utilities support the transition from the old ViewModel-based approach to the new Component-based architecture.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstViewState.kt (2)
6-8
: Consider enhancing the state class implementationWhile the implementation is correct, consider these improvements for better maintainability and clarity:
- Add KDoc documentation explaining the purpose and usage of this state class in the new architecture
- Consider using a more meaningful default value or making it required in the constructor
- Consider implementing a sealed interface pattern for better state management, especially if the state grows more complex
Example implementation:
+/** + * Represents the UI state for the first screen in the new Component-based architecture. + * This state is managed by FirstComponent and rendered by the UI layer. + * + * @property text The text to be displayed in the UI + */ data class FirstViewState( - val text: StringDesc = "".desc(), + val text: StringDesc ) { + companion object { + fun initial() = FirstViewState( + text = "Initial State".desc() + ) + } }
6-8
: Architectural Considerations for the New Component PatternAs part of the architectural revamp from MVVM to Component-based architecture, ensure that:
- This state class remains immutable and is only modified through the Component
- Consider implementing
equals()
andhashCode()
explicitly if custom equality comparison is needed for state updates- Plan for potential state expansion as features grow
- Consider adding a
copy()
method with named parameters for better state updates in the ComponentWould you like me to provide example implementations for any of these suggestions?
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostFactory.kt (1)
7-11
: Add KDoc documentation for better maintainability.Consider adding documentation to explain:
- The factory's role in the navigation architecture
- The relationship with AppComponentFactory
- The lifecycle of created components
Example:
+/** + * Factory responsible for creating the root navigation host component. + * Ensures proper initialization of retained navigation components through [AppComponentFactory]. + */ object RootNavHostFactory : KoinComponent {shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/NavigationTab.kt (1)
7-12
: LGTM! Consider documentation and utility enhancementsThe implementation is clean and aligns well with the architectural revamp objectives. A few optional enhancements to consider:
+/** + * Represents main navigation tabs in the signed-in state of the application. + * Used by SignedInNavHost to manage tab-based navigation. + */ enum class NavigationTab( val title: StringDesc, ) { HOME(MR.strings.tab_home.desc()), PROFILE(MR.strings.tab_profile.desc()), + + companion object { + val DEFAULT = HOME + + fun fromRoute(route: String): NavigationTab? = + values().firstOrNull { it.name.equals(route, ignoreCase = true) } + } }These additions would:
- Add clear documentation about the enum's purpose
- Provide a default tab for initial navigation
- Include a utility method for route-based tab resolution
iosApp/iosApp/Views/Screen/Login/LoginView.swift (1)
12-14
: Consider adding accessibility modifiers.To improve accessibility, consider adding these modifiers:
VStack(spacing: 10) { - Text(Localizable.login_screen_title.localized) - Button(Localizable.generic_sign_in.localized, action: viewModel.onLoginClick).buttonStyle(.borderedProminent) + Text(Localizable.login_screen_title.localized) + .accessibilityLabel(Localizable.login_screen_title.localized) + Button(Localizable.generic_sign_in.localized, action: viewModel.onLoginClick) + .buttonStyle(.borderedProminent) + .accessibilityIdentifier("loginButton") }androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/LoginScreenUi.kt (2)
37-51
: Consider standardizing spacing valuesWhile the layout structure is clean and well-organized, consider extracting the spacing values (24.dp, 8.dp) into a standardized spacing system. This would ensure consistency across the app's UI.
Example approach:
private object Spacing { val Horizontal = 24.dp val VerticalSmall = 8.dp // Add other standard spacings }
53-66
: Enhance preview coverage with dark theme variantConsider adding a dark theme preview to ensure the UI looks correct in both themes.
Example implementation:
@Preview(name = "Light Theme") @Preview( name = "Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES ) @Composable private fun LoginScreenPreview() { // existing preview code }shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostViewState.kt (1)
6-12
: Consider making the navigation tabs more configurable.While the current implementation is clean and focused, consider these architectural improvements:
- The hardcoded navigation tabs might limit reusability across different signed-in experiences
- The default tab selection could benefit from being more flexible
Consider this more flexible approach:
data class SignedInNavHostViewState( - val navigationTabs: ImmutableList<NavigationTab> = persistentListOf( - NavigationTab.HOME, - NavigationTab.PROFILE, - ), - val selectedTab: NavigationTab = NavigationTab.HOME, + val navigationTabs: ImmutableList<NavigationTab>, + val selectedTab: NavigationTab ) { + companion object { + fun default() = SignedInNavHostViewState( + navigationTabs = persistentListOf( + NavigationTab.HOME, + NavigationTab.PROFILE + ), + selectedTab = NavigationTab.HOME + ) + } + + init { + require(navigationTabs.isNotEmpty()) { "Navigation tabs cannot be empty" } + require(selectedTab in navigationTabs) { "Selected tab must be one of the navigation tabs" } + } }This approach:
- Makes the class more flexible for different navigation configurations
- Adds validation to ensure state consistency
- Maintains the default configuration through a companion object
- Better aligns with the PR's goal of improved architecture and reusability
Consider documenting the rationale for tab ordering and default selection in the codebase, as this might impact the user experience and navigation patterns.
iosApp/iosApp/Views/Navigation/Interop/TabContentView.swift (2)
50-52
: LGTM! Consider adding documentation for icon constantsThe simplified navigation structure with semantic tab names and appropriate SF Symbols is a good improvement. Consider adding a documentation comment explaining why these specific SF Symbol names were chosen, to help future maintainers.
extension NavigationTab { + /// Maps navigation tabs to their corresponding SF Symbols. + /// Uses filled variants for better visibility in the tab bar. func icon() -> String { switch self { case .home:
Line range hint
1-52
: Well-aligned with new architectural approachThe
TabContentView
implementation aligns well with the new architectural approach described in the PR objectives:
- It's focused solely on UI concerns without ViewModel dependencies
- The generic design allows for flexible navigation while maintaining type safety
- The implementation is stateless, making it easier to test and maintain
iosApp/iosApp/Views/Screen/Second/SecondView.swift (3)
12-14
: Add accessibility labels to buttonsWhile the implementation is clean and follows SwiftUI best practices, consider enhancing accessibility by adding explicit accessibility labels to the buttons.
- Button(Localizable.second_screen_button_fruit.localized, action: viewModel.onPickFruit).buttonStyle(.bordered) + Button(Localizable.second_screen_button_fruit.localized, action: viewModel.onPickFruit) + .buttonStyle(.bordered) + .accessibilityLabel(Localizable.second_screen_button_fruit.localized) - Button(Localizable.second_screen_button_veggie.localized, action: viewModel.onPickVeggie).buttonStyle(.bordered) + Button(Localizable.second_screen_button_veggie.localized, action: viewModel.onPickVeggie) + .buttonStyle(.bordered) + .accessibilityLabel(Localizable.second_screen_button_veggie.localized)
17-30
: Consider handling error and loading statesThe sheet presentation logic is clean, but consider handling potential edge cases:
- Add loading state handling while the picker is being initialized
- Include error handling in case picker initialization fails
.sheet( isPresented: Binding( get: { viewModel.picker != nil }, set: { _ in viewModel.onPickerDismissed() } ) ) { if let picker = viewModel.picker { - PickerView(PickerViewModel(picker)).presentationDetents([.medium]) + Group { + switch viewModel.pickerState { + case .loading: + ProgressView() + case .error(let message): + Text(message) + .foregroundColor(.red) + case .ready: + PickerView(PickerViewModel(picker)) + } + } + .presentationDetents([.medium]) } }
Line range hint
1-30
: Architectural changes align well with PR objectivesThe implementation successfully:
- Simplifies navigation by using sheet presentation instead of complex navigation patterns
- Maintains clear component boundaries with proper view-model separation
- Follows SwiftUI best practices for state management and UI composition
Consider documenting this pattern in the project's architecture guidelines as an example of the new navigation approach.
shared/feature/src/androidMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/RootNavHostDefaults.android.kt (1)
3-15
: Implementation aligns well with architectural goalsThe simplified navigation stack initialization supports the PR's architectural goals by:
- Enabling lazy initialization of navigation targets on Android
- Avoiding complex navigation tree structures
- Providing clear platform-specific behavior
This implementation contributes positively to the overall architectural revamp.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstScreen.kt (1)
Line range hint
3-12
: Add KDoc documentation for the architectural components.Given this interface's role in the new architectural approach, it would be valuable to add documentation explaining:
- The purpose and lifecycle of the FirstScreen component
- The relationship between viewState, actions, and events
- Usage guidelines for the NavigationAction pattern
Example documentation:
/** * Represents the first screen component in the application flow. * Follows the MVI pattern with unidirectional data flow. * * @property viewState Represents the current UI state * @property actions User-triggered actions that can be performed * @property events One-time events emitted by the screen */shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondScreen.kt (4)
8-13
: Add KDoc documentation for better API understanding.The interface structure is well-designed and aligns with the architectural goals. However, adding documentation would improve API clarity.
Consider adding documentation:
+/** + * Represents the second screen of the application. + * Implements MVI-like pattern with state management and actions. + */ interface SecondScreen { + /** Current view state of the screen */ val viewState: StateFlow<SecondViewState> + /** Available actions that can be performed on this screen */ val actions: Actions + /** State of the picker UI component */ val picker: StateFlow<ChildSlot<PickerType, Picker>>
14-19
: Add documentation for the picker types.The sealed interface design is clean and type-safe. Consider adding documentation to clarify the purpose of each picker type.
Consider adding documentation:
+/** + * Represents the different types of pickers available in the second screen. + */ @Serializable sealed interface PickerType { + /** Picker for selecting fruits */ data object Fruit : PickerType + /** Picker for selecting vegetables */ data object Vegetable : PickerType }
20-26
: Add documentation and explicit return types for actions.The actions interface is well-structured with clear naming. Consider adding documentation and explicit return types for better API clarity.
Consider these improvements:
+/** + * Defines the available actions that can be triggered from the second screen. + */ interface Actions { + /** Navigates back to the previous screen */ - fun onBack() + fun onBack(): Unit + /** Opens the fruit picker */ - fun onPickFruit() + fun onPickFruit(): Unit + /** Opens the vegetable picker */ - fun onPickVeggie() + fun onPickVeggie(): Unit + /** Handles the dismissal of any active picker */ - fun onPickerDismissed() + fun onPickerDismissed(): Unit }
1-26
: Great architectural alignment with PR objectives!The implementation successfully achieves the architectural goals outlined in the PR:
- Components as primary UI elements (SecondScreen interface)
- Simplified navigation using lambdas (Actions interface)
- Better testability through clear state management (StateFlow usage)
- Support for dependency injection through clear interface boundaries
iosApp/iosApp/Views/Screen/First/FirstView.swift (1)
Line range hint
1-26
: Consider adapting to the new component-based architecture.Based on the PR objectives describing a transition to a component-based architecture, this view might need further adjustments.
Consider:
- Evaluating if
FirstViewModelProtocol
should be replaced with a Component-based approach as mentioned in the PR objectives- Reviewing if the events handling aligns with the new architectural pattern
- Assessing if the view initialization pattern matches the new retained components strategy
Would you like assistance in adapting this view to the new component-based architecture?
shared/network/graphql/src/commonMain/kotlin/app/futured/kmptemplate/network/graphql/client/ApolloApiAdapter.kt (2)
Line range hint
39-54
: Well-structured query watching implementationThe addition of
filterOutExceptions
parameter provides good control over error handling behavior. The implementation correctly uses this parameter in the filter clause while maintaining a clean reactive flow.Consider adding a KDoc comment specifically explaining the implications of setting
filterOutExceptions
to true, as this might affect error observability in certain scenarios.
Error handling for Apollo exceptions appears incomplete
The current implementation handles two specific Apollo exceptions:
ApolloNetworkException
→NetworkError.ConnectionError
ApolloHttpException
→NetworkError.CloudHttpError
However, the base
ApolloException
class is imported and referenced in documentation but not handled in the error mapping. This could lead to unhandled exceptions for other Apollo error cases. Consider adding a catch-all case:is ApolloException -> NetworkError.CloudError( code = CloudErrorCode.UNKNOWN, message = throwable.message ?: throwable.cause?.message, cause = throwable.cause )🔗 Analysis chain
Line range hint
71-91
: Verify error mapping completenessThe error mapping in
executeOperation
looks comprehensive, but let's verify we're handling all possible Apollo error types.
Let me gather more information about the Apollo exceptions hierarchy and our error handling.
Let me check one more thing to understand the complete Apollo exception hierarchy.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for all Apollo exception types in the codebase rg "Apollo.*Exception" # Search for any custom error handling patterns ast-grep --pattern 'throw NetworkError.$_'Length of output: 2001
Script:
#!/bin/bash # Search for all NetworkError types to understand our error mapping rg "sealed class NetworkError" -A 10 # Look for any imports of Apollo exceptions rg "import.*apollo.*exception.*" # Search for any other exception handling patterns in the Apollo adapter ast-grep --pattern 'catch ($_) {'Length of output: 3590
Script:
#!/bin/bash # Search for the unfoldResult function to see how Apollo errors are handled there rg -A 10 "fun unfoldResult" shared/network/graphql/src/commonMain/kotlin/app/futured/kmptemplate/network/graphql/client/ApolloApiAdapter.kt # Look for any other Apollo error types being used rg "Apollo.*Error"Length of output: 932
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreenArgs.kt (2)
5-6
: LGTM! The implementation aligns well with the new architecture.The serializable data class is a good fit for cross-platform navigation arguments, supporting the PR's goal of simplifying navigation while maintaining type safety.
Consider documenting the purpose of this args class and any constraints on the
id
field, especially if it follows a specific format or has business meaning. This would help maintain the codebase as it grows.
6-6
: Consider adding validation for the id parameter.Since this is a shared module used across platforms, adding validation would help catch issues early and ensure consistent behavior.
Here's a suggested implementation using a secondary constructor:
-data class ThirdScreenArgs(val id: String) +data class ThirdScreenArgs(val id: String) { + init { + require(id.isNotBlank()) { "id cannot be blank" } + } +}shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondScreenNavigation.kt (1)
5-8
: Consider adding KDoc documentation for better clarity.The implementation aligns well with the new architecture's goals, using focused navigation lambdas. The design is clean and type-safe.
Consider adding documentation to clarify the purpose of each navigation action:
+/** + * Navigation actions for the second screen. + * + * @property pop Navigates back to the previous screen + * @property toThird Navigates to the third screen + * @param id Identifier for the third screen content + */ data class SecondScreenNavigation( val pop: () -> Unit, val toThird: (id: String) -> Unit, ) : NavigationActionsiosApp/iosApp/App/RootView.swift (1)
Line range hint
4-28
: Consider documenting the new navigation pattern.Since this change is part of the architectural revamp replacing Navigators with Components, consider:
- Adding comments explaining the new navigation pattern and lifecycle management
- Documenting how navigation state should be preserved during scene phase changes
- Creating a migration guide for other views that need to follow this pattern
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/FirstScreenUi.kt (1)
Line range hint
41-56
: Consider adding documentation for the new architectural patternThe implementation successfully adopts the new component-based architecture. Consider adding KDoc comments to explain:
- The relationship between Screen and Component
- The event handling pattern
- The navigation action pattern
Example documentation:
+/** + * UI implementation for the First screen following the Component-based architecture. + * + * @param screen The screen component handling business logic and state management + * @param modifier Compose modifier for customizing the UI + */iosApp/iosApp/Views/Screen/Profile/ProfileView.swift (1)
13-14
: Add accessibility support and extract spacing constantWhile the implementation is clean, consider these improvements for better maintainability and accessibility:
var body: some View { - VStack(spacing: 10) { + VStack(spacing: Layout.standardSpacing) { Text(Localizable.login_screen_title.localized) + .accessibilityIdentifier("profile_title") Button(Localizable.generic_sign_out.localized, action: viewModel.onLogoutClick) + .accessibilityIdentifier("logout_button") } } + private enum Layout { + static let standardSpacing: CGFloat = 10 + }shared/arkitekt-decompose/src/androidMain/kotlin/app/futured/arkitekt/decompose/event/EventsEffect.kt (1)
Line range hint
38-42
: LGTM! Consider adding documentationThe extension function change maintains consistency with
EventsEffect
. Consider adding KDoc documentation to explain its usage, similar to the well-documentedEventsEffect
function above.+/** + * Extension function to filter and handle specific event types in the [EventsEffect] observer. + * + * @param action Lambda to execute when the event matches type E + */ inline fun <reified E : UiEvent> UiEvent.onEvent(action: (E) -> Unit) {shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreenNavigation.kt (1)
5-7
: Consider adding KDoc documentation.Adding documentation would improve maintainability and clarity:
+/** + * Navigation actions for the profile screen. + * @property toLogin Action to navigate to the login screen, typically used after logout + */ internal data class ProfileScreenNavigation( val toLogin: () -> Unit, ) : NavigationActionsshared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostDefaults.kt (1)
3-3
: Architectural alignment with PR objectives confirmedThe rename to
SignedInNavHostDefaults
aligns well with the PR's goal of removing Navigator classes in favor of NavigationAction lambdas. TheNavHost
pattern provides a more flexible and reusable navigation structure.Consider documenting the following in the codebase:
- Migration guide from Navigator pattern to NavHost pattern
- Best practices for implementing NavigationAction lambdas
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ArkitektComponentContext.kt (1)
10-10
: Consider adding component lifecycle hooks.Since this interface is specific to Arkitekt and components are now retained, consider adding lifecycle-related functionality that components might need, such as:
- Initialization hooks for side-effects (mentioned in PR objectives)
- Cleanup methods for resource management
- State restoration callbacks
Example addition:
interface ArkitektComponentContext<T : Any> : GenericComponentContext<T> { + /** Called when the component is initialized */ + fun onInit() + + /** Called when the component is about to be destroyed */ + fun onDestroy() + + /** Called when the component should save its state */ + fun onSaveState(): T }iosApp/iosApp/Views/Screen/Picker/PickerView.swift (2)
11-34
: Consider enhancing the view's accessibility and user experienceWhile the implementation is functional, consider these improvements:
- Add accessibility identifiers for testing
- Center the ProgressView for better visual balance
- Consider supporting custom item views for more flexible presentation
- Add visual feedback for item selection
Here's a suggested implementation:
var body: some View { VStack { HStack { Text(Localizable.picker_title.localized).font(.headline) Button(Localizable.generic_close.localized, action: viewModel.onDismiss) - } + }.accessibilityIdentifier("picker_header") if viewModel.isLoading { - ProgressView() + ProgressView() + .frame(maxHeight: .infinity) + .accessibilityIdentifier("picker_loading") } else { List { ForEach(viewModel.items) { item in HStack { Text(item.id) Spacer() - } + }.background(Color.accentColor.opacity(0.0001)) // Improves tap feedback .contentShape(Rectangle()) .onTapGesture { viewModel.onPick(item: item) } + .accessibilityIdentifier("picker_item_\(item.id)") } } + .accessibilityIdentifier("picker_list") } } }
1-35
: Architecture aligns well with the PR objectivesThe implementation successfully achieves the architectural goals:
- Clean separation of concerns
- Unidirectional data flow through the ViewModel protocol
- Reusable component structure that can work with any conforming ViewModel
This aligns perfectly with the PR's objective of simplifying the architecture while maintaining flexibility.
androidApp/proguard-rules.pro (1)
Line range hint
1-24
: Consider additional ProGuard rules for the new architectureGiven the architectural changes (retained Components, NavigationAction lambdas, etc.), consider adding rules to:
- Preserve Component classes and their state
- Protect navigation-related lambdas from obfuscation
- Ensure proper serialization of navigation state
Add these rules after updating the package name:
+ # Keep Component classes + -keep class ** extends app.futured.kmptemplate.core.Component { *; } + + # Preserve navigation lambdas + -keepclassmembers class ** { + private static final kotlin.jvm.functions.Function* *Navigation*; + } + + # Keep names of classes involved in navigation state serialization + -keepnames class ** implements android.os.Parcelable + -keepnames class ** implements java.io.SerializableandroidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/ProfileNavHostUi.kt (2)
22-27
: Document the experimental API usageThe function uses experimental Decompose API features. Consider adding KDoc comments explaining:
- Why the experimental API is necessary
- Any potential risks or limitations
- Migration plans when the API becomes stable
+/** + * Composable navigation host for profile-related screens. + * + * Note: Uses experimental Decompose predictive back animation API for enhanced + * navigation experience. This API is expected to be stabilized in future releases. + */ @OptIn(ExperimentalDecomposeApi::class) @Composable fun ProfileNavHostUi(
28-30
: LGTM! Good state management practicesThe implementation correctly uses lifecycle-aware state collection and maintains a clean separation between navigation state and actions. This aligns well with the PR's architectural goals of simplifying navigation logic.
Consider documenting the navigation action types in the component's API documentation to help other developers understand the available navigation paths.
iosApp/iosApp/Views/Navigation/ProfileTabNavigationView.swift (1)
6-12
: Add documentation for better maintainability.Consider adding documentation comments to explain:
- The purpose of
SkieSwiftStateFlow
and its role in state management- The relationship between
ProfileNavHost
and its propertiesExample:
+/// Manages the navigation stack state for the profile tab private let stack: SkieSwiftStateFlow<ChildStack<ProfileConfig, ProfileChild>> +/// Handles navigation actions within the profile flow private let actions: ProfileNavHostActionsshared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/PickerState.kt (2)
8-11
: Add KDoc documentation for better maintainability.The implementation is clean and follows best practices. Consider adding KDoc documentation to explain:
- The purpose of this state class
- The significance of
isLoading
- The expected content of
items
+/** + * Represents the UI state of a picker component. + * @property isLoading Indicates whether the picker is currently loading data + * @property items List of selectable items displayed in the picker + */ data class PickerState( val isLoading: Boolean = false, val items: ImmutableList<StringDesc> = persistentListOf(), )
1-23
: Architecture aligns well with PR objectives.The implementation of
PickerState
effectively supports the architectural revamp by:
- Using immutable state management, which enhances safety
- Providing a clean interface for component state
- Supporting the transition from MVVM to the new component-based architecture
Consider documenting how this state class fits into the broader architectural patterns being established.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentContext.kt (2)
Line range hint
11-15
: Enhance documentation to reflect Arkitekt integrationWhile the interface change looks good, consider expanding the documentation to:
- Explain the benefits of using ArkitektComponentContext
- Document any specific considerations for component implementations
- Add examples of typical usage patterns
/** * Application-specific [ComponentContext] that can be decorated with custom functionality. + * This implementation uses ArkitektComponentContext to provide enhanced component lifecycle + * management and retained state handling. + * + * @see ArkitektComponentContext for details about the base functionality * See [docs](https://arkivanov.github.io/Decompose/component/custom-component-context/) for more info. */
Line range hint
1-33
: Consider documenting migration guidelinesAs this represents a significant architectural shift from Koin to Arkitekt, consider:
- Adding migration guidelines for existing components
- Documenting any breaking changes
- Providing examples of common patterns with the new architecture
This will help team members adapt to the new component context pattern and ensure consistent implementation across the codebase.
iosApp/iosApp/Views/Screen/Profile/ProfileViewModel.swift (3)
1-6
: LGTM! Protocol design aligns well with architectural goals.The protocol definition is clean and follows Swift best practices. The use of
DynamicProperty
ensures proper SwiftUI integration, which is essential for view model state management.Consider documenting the protocol with a brief comment explaining its role in the new component-based architecture, especially since this is part of a major architectural revamp.
8-14
: Consider adding state management capabilities.The current implementation handles actions well but might benefit from explicit state management to fully embrace the component-based architecture mentioned in the PR objectives.
Consider adding state handling:
struct ProfileViewModel { private let actions: ProfileScreenActions + @Published private(set) var state: ProfileScreenState init(_ screen: ProfileScreen) { actions = screen.actions + state = screen.state } }
16-20
: Consider modernizing with async/await pattern.While the current implementation is valid, modern Swift practices often handle authentication operations asynchronously.
Consider updating to async/await:
extension ProfileViewModel: ProfileViewModelProtocol { - func onLogoutClick() { - actions.onLogout() + func onLogoutClick() async { + try? await actions.onLogout() } }shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/Picker.kt (2)
5-8
: Consider adding KDoc documentation for the interface.The interface design aligns well with MVI architecture and provides a clean separation of concerns. Consider adding documentation to explain:
- The purpose and lifecycle of the Picker
- The relationship between viewState and actions
- Any threading considerations for state updates
Example documentation:
/** * Represents a picker component following MVI architecture. * * @property viewState Represents the current state of the picker * @property actions Handles user interactions with the picker */
9-13
: Consider improving type safety for picked items.While the current implementation is clean, consider these improvements:
- Replace String with a type-safe model for picked items
- Add documentation about threading guarantees for action handlers
Example refactor:
interface Actions { /** * Called when an item is picked. * @param item The selected item * @threading Can be called from any thread */ fun onPick(item: PickerItem) /** * Called when the picker should be dismissed. * @threading Can be called from any thread */ fun onDismiss() } sealed interface PickerItem { val id: String val title: String }shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt (4)
37-56
: RefactorchildFactory
to reduce code duplicationThe
childFactory
contains repeated code for creating child components for eachSignedInConfig
. Consider extracting the shared logic into a separate function or applying a more scalable pattern to enhance maintainability and readability.
68-75
: Abstract common logic inhomeTab
andprofileTab
propertiesBoth
homeTab
andprofileTab
properties use similar code to retrieve the first instance of a specificSignedInChild
. You could create a generic helper function to eliminate duplication and make the code more concise.
78-85
: EnsureonTabSelected
handles already selected tabs efficientlyIn the
onTabSelected
method, switching to the currently active tab might result in unnecessary operations. Consider checking if the selected tab is different from the current tab before callingswitchTab
to optimize performance.
23-28
: Add documentation for theSignedInNavHostComponent
classProviding KDoc comments for the
SignedInNavHostComponent
class and its public members can improve code maintainability and assist other developers in understanding the component's purpose and usage.shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginScreen.kt (2)
9-12
: Consider adding KDoc documentation for the Actions interface.The Actions interface design is clean and focused. Consider adding KDoc documentation to describe the expected behavior of
onLoginClick()
, including any side effects or state changes it triggers.Example documentation:
interface Actions { + /** + * Handles the login button click event. + * Triggers the login process and navigation to the appropriate screen + * based on authentication result. + */ fun onLoginClick() }
5-12
: Strong foundation for the architectural revamp.The interface design effectively supports the architectural goals outlined in the PR:
- Clean separation of concerns with distinct state and actions
- Reactive state management with StateFlow
- Interface-first approach enabling better testability
- Simple contract supporting dependency injection in implementing components
Consider creating a similar pattern for other screens to maintain consistency across the application.
iosApp/iosApp/Views/Navigation/SignedInNavigationView.swift (1)
31-35
: Consider adding documentation for the navigation structure.The navigation implementation is clean and semantically clear. The transition from generic tab names (A/B) to specific ones (home/profile) improves code readability.
Consider adding documentation comments explaining:
- The navigation hierarchy (parent-child relationship)
- The role of
navHost
in the navigation structure- The relationship between
TabContentView
and tab-specific navigation views+ /// Represents the main navigation structure for signed-in users. + /// - Uses a tab-based navigation with home and profile sections. + /// - Each tab maintains its own navigation stack through its respective NavHost. struct SignedInNavigationView: View {shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/PickerNavigation.kt (3)
5-7
: Add KDoc documentation for better API clarity.As part of the architectural revamp, adding comprehensive documentation would help other developers understand the new navigation pattern.
Consider adding documentation like this:
+/** + * Navigation actions for the Picker component. + * + * @property dismiss Lambda to handle dismissal of the picker. Optionally returns the selected item. + */ internal data class PickerNavigation( val dismiss: (item: String?) -> Unit, ) : NavigationActions
6-6
: Consider using a sealed type for the item parameter.Instead of using a nullable String, consider creating a sealed type to represent the dismissal result more explicitly. This would make the API more type-safe and self-documenting.
Example implementation:
+sealed interface DismissResult { + data class Selected(val item: String) : DismissResult + data object Cancelled : DismissResult +} internal data class PickerNavigation( - val dismiss: (item: String?) -> Unit, + val dismiss: (result: DismissResult) -> Unit, ) : NavigationActions
5-5
: Consider a more descriptive name for the navigation class.Since this class represents picker-specific navigation actions, a more descriptive name like
PickerComponentNavigation
would better align with the component-based architecture being introduced.-internal data class PickerNavigation( +internal data class PickerComponentNavigation(iosApp/iosApp/Views/Navigation/RootNavigationView.swift (1)
9-11
: Consider standardizing event handler namingThe change from
RootNavigation
toRootNavHost
aligns with the architectural revamp. The deep link handler naming could be more consistent.Consider standardizing the event handler naming:
- self.openDeepLink = component.actions.onDeepLink + self.handleDeepLink = component.actions.onDeepLinkThis would better align with Swift naming conventions where the property name describes the action and the handler name starts with "on".
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deepLink/DeepLink.kt (2)
13-16
: Consider adding URI validationThe class accepts any string as URI without validation. Consider adding validation to ensure the URI is well-formed, especially since it's later used with
Url(uri)
.internal data class DeepLink( val regex: Regex, val uri: String, -) { +) { + init { + require(uri.isNotBlank()) { "URI cannot be blank" } + runCatching { Url(uri) }.getOrElse { + throw IllegalArgumentException("Invalid URI format", it) + } + }
1-43
: Consider adding usage examples in documentationAs this is part of the architectural revamp, it would be valuable to include documentation showing:
- Example regex patterns for common deep link scenarios
- Integration with the new Component-based architecture
- How this fits into the overall navigation strategy without Navigators
This would help developers understand how to effectively use this new deep linking system within the revised architecture.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt (2)
15-18
: Consider adding validation for navigation stack changes.The
navigate
method accepts a list of children without any apparent validation. Consider adding checks for:
- Empty list prevention
- Maximum stack depth
- Valid state transitions
Example implementation:
interface Actions { fun pop() - fun navigate(newStack: List<Child<ProfileConfig, ProfileChild>>) + fun navigate(newStack: List<Child<ProfileConfig, ProfileChild>>) { + require(newStack.isNotEmpty()) { "Navigation stack cannot be empty" } + require(newStack.size <= MAX_STACK_DEPTH) { "Navigation stack exceeds maximum depth" } + // Proceed with navigation + } + + companion object { + private const val MAX_STACK_DEPTH = 10 + } }
1-30
: Architecture aligns well with PR objectives.The implementation successfully achieves the architectural goals outlined in the PR:
- Simplifies navigation by replacing complex navigators with straightforward Actions
- Supports retained components through proper state management
- Enables better testing through clear component boundaries
Consider documenting the following aspects:
- Component lifecycle management
- State restoration strategy
- Navigation action handling in parent components
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/ProfileScreenUi.kt (2)
55-55
: Review the spacing valueA 4.dp spacing seems unusually small between text and a button. Consider using standard Material spacing guidelines.
-Spacer(modifier = Modifier.height(4.dp)) +Spacer(modifier = Modifier.height(16.dp))
56-58
: Consider adding loading and error statesThe logout action might not be instantaneous. Consider handling loading and error states during logout.
Example implementation:
Button( onClick = actions::onLogout, enabled = !viewState.isLoading ) { if (viewState.isLoading) { CircularProgressIndicator(modifier = Modifier.size(16.dp)) } else { Text(kmpStringResource(MR.strings.generic_sign_out)) } } // Show error if present viewState.error?.let { error -> Text( text = error.message, color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodySmall, modifier = Modifier.padding(top = 8.dp) ) }shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deepLink/DeepLinkDestination.kt (2)
3-5
: Enhance KDoc documentation for better clarity.While the current documentation provides a basic overview, it could be more comprehensive to help developers understand the usage and purpose of each destination.
Consider expanding the documentation:
/** * Specifies all destinations reachable by deep links in application. + * + * This sealed class represents the complete navigation graph for deep linking, + * ensuring type-safe navigation throughout the application. + * + * @see HomeTab - Main home screen destination + * @see ProfileTab - User profile screen destination + * @see SecondScreen - Secondary screen destination + * @see ThirdScreen - Parameterized screen requiring a string argument */
6-15
: Consider implementing navigation result handling.Based on the PR objectives mentioning "navigation results", we should consider adding support for handling navigation results in the destinations.
Consider extending the sealed class to support result handling:
sealed class DeepLinkDestination { // Existing code... /** * Represents the result that can be returned when navigating back from this destination. * Implement this interface in destinations that need to return results. */ interface WithResult<T> { fun createResult(value: T): NavigationResult<T> } /** * Example implementation for ThirdScreen with result: */ data class ThirdScreen(val argument: String) : DeepLinkDestination(), WithResult<String> { override fun createResult(value: String) = NavigationResult(route, value) } } data class NavigationResult<T>( val sourceRoute: String, val value: T )This enhancement:
- Adds type-safe result handling
- Maintains consistency with the architectural revamp
- Provides a clear pattern for implementing result-returning screens
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt (2)
9-18
: Add KDoc documentation for better maintainabilityThe component structure aligns well with the new architecture. Consider adding KDoc documentation to explain:
- The component's responsibility
- The meaning of Nothing as the second type parameter
- The relationship with ProfileScreen interface
Example documentation:
+/** + * Profile screen component responsible for managing user profile view state and logout action. + * This component doesn't emit any internal events (Nothing). + */ @Factory internal class ProfileComponent(
1-24
: Consider documenting architectural patternsThe implementation successfully achieves the architectural goals outlined in the PR:
- Removes ViewModel wrappers
- Uses constructor injection
- Implements retained component pattern
- Handles navigation through specific actions
Consider adding a comment block explaining these architectural decisions for future maintainers.
Add a file-level comment:
+/* + * This component follows the retained component pattern introduced in the architectural revamp. + * Key characteristics: + * - Direct component-based presentation logic (no ViewModel wrapper) + * - Constructor injection for dependencies + * - Specific navigation actions instead of general navigator + * - StateFlow-based state management + */ package app.futured.kmptemplate.feature.ui.profileScreeniosApp/iosApp/Views/Navigation/HomeTabNavigationView.swift (2)
6-12
: Consider memory management implicationsThe use of
SkieSwiftStateFlow
for Kotlin->Swift bridging is correct, but be mindful of potential memory leaks. Ensure proper cleanup of the state flow subscription when the view is destroyed.Consider adding a cleanup mechanism, for example:
private var cancellable: Cancellable? init(_ component: HomeNavHost) { self.stack = component.stack self.actions = component.actions // Consider observing lifecycle events to cleanup } // Add cleanup method func cleanup() { cancellable?.cancel() }
15-19
: Add error handling for navigation actionsThe navigation action closure should handle potential errors during navigation state changes.
Consider wrapping the navigation action in a do-catch block:
DecomposeNavigationStack( kotlinStack: stack, setPath: { path in do { try actions.navigate(path) } catch { // Handle or log navigation errors print("Navigation error: \(error)") } } )iosApp/iosApp/Views/Screen/Picker/PickerViewModel.swift (2)
4-6
: Add documentation to improve code clarityConsider adding documentation comments to explain the purpose and usage of
PickerItem
. This would help other developers understand its role in the picker implementation.+/// Represents a selectable item in the picker view. +/// Conforms to `Identifiable` for unique identification in SwiftUI lists. struct PickerItem: Identifiable { let id: String }
8-14
: Well-structured protocol design with SwiftUI integrationThe protocol effectively defines the contract for picker view models and correctly inherits from
DynamicProperty
for SwiftUI integration. Consider adding documentation to explain the protocol's purpose and requirements.+/// Protocol defining the interface for picker view models. +/// Inherits from `DynamicProperty` to support SwiftUI's property wrapper system. protocol PickerViewModelProtocol: DynamicProperty { + /// Indicates whether the picker is currently loading data var isLoading: Bool { get } + /// Array of available items in the picker var items: [PickerItem] { get } + /// Called when user selects an item func onPick(item: PickerItem) + /// Called when user dismisses the picker func onDismiss() }shared/feature/src/commonTest/kotlin/app/futured.kmptemplate/feature/navigation/deepLink/DeepLinkResolverTest.kt (1)
6-9
: Consider adding documentation and using @BeforeTest.While the setup is functional, consider these improvements:
- Add KDoc documentation for the test class describing its purpose
- Move the resolver initialization to a setup method with @BeforeTest annotation
+/** + * Unit tests for DeepLinkResolver implementation validating various deep link resolution scenarios. + */ class DeepLinkResolverTest { - private val deepLinkResolver: DeepLinkResolver = DeepLinkResolverImpl() + private lateinit var deepLinkResolver: DeepLinkResolver + + @BeforeTest + fun setup() { + deepLinkResolver = DeepLinkResolverImpl() + }shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/BaseComponents.kt (2)
9-16
: Consistent Terminology for Parameter DescriptionsIn the
AppComponent
KDoc, the parameterVS
is described as "The type of the component state." For clarity and consistency across components, consider standardizing the terminology to "view state" if it refers to the same concept as inScreenComponent
.
23-30
: Consistent Terminology for Parameter DescriptionsIn the
ScreenComponent
KDoc,VS
is described as "The type of the view state." Ensure consistency with theAppComponent
by aligning the parameter descriptions if they represent the same concept.shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt (3)
11-16
: Consider making the navigation property private.The
navigation
property is currently exposed through the override modifier, but since it's only used internally inonBack()
, consider making it private and only exposing necessary navigation actions through theActions
interface.- @InjectedParam override val navigation: ThirdScreenNavigation, + @InjectedParam private val navigation: ThirdScreenNavigation,
20-27
: Consider using a property delegate for actions.The current implementation creates an anonymous object on property access. Consider using
lazy
to cache the actions object:- override val actions: ThirdScreen.Actions = object : ThirdScreen.Actions { + override val actions: ThirdScreen.Actions by lazy { + object : ThirdScreen.Actions { override fun onBack() = navigation.pop() - } + } + }
1-27
: Great implementation of the new architecture!The component successfully demonstrates the key architectural changes outlined in the PR:
- Integrates presentation logic directly into the Component
- Uses constructor injection for dependencies
- Implements clean navigation through lambdas
- Maintains state through ComponentContext
This implementation serves as a good example for other components in the new architecture.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt (4)
10-15
: Enhance documentation to better reflect architectural patternsWhile the documentation explains the copy-paste nature of the template, it could be more comprehensive:
- Add explanation of the Component-based architecture
- Include example usage
- Remove IDE-specific shortcut hint or make it more generic
Consider updating the documentation like this:
/** - * A copy-paste template for creating new screen. - * All you need to do is to copy contents of this file into your destination package and replace "TEMPLATE" with name of the screen. + * Template for creating new screens following the Component-based architecture. + * + * Usage: + * 1. Copy this file to your feature package + * 2. Replace "TEMPLATE" with your screen name + * 3. Implement your screen-specific state and actions * - * Hint: Ctrl+G + * Example: + * ProfileScreen, HomeScreen, etc. */
16-23
: LGTM! Consider adding KDoc documentationThe interface structure is clean and well-organized, following the new architectural approach.
Consider adding KDoc documentation to explain the purpose of each property and interface:
+/** + * Represents a screen's contract defining its state and available actions. + */ interface TEMPLATEScreen { + /** Represents the current state of the screen */ val viewState: StateFlow<TEMPLATEViewState> + /** Available user actions for this screen */ val actions: Actions + /** + * Defines all possible user actions that can be performed on this screen. + */ interface Actions { + /** Handles the back navigation action */ fun onBack() } }
29-29
: Consider providing a more detailed ViewState templateWhile keeping the ViewState empty makes sense for a template, consider providing commented examples of common state properties to guide developers.
-data object TEMPLATEViewState +data class TEMPLATEViewState( + // Example state properties: + // val isLoading: Boolean = false, + // val items: List<Item> = emptyList(), + // val error: String? = null +)
31-42
: LGTM! Consider documenting the Nothing type usageThe component implementation is clean and follows the new architectural patterns well. The use of constructor injection and Factory annotation is appropriate.
Consider adding a comment explaining why
Nothing
is used as the event type parameter:@Factory +/** + * Screen component that handles the presentation logic. + * Uses `Nothing` as event type since this template doesn't process any events. + */ internal class TEMPLATEComponent( @InjectedParam componentContext: AppComponentContext, @InjectedParam override val navigation: TEMPLATEScreenNavigation, ) : ScreenComponent<TEMPLATEViewState, Nothing, TEMPLATEScreenNavigation>( componentContext, TEMPLATEViewState ), TEMPLATEScreen {shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/VegetablePickerComponent.kt (3)
16-21
: LGTM! Consider adding KDoc documentation.The component structure aligns well with the architectural revamp objectives, particularly the constructor injection approach. Consider adding KDoc documentation to describe the component's purpose and responsibilities.
+/** + * Component responsible for vegetable selection functionality. + * Manages the presentation logic for displaying and selecting vegetables. + * + * @property componentContext The application component context + * @property navigation Navigation handler for picker-related actions + */ @Factory internal class VegetablePickerComponent(
23-26
: Consider using a sealed class for picker results.While the current implementation works, using null to represent dismissal could be improved with a sealed class for better type safety.
+sealed class PickerResult { + data class Selected(val item: String) : PickerResult() + object Dismissed : PickerResult() +} override val actions: Picker.Actions = object : Picker.Actions { - override fun onPick(item: String) = navigation.dismiss(item) - override fun onDismiss() = navigation.dismiss(null) + override fun onPick(item: String) = navigation.dismiss(PickerResult.Selected(item)) + override fun onDismiss() = navigation.dismiss(PickerResult.Dismissed) }
1-49
: Consider adding unit tests for initialization logic.The component implements the new architecture well, but the initialization logic would benefit from unit tests to verify:
- State transitions during loading
- Error handling
- Resource loading behavior
Would you like me to help create a test suite for this component?
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/PickerUi.kt (4)
73-79
: Add semantic properties to loading indicatorConsider adding semantic properties to improve testability and accessibility:
Box(Modifier.fillMaxWidth()) { CircularProgressIndicator( Modifier .align(Alignment.Center) .padding(16.dp) + .semantics { contentDescription = "Loading" } ) }
86-95
: Enhance accessibility and code organization of list itemsConsider these improvements:
- Add content description for better accessibility
- Extract click handler to a separate function
ListItem( headlineContent = { Text(title) }, modifier = Modifier .fillMaxWidth() + .semantics { + contentDescription = "Select $title" + onClick { actions.onPick(title); true } + } .clickable { actions.onPick(title) }, )
109-124
: Enhance preview configurationsConsider adding:
- Multiple preview configurations for different themes and sizes
- Documentation for the preview states
+@Preview(name = "Light Theme") @Preview +@Preview(name = "Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview(name = "Large Font", fontScale = 1.5f) @Composable private fun PickerUiPreview( @PreviewParameter(PickerUiPreviewProvider::class) state: PickerState, ) { MaterialTheme { Content( viewState = state, actions = object : Picker.Actions { override fun onPick(item: String) = Unit override fun onDismiss() = Unit }, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth().padding(16.dp), ) } }
1-124
: Implementation aligns well with architectural goalsThe component successfully implements the new architectural approach outlined in the PR objectives:
- Clean separation of concerns
- Proper state management with lifecycle awareness
- Independent and reusable component structure
- Consistent Material Design implementation
This serves as a good example for implementing other components in the new architecture.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt (3)
25-77
: Consider adding KDoc comments for clarity and maintainabilityAdding KDoc comments to the
HomeNavHostComponent
class and its members can improve code readability and help other developers understand the navigation structure and logic.
33-36
: Specify explicit return types for lambda expressionsIn the
actions
object, consider specifying explicit return types for the lambda expressions to enhance type safety and readability. This can help prevent potential issues if the implementation changes in the future.Apply this change:
override val actions: HomeNavHost.Actions = object : HomeNavHost.Actions { - override fun navigate(newStack: List<Child<HomeConfig, HomeChild>>) = homeNavigator.navigate { newStack.map { it.configuration } } - override fun pop() = homeNavigator.pop() + override fun navigate(newStack: List<Child<HomeConfig, HomeChild>>): Unit = homeNavigator.navigate { newStack.map { it.configuration } } + override fun pop(): Unit = homeNavigator.pop() }
45-74
: RefactorchildFactory
to reduce code duplicationThe
childFactory
when
block repeats similar patterns for creating components. Consider extracting common logic into a separate method or utilizing a factory pattern to minimize code duplication and enhance maintainability.For example, you could create a helper function:
private fun <T : AppComponent<*, *>> createChild( childCtx: AppComponentContext, componentClass: KClass<T>, navigation: Any, args: Any? = null ): T { return AppComponentFactory.createComponent( childContext = childCtx, navigation = navigation, args = args ) }And refactor the
childFactory
as:when (config) { HomeConfig.First -> HomeChild.First( - AppComponentFactory.createComponent<FirstComponent>( - childContext = childCtx, - navigation = FirstScreenNavigation( - toSecond = { homeNavigator.pushNew(HomeConfig.Second) }, - ), - ), + createChild( + childCtx, + FirstComponent::class, + FirstScreenNavigation( + toSecond = { homeNavigator.pushNew(HomeConfig.Second) }, + ) + ), ) // Similarly for other cases... }shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/FruitPickerComponent.kt (1)
16-21
: Add KDoc documentation for the component class.Consider adding documentation to describe the component's purpose, responsibilities, and usage. This is particularly important as this component is part of the new architecture.
@Factory +/** + * Component responsible for displaying and handling fruit selection. + * Part of the new retained component architecture, this component manages + * its own presentation logic and navigation through injected [PickerNavigation]. + * + * @property componentContext The application component context + * @property navigation Navigation handler for picker actions + */ internal class FruitPickerComponent( @InjectedParam componentContext: AppComponentContext, @InjectedParam override val navigation: PickerNavigation,shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt (2)
19-21
: Consider adding validation before navigation.The
onLoginClick
implementation directly triggers navigation without any validation. Consider:
- Checking if navigation should be allowed (e.g., form validation, loading state)
- Adding error handling for navigation failures
override val actions: LoginScreen.Actions = object : LoginScreen.Actions { - override fun onLoginClick() = navigation.toSignedIn() + override fun onLoginClick() { + // Add validation logic here + if (isValidToNavigate()) { + try { + navigation.toSignedIn() + } catch (e: Exception) { + // Handle navigation errors + } + } + } }
1-24
: Document architectural decisions and testing approach.The component successfully implements the new architectural patterns outlined in the PR objectives. Consider:
- Adding KDoc comments explaining the component's role in the new architecture
- Documenting how to test this component, especially regarding the initialization logic mentioned in the PR objectives
- Adding examples of how to handle side-effects during initialization
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHost.kt (1)
47-47
: Caution with using experimental UUID APIYou are opting into the
ExperimentalUuidApi
by using the@OptIn(ExperimentalUuidApi::class)
annotation to utilizeUuid.random()
. Be cautious when using experimental APIs, as they may change in future releases, potentially affecting code stability. Consider using a stable UUID generation method or library if possible..github/workflows/android_enterprise_release.yml (1)
46-49
: Align Product Flavor with New ArchitectureGiven the architectural revamp from MVVM to Component-based architecture, ensure the KMP flavor configuration aligns with the new structure.
Consider documenting the relationship between product flavors and the new component-based architecture in the project documentation.
androidApp/build.gradle.kts (1)
Based on the gathered information, I can now provide a final response:
Enterprise build type needs ProGuard rules update for component architecture
The current ProGuard configuration lacks specific rules for the Decompose-based component architecture. Add the following rules to
proguard-rules.pro
:
- Keep component classes and their state classes
- Preserve navigation-related classes
- Ensure proper serialization of component states
Specific locations requiring ProGuard rules:
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/
- navigation componentsshared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/
- UI componentsshared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/
- base component classes🔗 Analysis chain
Line range hint
67-77
: Review Enterprise Build Type ConfigurationThe Enterprise build type is using debug signing config which might not be appropriate for distribution builds. Also, verify if ProGuard rules are compatible with the new component-based architecture.
Consider adding specific ProGuard rules for the new component-based architecture to ensure proper code optimization without breaking the navigation structure.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check ProGuard rules for component-related configurations rg -l "proguard|keep" | xargs grep -l -i "component|navigator|viewmodel"Length of output: 74
Script:
#!/bin/bash # Let's first check if proguard-rules.pro exists and its content fd "proguard-rules.pro" --type f --exec cat {} # Also check for any other ProGuard configuration files fd -e pro --type f --exec cat {} # Check for component-related classes in the codebase ast-grep --pattern 'class $_ : Component'Length of output: 3971
Script:
#!/bin/bash # Let's check for the signing configurations in build.gradle files fd "build.gradle" --type f --exec cat {} fd "build.gradle.kts" --type f --exec cat {} # Let's also check for any component or navigation related classes to understand the architecture rg -l "class.*Component|interface.*Component" --type kotlin rg -l "class.*Navigator|interface.*Navigator" --type kotlinLength of output: 58174
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt (1)
33-33
: Unused logger instanceThe
logger
instance is declared but not used anywhere in the class. Consider removing it to keep the code clean.Apply this diff to remove the unused logger:
- private val logger = Logger.withTag("RootNavHostComponent")
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/SecondScreenUi.kt (4)
51-51
: Correct 'contentWindowInsets' assignmentThe
contentWindowInsets
parameter expects aWindowInsets
object, not a lambda. Passing the object directly improves readability.Apply this diff to fix the issue:
- contentWindowInsets = { WindowInsets(0) }, + contentWindowInsets = WindowInsets(0),
72-72
: Provide content description for accessibilityThe
Icon
component hascontentDescription = null
, which may affect accessibility tools. Consider providing a meaningful description to enhance accessibility.Apply this diff to fix the issue:
- Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = kmpStringResource(MR.strings.back_button_description) + )
85-92
: Optimize horizontal arrangement in theRow
Instead of manually adding a
Spacer
, consider usinghorizontalArrangement = Arrangement.spacedBy(8.dp)
for consistent spacing and cleaner code.Apply this diff to refactor the code:
Row( - Modifier.padding(horizontal = 20.dp) + modifier = Modifier.padding(horizontal = 20.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) ) { Button( onClick = { actions.onPickFruit() }, modifier = Modifier.weight(1f) ) { Text(text = kmpStringResource(MR.strings.second_screen_button_fruit)) } - Spacer(modifier = Modifier.width(8.dp)) Button( onClick = { actions.onPickVeggie() }, modifier = Modifier.weight(1f) ) { Text(text = kmpStringResource(MR.strings.second_screen_button_veggie)) } }
46-46
: Specify initial value forModalBottomSheetState
Consider specifying the initial visibility state for
rememberModalBottomSheetState()
to make the behavior explicit.Apply this diff to enhance clarity:
- val bottomSheetState = rememberModalBottomSheetState() + val bottomSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt (2)
45-56
: Enhance Readability by Extracting Child Component CreationFor improved readability and maintainability, consider extracting the child component creation into separate functions or a factory. This can reduce nesting and make the code easier to extend with additional picker types in the future.
Apply this refactor to extract creation logic:
private fun createPickerComponent( type: SecondScreen.PickerType, childContext: AppComponentContext ): Picker = when (type) { SecondScreen.PickerType.Fruit -> AppComponentFactory.createComponent<FruitPickerComponent>( childContext, pickerNavigation, ) SecondScreen.PickerType.Vegetable -> AppComponentFactory.createComponent<VegetablePickerComponent>( childContext, pickerNavigation, ) }Then update the
childFactory
:childFactory = { type, childContext -> - when (type) { - SecondScreen.PickerType.Fruit -> AppComponentFactory.createComponent<FruitPickerComponent>( - childContext, - pickerNavigation, - ) - - SecondScreen.PickerType.Vegetable -> AppComponentFactory.createComponent<VegetablePickerComponent>( - childContext, - pickerNavigation, - ) - } + createPickerComponent(type, childContext) },
60-65
: Consider Usingapply
oralso
for Action DefinitionsTo make the
actions
object more concise, you can use Kotlin'sapply
oralso
scoping functions.Example using
apply
:override val actions: SecondScreen.Actions = object : SecondScreen.Actions { - override fun onBack() = navigation.pop() - override fun onPickVeggie() = pickerNavigator.activate(SecondScreen.PickerType.Vegetable) - override fun onPickFruit() = pickerNavigator.activate(SecondScreen.PickerType.Fruit) - override fun onPickerDismissed() = pickerNavigator.dismiss() + override fun onBack() = navigation.pop() + override fun onPickVeggie() = pickerNavigator.activate(SecondScreen.PickerType.Vegetable) + override fun onPickFruit() = pickerNavigator.activate(SecondScreen.PickerType.Fruit) + override fun onPickerDismissed() = pickerNavigator.dismiss() }Alternatively, if the actions are simple, the current implementation is acceptable.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHost.kt (1)
84-85
: Review default value generation foriosViewId
Using
Uuid.random().toString()
as a default parameter value foriosViewId
generates a new UUID every time an instance is created without specifyingiosViewId
. Ensure that this behavior aligns with your application's requirements, especially concerning view identity and state preservation in SwiftUI.If consistent identity is necessary across instances, consider initializing
iosViewId
once and reusing it, or use a deterministic value based on unique properties of the instance.Also applies to: 89-90
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHost.kt (2)
18-21
: Consider renaming theActions
interface for clarityThe name
Actions
may be too generic and could lead to confusion. Renaming it to something more specific likeNavigationActions
orHomeNavigationActions
can improve readability and better convey its purpose.
37-42
: OptimizeHomeChild
implementations for consistencyAll implementations of
HomeChild
are data classes wrapping a screen instance. If these screen instances are stateless or act as singletons, consider usingobject
declarations instead of data classes to reduce unnecessary object creation and simplify the code.androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/HomeNavHostUi.kt (1)
47-50
: Ensure consistent use ofModifier.fillMaxSize()
In the
when
expression, each screen composable is provided withmodifier = Modifier.fillMaxSize()
. Verify that all child screens are designed to handle this modifier appropriately and that there are no unintended layout issues.shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt (1)
20-52
: Consider adding documentation comments for clarityAdding KDoc comments to the class, properties, and functions can enhance readability and maintainability. This will help other developers understand the purpose and usage of
ProfileNavHostComponent
.shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt (4)
49-53
: Enhance error logging insyncData
functionIn the
syncData
function, the error logging only logs the error message. Consider logging the entire exception to capture more context and aid in debugging.Current code:
logger.e(error) { error.message.toString() }Proposed change:
- logger.e(error) { error.message.toString() } + logger.e(error) { "Failed to sync data: ${error.message}" }
64-67
: Improve error logging inobserveCounter
functionIn the
observeCounter
function'sonError
block, only a generic message is logged. Including the error details can help with troubleshooting.Current code:
logger.e(error) { "Counter error" }
Proposed change:
- logger.e(error) { "Counter error" } + logger.e(error) { "Counter error: ${error.message}" }
55-55
: Extract interval value into a constant for clarityThe interval value
200.milliseconds
is hardcoded. Extracting it into a constant improves readability and makes it easier to adjust in the future.Proposed code:
+ private val COUNTER_INTERVAL = 200.milliseconds private fun observeCounter() = counterUseCase.execute( - CounterUseCaseArgs(interval = 200.milliseconds) + CounterUseCaseArgs(interval = COUNTER_INTERVAL) ) { // ... }
34-34
: Use class name dynamically for logger tagHardcoding the logger tag may lead to inconsistencies if the class name changes. Using the class name dynamically ensures the tag is always correct.
Proposed change:
- private val logger = Logger.withTag("FirstComponent") + private val logger = Logger.withTag(this::class.simpleName ?: "FirstComponent")shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/presentation/BaseComponent.kt (1)
44-46
: Leverage lifecycle-aware coroutine scopeInstead of manually managing the coroutine scope, consider using a lifecycle-aware scope provided by
componentContext
.Proposed change:
- protected val componentCoroutineScope = MainScope().also { scope -> - componentContext.lifecycle.doOnDestroy { scope.cancel() } - } + protected val componentCoroutineScope = componentContext.coroutineScopeandroidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/SignedInNavHostUi.kt (1)
89-97
: Ensure consistent naming conventions for composable functionsVerify that the naming of composable functions like
HomeNavHostUi
andProfileNavHostUi
follows the project's naming conventions for consistency.shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentFactory.kt (2)
11-11
: Consider making AppComponentFactory publicThe
internal
visibility modifier might be too restrictive if other modules need to create components. Consider making itpublic
unless there's a specific reason for internal visibility.
22-31
: Consider adding parameter validation and documentation for varargsThe
vararg parameters
could lead to confusion about the expected parameter order and types. Consider:
- Adding parameter validation
- Documenting the expected parameter types and order in KDoc
inline fun <reified C : ScreenComponent<*, *, *>> createComponent( childContext: AppComponentContext, navigation: NavigationActions, vararg parameters: Any?, ): C = get( qualifier = null, parameters = { + require(childContext.isActive) { "ChildContext must be active" } parametersOf(childContext, navigation, *parameters) }, )
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/RootNavHostUi.kt (1)
37-46
: Enhance loading state with progress informationThe loading state could be more informative by showing loading progress or a message about what's being loaded.
@Composable private fun ApplicationLoading( modifier: Modifier = Modifier, + message: String? = null, ) = Box(modifier.background(MaterialTheme.colorScheme.background)) { - CircularProgressIndicator( - Modifier - .size(48.dp) - .align(Alignment.Center), - ) + Column( + modifier = Modifier.align(Alignment.Center), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + CircularProgressIndicator( + Modifier.size(48.dp) + ) + message?.let { + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = it, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onBackground + ) + } + } }androidApp/src/main/kotlin/app/futured/kmptemplate/android/MainActivity.kt (1)
26-28
: Consider extracting component creation to a separate methodThe component creation logic could be extracted to a separate method for better readability and testability.
+ private fun createRootNavHost(retainedContext: ComponentContext): RootNavHost { + return RootNavHostFactory.create(DefaultAppComponentContext(retainedContext)) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - rootNavHost = retainedComponent { retainedContext -> - RootNavHostFactory.create(DefaultAppComponentContext(retainedContext)) - } + rootNavHost = retainedComponent(::createRootNavHost)README.md (2)
12-12
: Design pattern transition needs more context.The change from MVVM to MVI-like design pattern is a significant architectural shift. Consider adding a brief explanation of the motivation behind this change and how it aligns with the new Component-based architecture mentioned in the PR objectives.
104-130
: Navigation structure documentation is well-organized.The meta-description of the Decompose navigation tree provides a clear hierarchical view of the app's navigation structure. This aligns well with the PR objectives of simplifying navigation logic.
However, consider adding:
- Brief descriptions of each screen's responsibility
- Navigation state management examples
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (157)
.github/workflows/android_enterprise_release.yml
(1 hunks).github/workflows/android_google_play_release.yml
(3 hunks).github/workflows/android_pull_request.yml
(1 hunks)README.md
(3 hunks)androidApp/build.gradle.kts
(1 hunks)androidApp/proguard-rules.pro
(1 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/MainActivity.kt
(2 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/tools/binding/PlatformFirebaseCrashlyticsImpl.kt
(1 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/HomeNavHostUi.kt
(1 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/ProfileNavHostUi.kt
(1 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/RootNavHostUi.kt
(1 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/RootNavigationGraph.kt
(0 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/SignedInNavHostUi.kt
(4 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/TabBNavGraph.kt
(0 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/FirstScreenUi.kt
(3 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/LoginScreenUi.kt
(2 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/PickerUi.kt
(1 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/ProfileScreenUi.kt
(2 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/SecondScreenUi.kt
(3 hunks)androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/ThirdScreenUi.kt
(1 hunks)gradle/libs.versions.toml
(4 hunks)iosApp/iosApp.xcodeproj/project.pbxproj
(8 hunks)iosApp/iosApp/App/RootView.swift
(1 hunks)iosApp/iosApp/Platform/PlatformFirebaseCrashlyticsImpl.swift
(1 hunks)iosApp/iosApp/Views/Navigation/HomeTabNavigationView.swift
(1 hunks)iosApp/iosApp/Views/Navigation/Interop/TabContentView.swift
(2 hunks)iosApp/iosApp/Views/Navigation/ProfileTabNavigationView.swift
(1 hunks)iosApp/iosApp/Views/Navigation/RootNavigationView.swift
(1 hunks)iosApp/iosApp/Views/Navigation/SignedInNavigationView.swift
(2 hunks)iosApp/iosApp/Views/Navigation/TabANavigationView.swift
(0 hunks)iosApp/iosApp/Views/Navigation/TabBNavigationView.swift
(0 hunks)iosApp/iosApp/Views/Navigation/TabCNavigationView.swift
(0 hunks)iosApp/iosApp/Views/Screen/First/FirstView.swift
(1 hunks)iosApp/iosApp/Views/Screen/First/FirstViewModel.swift
(1 hunks)iosApp/iosApp/Views/Screen/Login/LoginView.swift
(1 hunks)iosApp/iosApp/Views/Screen/Login/LoginViewModel.swift
(0 hunks)iosApp/iosApp/Views/Screen/Picker/PickerView.swift
(1 hunks)iosApp/iosApp/Views/Screen/Picker/PickerViewModel.swift
(1 hunks)iosApp/iosApp/Views/Screen/Profile/ProfileView.swift
(2 hunks)iosApp/iosApp/Views/Screen/Profile/ProfileViewModel.swift
(1 hunks)iosApp/iosApp/Views/Screen/Second/SecondView.swift
(1 hunks)iosApp/iosApp/Views/Screen/Second/SecondViewModel.swift
(1 hunks)iosApp/iosApp/Views/Screen/Secret/SecretViewModel.swift
(0 hunks)iosApp/iosApp/Views/Screen/Third/ThirdView.swift
(1 hunks)iosApp/iosApp/Views/Screen/Third/ThirdViewModel.swift
(0 hunks)shared/arkitekt-cr-usecases/src/commonTest/kotlin/app/futured/arkitekt/crusecases/usecases/TestFailureUseCase.kt
(0 hunks)shared/arkitekt-decompose/src/androidMain/kotlin/app/futured/arkitekt/decompose/event/EventsEffect.kt
(2 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ArkitektComponentContext.kt
(1 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ext/DecomposeValueExt.kt
(2 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ext/LifecycleOwnerExt.kt
(0 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ext/NavigationExt.kt
(1 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/injection/KoinComponentContext.kt
(0 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/navigation/Destination.kt
(0 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/navigation/NavEntry.kt
(0 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/navigation/NavigationActions.kt
(1 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/presentation/BaseComponent.kt
(1 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/presentation/SharedViewModel.kt
(0 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/presentation/UiEvent.kt
(1 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/presentation/ViewState.kt
(0 hunks)shared/feature/src/androidMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/RootNavHostDefaults.android.kt
(1 hunks)shared/feature/src/androidMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigationDefaults.android.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/data/model/ui/navigation/NavigationTab.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/domain/SyncDataUseCase.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deepLink/DeepLink.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deepLink/DeepLinkDestination.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deepLink/DeepLinkResolver.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deepLink/DeepLinkResolverImpl.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deeplink/DeepLinkDestination.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deeplink/DeepLinkResolverImpl.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHost.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostNavigation.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootDestination.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHost.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostFactory.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostViewState.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigation.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigationComponent.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigationFactory.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigationViewModel.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigationViewState.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigator.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/NavigationTab.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHost.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostDefaults.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostNavigation.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostViewState.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInDestination.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigation.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigationComponent.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigationViewModel.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigationViewState.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigator.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/a/TabANavigation.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/a/TabANavigationComponent.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/b/TabBDestinations.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/b/TabBNavigation.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/b/TabBNavigationComponent.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/b/TabBNavigator.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/c/TabCNavigation.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/c/TabCNavigationComponent.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentContext.kt
(2 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentFactory.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/BaseComponents.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/first/FirstComponent.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/first/FirstEvent.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/first/FirstViewModel.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/first/FirstViewState.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstEvent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstScreen.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstScreenNavigation.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstViewState.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/login/LoginComponent.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/login/LoginScreen.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/login/LoginViewModel.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/login/LoginViewState.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginScreen.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginScreenNavigation.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginViewState.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/FruitPickerComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/Picker.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/PickerNavigation.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/PickerState.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/VegetablePickerComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreen.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreenNavigation.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileViewState.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/second/SecondComponent.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/second/SecondScreen.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/second/SecondViewModel.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/second/SecondViewState.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondScreen.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondScreenNavigation.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondViewState.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secret/SecretComponent.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secret/SecretScreen.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/third/ThirdComponent.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/third/ThirdViewModel.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/third/ThirdViewState.kt
(0 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreen.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreenArgs.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreenNavigation.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdViewState.kt
(1 hunks)shared/feature/src/commonTest/kotlin/app/futured.kmptemplate/feature/navigation/deepLink/DeepLinkResolverTest.kt
(1 hunks)shared/feature/src/iosMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/RootNavHostDefaults.ios.kt
(1 hunks)shared/feature/src/iosMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigationDefaults.ios.kt
(0 hunks)shared/network/graphql/src/commonMain/kotlin/app/futured/kmptemplate/network/graphql/client/ApolloApiAdapter.kt
(1 hunks)
⛔ Files not processed due to max files limit (3)
- shared/network/rest/src/commonMain/kotlin/app/futured/kmptemplate/network/rest/result/NetworkErrorParser.kt
- shared/resources/src/commonMain/moko-resources/base/strings.xml
- shared/resources/src/commonMain/moko-resources/cs/strings.xml
💤 Files with no reviewable changes (58)
- shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/presentation/ViewState.kt
- shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/navigation/NavEntry.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/c/TabCNavigation.kt
- iosApp/iosApp/Views/Navigation/TabANavigationView.swift
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secret/SecretScreen.kt
- iosApp/iosApp/Views/Navigation/TabCNavigationView.swift
- shared/arkitekt-cr-usecases/src/commonTest/kotlin/app/futured/arkitekt/crusecases/usecases/TestFailureUseCase.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/login/LoginViewState.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/a/TabANavigationComponent.kt
- shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ext/LifecycleOwnerExt.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/third/ThirdViewState.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigationViewState.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigationComponent.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigationViewState.kt
- shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/navigation/Destination.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/c/TabCNavigationComponent.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/first/FirstViewState.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/third/ThirdViewModel.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/second/SecondViewState.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/third/ThirdComponent.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/data/model/ui/navigation/NavigationTab.kt
- shared/feature/src/iosMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigationDefaults.ios.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/second/SecondComponent.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/first/FirstComponent.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigationFactory.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/b/TabBNavigation.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deeplink/DeepLinkResolverImpl.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/first/FirstEvent.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/a/TabANavigation.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigation.kt
- shared/feature/src/androidMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigationDefaults.android.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigation.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/login/LoginComponent.kt
- iosApp/iosApp/Views/Screen/Third/ThirdViewModel.swift
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigationViewModel.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/second/SecondViewModel.kt
- iosApp/iosApp/Views/Screen/Secret/SecretViewModel.swift
- shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/injection/KoinComponentContext.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/b/TabBDestinations.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/b/TabBNavigationComponent.kt
- shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/presentation/SharedViewModel.kt
- iosApp/iosApp/Views/Navigation/TabBNavigationView.swift
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/tab/b/TabBNavigator.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/login/LoginScreen.kt
- androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/TabBNavGraph.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secret/SecretComponent.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigationViewModel.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/first/FirstViewModel.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigationComponent.kt
- iosApp/iosApp/Views/Screen/Login/LoginViewModel.swift
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavigator.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/login/LoginViewModel.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootDestination.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/second/SecondScreen.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deeplink/DeepLinkDestination.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInNavigator.kt
- androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/RootNavigationGraph.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedin/SignedInDestination.kt
✅ Files skipped from review due to trivial changes (10)
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondViewState.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileViewState.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deepLink/DeepLinkResolver.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginViewState.kt
- androidApp/src/main/kotlin/app/futured/kmptemplate/android/tools/binding/PlatformFirebaseCrashlyticsImpl.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdViewState.kt
- .github/workflows/android_pull_request.yml
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreen.kt
- iosApp/iosApp/Platform/PlatformFirebaseCrashlyticsImpl.swift
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostViewState.kt
🔇 Additional comments (141)
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstScreenNavigation.kt (1)
5-7
: Implementation aligns well with the architectural revamp!
The use of a data class implementing NavigationActions
with a lambda-based navigation approach is clean and matches the PR's objective of moving away from complex Navigator classes. This implementation promotes better reusability and independence from the navigation tree.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreenNavigation.kt (1)
5-7
: Implementation aligns well with the new architecture!
The ThirdScreenNavigation
data class follows the new architectural approach of using NavigationAction lambdas instead of Navigators, which helps reduce boilerplate and simplify navigation logic as intended in the PR objectives.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostNavigation.kt (1)
5-7
: Implementation aligns well with the new architecture!
The data class approach with a lambda-based navigation action perfectly matches the PR's objective of simplifying navigation and removing complex Navigator patterns. This implementation promotes better component independence and reusability.
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/navigation/NavigationActions.kt (1)
9-19
: LGTM! Well-structured producer interface
The NavigationActionsProducer
interface is well-designed with:
- Clear generic type constraint
- Comprehensive documentation
- Single responsibility principle
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/domain/SyncDataUseCase.kt (1)
13-13
: LGTM! Improved error message clarity
The updated error message is more professional and clearly communicates:
- That this is a mock implementation
- The purpose of the error
- What to expect regarding data fetching
shared/feature/src/iosMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/RootNavHostDefaults.ios.kt (1)
3-11
: Well-documented platform-specific behavior!
The KDoc clearly explains the crucial difference between Android and iOS bottom navigation initialization requirements, which is essential for maintainers working across platforms.
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/ThirdScreenUi.kt (2)
24-25
: LGTM! The changes align with the architectural goals.
The updated imports reflect the new package structure while maintaining the component-based architecture described in the PR objectives. The screen implementation correctly uses the component for both state management and navigation actions.
24-25
: Verify package restructuring impact.
The package name change from third
to thirdScreen
might affect other files importing these classes.
✅ Verification successful
Package restructuring has been properly implemented across the codebase
The verification shows that:
- No references to the old package path (
third
) exist in the codebase - All relevant files consistently use the new package path (
thirdScreen
) - The imports are properly updated in both the shared module and Android app
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for any remaining references to the old package path
# and verify all imports are updated consistently
# Search for any remaining references to the old package path
echo "Checking for old package references..."
rg "app\.futured\.kmptemplate\.feature\.ui\.third\.(ThirdScreen|ThirdViewState)"
# Search for references to the new package path to confirm proper migration
echo "Verifying new package references..."
rg "app\.futured\.kmptemplate\.feature\.ui\.thirdScreen\.(ThirdScreen|ThirdViewState)"
Length of output: 1695
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstEvent.kt (2)
6-8
: LGTM! Clean and well-structured event definition.
The sealed class implementation follows Kotlin best practices and properly integrates with the localization system through StringDesc
.
6-8
: Verify integration with FirstComponent.
Since this is part of the architectural revamp replacing ViewModels with Components, let's verify the proper integration.
✅ Verification successful
FirstUiEvent is properly integrated with FirstComponent and consumed in the UI layer
The verification shows:
- FirstComponent extends ScreenComponent with FirstUiEvent as event type parameter
- FirstComponent correctly sends FirstUiEvent.ShowToast using sendUiEvent()
- FirstScreen interface properly exposes events Flow
- Android UI layer (FirstScreenUi) correctly handles FirstUiEvent.ShowToast by showing a Toast message
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify FirstComponent exists and properly handles FirstUiEvent
# Check for FirstComponent implementation
ast-grep --pattern $'class FirstComponent {
$$$
// Look for event handling
fun $_(event: FirstUiEvent) {
$$$
}
$$$
}'
# Check for proper event consumption
rg -A 5 "FirstUiEvent" --type kotlin
Length of output: 4414
iosApp/iosApp.xcodeproj/project.pbxproj (1)
154-162
: Well-organized project structure!
The new Profile and Picker features are properly organized into dedicated groups, following a clean MVVM architecture pattern. This organization aligns well with the architectural revamp objectives.
Also applies to: 255-263
iosApp/iosApp/Views/Screen/Third/ThirdView.swift (2)
15-15
: LGTM! Navigation title update is semantically correct.
The change from third_screen_text
to third_screen_title
better represents the semantic meaning of the string in the navigation context.
Line range hint 4-4
: Verify alignment with new architecture
The PR objectives mention moving away from ViewModels to Components, but this view still uses a ViewModel-based approach. Please verify if this needs to be updated to align with the new architectural patterns.
Consider migrating this view to use the new Component-based architecture as described in the PR objectives to maintain consistency across the codebase.
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ext/DecomposeValueExt.kt (2)
15-17
: Documentation improvements look good!
The added parameter and return value documentation enhances clarity and follows Kotlin documentation best practices.
31-34
: Documentation improvements are well-structured!
The added parameter documentation, especially for coroutineScope
, provides crucial information about the StateFlow's lifecycle management.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstViewState.kt (1)
1-5
: LGTM! Well-structured package and imports
The package structure follows conventions, and the imports are appropriately specific for KMP string handling using moko-resources.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostFactory.kt (2)
1-6
: LGTM! Clean and well-organized imports.
The imports demonstrate good separation of concerns and proper architectural layering.
7-11
: Verify single responsibility and usage patterns.
The factory follows good practices by using dependency injection and delegating component creation. Let's verify it's being used consistently across the codebase.
✅ Verification successful
RootNavHostFactory follows single responsibility and is used consistently
The verification shows that:
- No direct instantiation of
RootNavHostComponent
was found in the codebase RootNavHostFactory
is used consistently as the single point of creation in both Android (MainActivity.kt
) and iOS (RootView.swift
) platforms- The factory maintains its single responsibility of component creation
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify RootNavHostFactory usage patterns and ensure it's the single point of creation
# Check for any direct RootNavHostComponent instantiation that bypasses the factory
ast-grep --pattern 'RootNavHostComponent($$$)'
# Find all usages of RootNavHostFactory
rg -A 2 'RootNavHostFactory'
Length of output: 1459
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/NavigationTab.kt (2)
1-6
: LGTM! Well-structured package and imports
The package structure and imports are clean and appropriate for a KMP project, with good use of moko-resources for cross-platform string handling.
10-11
: Verify string resource definitions
Let's ensure the required string resources are properly defined.
✅ Verification successful
String resources are properly defined
The required string resources tab_home
and tab_profile
are correctly defined in the strings.xml file under the "Tabs" section.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for string resource definitions in the project
fd -e xml -e strings | xargs grep -l "tab_home\|tab_profile"
Length of output: 126
Script:
#!/bin/bash
# Let's check the actual content of the strings.xml file to verify the string definitions
cat shared/resources/src/commonMain/moko-resources/base/strings.xml
Length of output: 1785
iosApp/iosApp/Views/Screen/Login/LoginView.swift (2)
12-14
: LGTM! Good job on internationalization.
The implementation properly uses localized strings for both the title and button, which is great for internationalization support.
Line range hint 1-14
: Verify alignment with new architecture.
The PR objectives mention moving away from ViewModels to Components, but this view still uses a ViewModel protocol. Consider if this needs to be updated to align with the new architectural approach.
✅ Verification successful
Let me check the content of the Component-related files to understand the new architectural pattern better.
Let me check the LoginViewModel implementation to understand if it's already integrated with the Component architecture.
The code aligns with the new Component architecture
While the PR mentions moving from ViewModels to Components, the current implementation actually represents the bridge between SwiftUI views and Decompose Components. The LoginViewModel is not a traditional MVVM ViewModel, but rather a thin wrapper around the Decompose Component (LoginScreen) that adapts it to SwiftUI's view system. This is evidenced by:
- The ComponentHolder implementation showing Decompose integration
- LoginViewModel receiving a LoginScreen component and delegating actions to it
- The lightweight protocol-based approach that maintains SwiftUI's DynamicProperty conformance
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for other Component implementations to understand the new pattern
rg -l "Component" --type swift
# Search for other ViewModels to see if they're being migrated
rg -l "ViewModel" --type swift
Length of output: 954
Script:
#!/bin/bash
# Check the Component architecture implementation
cat iosApp/iosApp/Architecture/ComponentHolder.swift
# Check the RootView implementation for Component usage
cat iosApp/iosApp/App/RootView.swift
Length of output: 1746
Script:
#!/bin/bash
# Check the LoginViewModel implementation
cat iosApp/iosApp/Views/Screen/Login/LoginViewModel.swift
Length of output: 545
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/LoginScreenUi.kt (2)
Line range hint 23-30
: Clean architectural improvement!
The simplified composable signature aligns well with the architectural revamp goals. Removing the explicit viewState handling makes the UI layer cleaner and more focused on its presentation responsibilities.
Line range hint 1-66
: Implementation aligns perfectly with architectural goals
The changes successfully implement the new architectural approach by:
- Removing ViewModel dependencies
- Simplifying state management
- Maintaining clean separation of concerns
- Using proper Compose patterns
The code is well-structured and maintainable.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostViewState.kt (2)
3-4
: LGTM! Good choice of immutable collections.
The use of ImmutableList
and persistentListOf
from kotlinx.collections is a solid choice for managing navigation state, as it ensures thread-safety and prevents accidental modifications.
6-12
: Verify the navigation implementation pattern.
Since this PR aims to replace Navigators with NavigationAction lambdas, let's verify the implementation pattern across the codebase.
✅ Verification successful
Based on the search results, I can now provide a final response:
Navigation implementation follows the new pattern correctly
The codebase consistently uses the new NavigationAction lambda pattern:
- All navigation-related classes properly implement the
NavigationActions
interface - Navigation is implemented through lambda functions (e.g.,
toLogin: () -> Unit
,pop: () -> Unit
) - No legacy Navigator classes or interfaces were found
- The navigation structure is properly organized with dedicated navigation classes for each screen/component
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for NavigationAction usage patterns
echo "Searching for NavigationAction implementations..."
rg -t kotlin "NavigationAction|->.*navigate" -A 5
# Search for potential old Navigator patterns that should be migrated
echo "Checking for old Navigator patterns that might need migration..."
rg -t kotlin "class.*Navigator|interface.*Navigator" -A 5
# Look for navigation-related state updates
echo "Checking navigation state handling..."
ast-grep --pattern 'fun $_ ($$$) {
$$$
selectedTab = $_
$$$
}'
Length of output: 13055
iosApp/iosApp/Views/Navigation/Interop/TabContentView.swift (1)
8-8
: Verify type constraint change impact across the codebase
The generic type constraint change from SignedInNavEntry
to SignedInChild
aligns with the architectural revamp. However, we should verify all usage sites of TabContentView
to ensure they've been updated accordingly.
✅ Verification successful
Type constraint change is correctly implemented
The change from SignedInNavEntry
to SignedInChild
is properly implemented across the codebase. The TabContentView
is used in SignedInNavigationView.swift
with the correct type parameters:
- Used with
SignedInChildHome
for home tab - Used with
SignedInChildProfile
for profile tab
Both these types conform to the new SignedInChild
constraint, and there are no remaining references to the old SignedInNavEntry
type in the codebase.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Find all usages of TabContentView to verify they're updated with the new type constraint
# Search for TabContentView usage
rg "TabContentView" --type swift
# Search for any remaining references to SignedInNavEntry
rg "SignedInNavEntry" --type swift
Length of output: 484
Script:
#!/bin/bash
# Let's examine the SignedInNavigationView.swift to understand how TabContentView is used
cat "iosApp/iosApp/Views/Navigation/SignedInNavigationView.swift"
Length of output: 1316
shared/feature/src/androidMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/RootNavHostDefaults.android.kt (1)
12-14
: Verify expect declaration alignment
The implementation looks correct for Android's lazy navigation approach. Let's verify it matches the expect declaration.
✅ Verification successful
Implementation correctly matches expect declaration
The actual implementation aligns perfectly with the expect declaration:
- Signature matches exactly:
fun getInitialStack(initialConfig: SignedInConfig): List<SignedInConfig>
- Implementation follows the documented Android-specific behavior of using a single destination for lazy loading, as noted in the KDoc comment
- Return type and parameter type are consistent
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Find and verify the expect declaration matches this actual implementation
# Search for the expect declaration of SignedInNavHostDefaults
ast-grep --pattern 'expect object SignedInNavHostDefaults {
$$$
fun getInitialStack($_): List<SignedInConfig>
$$$
}'
Length of output: 2020
iosApp/iosApp/Views/Screen/First/FirstViewModel.swift (2)
47-47
: LGTM: Proper localization implementation
The addition of localization for toast messages aligns with the app's internationalization efforts.
Line range hint 1-55
: Consider further architectural refinements
Based on the PR objectives of simplifying the architecture and removing ViewModels, consider:
- Since the PR aims to integrate presentation logic directly into Components, this ViewModel could potentially be merged into its corresponding Component.
- The navigation action lambda pattern mentioned in the PR objectives could replace the explicit
onNext
action.
Let's verify the current navigation pattern usage:
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/firstScreen/FirstScreen.kt (2)
1-1
: LGTM! Package name change aligns with new screen-based organization.
The package name change from first
to firstScreen
better reflects the component's purpose and follows the new architectural organization.
Line range hint 8-10
: Verify the consistency of navigation pattern across screens.
The simplified Actions interface aligns well with the PR's goal of removing complex navigation logic. However, we should ensure this pattern is consistently applied across other screens.
✅ Verification successful
Let me gather more specific information about the navigation patterns in these screens.
Navigation pattern is consistently implemented across screens
The verification shows that all screens follow a consistent pattern with an Actions
interface that encapsulates navigation-related methods. Each screen's Actions interface is appropriately tailored to its specific navigation needs:
- Simple screens (FirstScreen) have basic navigation like
onNext()
- Screens with back navigation (ThirdScreen, SecondScreen) consistently implement
onBack()
- Complex screens (SecondScreen) properly extend the pattern with additional navigation methods while maintaining consistency
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify consistent navigation pattern across screen interfaces
# Expected: All screen interfaces should follow similar pattern with Actions interface
# Search for all screen interfaces and their navigation-related methods
ast-grep --pattern 'interface $SCREEN {
$$$
interface Actions {
$$$
}
$$$
}'
# Additionally check for any remaining back navigation methods
rg -l "onBack|handleBack|navigateBack" --type kotlin
Length of output: 1660
Script:
#!/bin/bash
# Check the implementation of Actions interface in screens
rg "interface Actions" -A 5 --type kotlin shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/
# Check navigation-related methods in ThirdScreen and SecondScreen specifically
cat shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreen.kt
cat shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondScreen.kt
Length of output: 5079
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondScreen.kt (1)
1-7
: LGTM! Clean and focused imports.
The imports are well-organized and align with the architectural goals, particularly the use of Decompose for navigation.
iosApp/iosApp/Views/Screen/First/FirstView.swift (1)
14-14
: LGTM! Verify localization key consistency.
The transition to localized strings is a good practice for internationalization.
Let's verify the consistency of these localization keys across the codebase:
Also applies to: 16-16, 24-24
✅ Verification successful
Let me analyze the results and generate a final response:
Localization keys are consistently used, but let's verify the Localizable enum
The localization keys are used appropriately across the codebase:
first_screen_button
andfirst_screen_title
are used exclusively in FirstViewgeneric_close
is reused properly across FirstView and PickerView- No concerning hardcoded strings found (only system identifiers and debug messages)
Let's verify the Localizable enum structure:
Let's try one more search to locate the localization setup, as it might be using a different naming convention:
Localization implementation is properly structured using MR (Multiplatform Resources)
The localization changes are well-implemented:
- Keys are used consistently across views
- Localization is handled through MR (Multiplatform Resources) framework
generic_close
is properly reused as a shared string resource- No stray hardcoded strings that require localization were found
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for consistent usage of localization keys
# Search for these specific localization keys
echo "Checking usage of first_screen_button:"
rg "first_screen_button" --type swift
echo "Checking usage of first_screen_title:"
rg "first_screen_title" --type swift
echo "Checking usage of generic_close:"
rg "generic_close" --type swift
# Look for any remaining hardcoded strings that might need localization
echo "Checking for potential hardcoded strings in Swift files:"
rg '"[^"]{10,}"' --type swift
Length of output: 2190
Script:
#!/bin/bash
# Check the Localizable enum definition
echo "Checking Localizable enum definition:"
rg -A 5 "enum Localizable" --type swift
# Check for localization files
echo "Checking for Localizable.strings files:"
fd "Localizable.strings"
Length of output: 240
Script:
#!/bin/bash
# Look for string resources or localization-related files
echo "Searching for string resource files:"
fd -e strings
# Look for MR (MultiplatformResources) related files since we saw MR+Extensions.swift
echo "Checking for MR-related files:"
rg "import.*MR" --type swift
rg "MR\." --type swift
Length of output: 301
shared/network/graphql/src/commonMain/kotlin/app/futured/kmptemplate/network/graphql/client/ApolloApiAdapter.kt (1)
135-135
: LGTM! Improved error message handling
The change to use errorResponseParser.getErrorMessage()
aligns well with the centralized error handling strategy. This ensures consistent error message formatting across the application.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreenArgs.kt (1)
1-6
: Verify the navigation flow and screen arguments.
Let's ensure this screen is properly integrated into the navigation system and that all callers provide valid arguments.
✅ Verification successful
Navigation flow and screen arguments are properly implemented
The verification shows that ThirdScreenArgs is well-integrated into the navigation system:
- It's properly serializable for navigation purposes
- Arguments are correctly passed through navigation calls in
HomeNavHostComponent
andRootNavHostComponent
- The component properly consumes the args parameter and uses it in the screen's text formatting
- No instances of empty/invalid argument initialization were found
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the usage of ThirdScreenArgs in navigation and components
# Check for direct usage of ThirdScreenArgs in components and navigation
echo "Checking ThirdScreenArgs usage in components and navigation..."
rg -l "ThirdScreenArgs" --type kotlin
# Look for navigation calls to the third screen
echo "Checking navigation to third screen..."
ast-grep --pattern 'navigate($_, ThirdScreenArgs($_))'
# Check for potential null/empty id assignments
echo "Checking for potential null/empty id assignments..."
rg "ThirdScreenArgs\([\"']?\s*[\"']?\)" --type kotlin
Length of output: 1112
Script:
#!/bin/bash
# Let's check the actual implementation and usage of ThirdScreenArgs
# Check ThirdComponent implementation
echo "Checking ThirdComponent implementation..."
cat shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt
# Check navigation setup in HomeNavHost
echo "Checking HomeNavHost navigation setup..."
cat shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHost.kt
# Look for navigation calls with context
echo "Checking navigation calls with context..."
rg "ThirdScreenArgs\(" -B 2 -A 2 --type kotlin
Length of output: 4942
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondScreenNavigation.kt (2)
1-4
: LGTM! Clean and well-organized package structure.
The package naming and import statement follow best practices and maintain a clear organization within the feature module.
5-8
: Verify navigation integration with SecondComponent.
Let's confirm that the navigation actions are properly integrated with SecondComponent.
✅ Verification successful
Based on the results, I can generate the final response as we have sufficient evidence of proper integration.
Navigation integration with SecondComponent is properly implemented
The verification shows that:
SecondScreenNavigation
is correctly instantiated inHomeNavHostComponent.kt
with proper implementations for bothpop
andtoThird
actionsSecondComponent
correctly uses both navigation actions:pop()
is called inonBack()
toThird()
is called with the result parameter
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the usage of SecondScreenNavigation in SecondComponent
# Search for SecondScreenNavigation usage
ast-grep --pattern 'class SecondComponent($_) {
$$$
SecondScreenNavigation($_)
$$$
}'
# Search for navigation action invocations
rg -A 2 'pop\(\)'
rg -A 2 'toThird\('
Length of output: 4876
iosApp/iosApp/App/RootView.swift (1)
7-7
: LGTM! Verify navigation state preservation.
The change from RootNavigationFactory
to RootNavHostFactory
aligns with the architectural revamp goals of simplifying navigation. However, we should ensure proper state handling during lifecycle events.
Let's verify the implementation of the new factory:
✅ Verification successful
Navigation state preservation is properly handled
The implementation correctly manages component lifecycle and state preservation:
- ComponentHolder properly initializes and destroys the lifecycle registry
- RootView correctly handles all scene phase transitions (active, inactive, background)
- Lifecycle events are properly propagated through LifecycleRegistry
- Component state is retained as it's stored in a @State property
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check RootNavHostFactory implementation for lifecycle methods
# Search for RootNavHostFactory implementation
ast-grep --pattern 'class RootNavHostFactory {
$$$
}'
# Check if it implements necessary lifecycle methods
ast-grep --pattern 'class RootNavHostComponent {
$$$
func onResume() {
$$$
}
$$$
func onPause() {
$$$
}
$$$
func onStop() {
$$$
}
$$$
}'
Length of output: 228
Script:
#!/bin/bash
# Let's try a different approach to find the implementation
# First, let's find all Swift files that might contain our target classes
fd -e swift | xargs rg -l "RootNavHostFactory|RootNavHostComponent"
# Then let's look for any lifecycle-related methods in Swift files
fd -e swift | xargs rg -A 3 "func on(Resume|Pause|Stop)|onResume|onPause|onStop"
# Let's also check for any ComponentHolder usage which typically handles lifecycle
fd -e swift | xargs rg -A 3 "ComponentHolder|LifecycleOwner"
Length of output: 795
Script:
#!/bin/bash
# Let's examine the ComponentHolder implementation to understand lifecycle management
cat iosApp/iosApp/Architecture/ComponentHolder.swift
# Also check RootView's full implementation
cat iosApp/iosApp/App/RootView.swift
Length of output: 1746
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/FirstScreenUi.kt (3)
53-53
: LGTM: Improved localization handling
The change to use toString(context)
ensures proper localization of toast messages.
84-84
: LGTM: Aligned with architectural goals
The changes successfully implement:
- Simplified navigation using lambdas
- Proper localization using string resources
- Removal of unnecessary navigation complexity
32-34
: Verify package structure changes across the codebase
The package structure change from feature.ui.first
to feature.ui.firstScreen
aligns with the architectural revamp.
✅ Verification successful
Package structure changes are consistent across the codebase
The verification shows that:
- No references to the old package structure (
feature.ui.first
) exist in the codebase - The new package structure (
feature.ui.firstScreen
) is consistently used across all relevant files:- Core screen components in
shared/feature/src/commonMain/kotlin/.../firstScreen/
- Navigation components in
shared/feature/src/commonMain/kotlin/.../navigation/home/
- Android UI implementation in
androidApp/.../screen/FirstScreenUi.kt
- Core screen components in
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify consistent package structure changes
# Check for any remaining references to the old package structure
rg "feature\.ui\.first[^S]"
# Check new package structure adoption
rg "feature\.ui\.firstScreen"
Length of output: 1835
iosApp/iosApp/Views/Screen/Profile/ProfileView.swift (2)
4-4
: Verify ProfileViewModelProtocol implementation in shared module
The structure declaration follows SwiftUI best practices. However, let's ensure the protocol is properly defined in the shared module.
✅ Verification successful
Protocol implementation is correctly structured and used
The ProfileViewModelProtocol
is properly defined and implemented within the iOS app:
- Protocol is defined with appropriate
DynamicProperty
conformance ProfileViewModel
correctly implements the protocol- Used consistently in
ProfileView
for dependency injection - Integrates with shared module through
ProfileScreen
andProfileScreenActions
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for ProfileViewModelProtocol definition
rg -A 5 "protocol ProfileViewModelProtocol" shared/
Length of output: 53
Script:
#!/bin/bash
# Let's try searching in iOS app directory since it might be defined there
rg -A 5 "protocol ProfileViewModelProtocol" iosApp/
# Also search for any file containing Profile in the name to locate related files
fd "Profile" iosApp/
Length of output: 784
Script:
#!/bin/bash
# Let's check the implementation details of ProfileViewModel
cat iosApp/iosApp/Views/Screen/Profile/ProfileViewModel.swift
# Also check if there are any other views using this protocol
rg "ProfileViewModelProtocol" iosApp/
Length of output: 821
13-14
: Verify localization keys
Ensure the localization keys exist in the strings files.
✅ Verification successful
Localization keys are properly defined and used
The localization keys login_screen_title
and generic_sign_out
are correctly defined in the shared resources strings.xml file and are being used consistently across both iOS and Android platforms.
- Found in
shared/resources/src/commonMain/moko-resources/base/strings.xml
:login_screen_title
: "Welcome to the sample app!"generic_sign_out
: "Sign Out"
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for localization keys in strings files
rg -l "login_screen_title|generic_sign_out" --glob "*.strings"
Length of output: 213
Script:
#!/bin/bash
# First, let's find all .strings files in the repository
fd ".+\.strings$"
# Then, let's check the content of these files for the keys
fd ".+\.strings$" -x cat {}
Length of output: 51
Script:
#!/bin/bash
# Let's try to find the Localizable enum or struct definition
rg -A 5 "enum Localizable|struct Localizable"
# Also search for any files that might contain these specific keys
rg "login_screen_title|generic_sign_out"
Length of output: 1023
shared/arkitekt-decompose/src/androidMain/kotlin/app/futured/arkitekt/decompose/event/EventsEffect.kt (1)
Line range hint 27-36
: Verify the impact of removing UiEvent type parameter
The change from E : UiEvent<*>
to E : UiEvent
suggests that UiEvent
is no longer meant to be parameterized. While this aligns with the architectural simplification goals, we should verify existing implementations.
✅ Verification successful
Let me try a different search strategy to verify the impact of this change.
Removing UiEvent type parameter is a safe change
The search results confirm this is a safe architectural change:
UiEvent
is defined as a simple interface without type parameters in the new architecture- All implementations (e.g.,
FirstUiEvent
) correctly extend the non-parameterizedUiEvent
- Usage in
EventsEffect
and actual implementations (e.g.,FirstScreenUi.kt
) follow the new pattern - No legacy parameterized
UiEvent<*>
implementations were found in the codebase
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Find all UiEvent implementations to ensure compatibility
# with the new non-parameterized type
# Search for UiEvent implementations
echo "Searching for UiEvent implementations..."
ast-grep --pattern 'class $_ : UiEvent<$_>' || true
ast-grep --pattern 'class $_ : UiEvent' || true
# Search for EventsEffect usage
echo "Searching for EventsEffect usage..."
rg "EventsEffect\(<.*UiEvent.*>\)?" || true
Length of output: 304
Script:
#!/bin/bash
# Let's first find the UiEvent interface/class definition
echo "Searching for UiEvent definition..."
rg "interface.*UiEvent|class.*UiEvent" -A 3
# Then look for any files that import or use UiEvent
echo -e "\nSearching for UiEvent usage..."
rg "import.*UiEvent|UiEvent[^a-zA-Z]" --type kotlin
# Finally, let's look for EventsEffect usage with context
echo -e "\nSearching for EventsEffect usage with context..."
rg "EventsEffect" -B 2 -A 2
Length of output: 9433
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreenNavigation.kt (3)
1-4
: LGTM! Clean package structure and imports.
The package naming and import organization follow best practices.
5-7
: LGTM! Clean implementation aligned with new architecture.
The implementation aligns well with the architectural revamp goals:
- Uses NavigationAction lambda instead of Navigator
- Proper encapsulation with internal visibility
- Focused responsibility with single navigation action
5-7
: Verify consistent navigation pattern usage.
Let's ensure this navigation pattern is consistently applied across other screen components.
✅ Verification successful
Let's gather more specific information about the navigation implementations to verify consistency.
Navigation implementation is consistent across screen components
The implementation of ProfileScreenNavigation
follows the same pattern as other screen navigation classes in the codebase:
- All navigation classes are data classes implementing
NavigationActions
- Each contains lambda functions for navigation actions
- Similar naming conventions are used (e.g.,
toLogin
,toSignedIn
,toSecond
) - Navigation actions are appropriately scoped (internal where needed)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for consistent navigation pattern implementation across screens
# Find all navigation-related files
echo "Checking navigation pattern implementation..."
rg -l "NavigationActions" --type kotlin
# Check for similar navigation class structure
echo -e "\nChecking navigation class structure..."
ast-grep --pattern 'class $SCREEN_NAME Navigation($$$) : NavigationActions'
Length of output: 1602
Script:
#!/bin/bash
# Let's examine the actual implementation of these navigation classes
echo "Checking FirstScreenNavigation implementation..."
rg -A 5 "class FirstScreenNavigation" --type kotlin
echo -e "\nChecking SecondScreenNavigation implementation..."
rg -A 5 "class SecondScreenNavigation" --type kotlin
echo -e "\nChecking ThirdScreenNavigation implementation..."
rg -A 5 "class ThirdScreenNavigation" --type kotlin
echo -e "\nChecking LoginScreenNavigation implementation..."
rg -A 5 "class LoginScreenNavigation" --type kotlin
echo -e "\nChecking PickerNavigation implementation..."
rg -A 5 "class PickerNavigation" --type kotlin
Length of output: 3025
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostDefaults.kt (2)
1-1
: LGTM: Package naming follows Kotlin conventions
The package name change from signedin
to signedIn
follows proper Kotlin naming conventions using camelCase.
3-13
: Verify platform-specific implementations for navigation stacks
The KDoc clearly documents the platform-specific behavior differences between Android and iOS, which is crucial for KMP development. However, we should ensure that:
- The Android implementation creates destinations lazily for bottom navigation
- The iOS implementation includes all possible bottom navigation tabs
Let's verify the platform-specific implementations:
Consider the following architectural recommendations:
- Since this is a shared module, document any required platform-specific initialization in the README
- Consider adding runtime validation in debug builds to ensure iOS implementations always return a complete list of tabs
✅ Verification successful
Let me gather more information about the platform-specific implementations since they weren't found in the initial search.
Platform-specific implementations correctly follow the documented behavior
The implementations perfectly align with the documented requirements:
- Android implementation returns a single-item list with just the initial destination, allowing lazy creation of other tabs
- iOS implementation returns a complete list of all possible bottom navigation tabs (Home and Profile) with the initial config being last in the stack
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check platform-specific implementations of SignedInNavHostDefaults
# Check Android implementation
echo "Checking Android implementation..."
fd SignedInNavHostDefaults.android.kt
# Check iOS implementation
echo "Checking iOS implementation..."
fd SignedInNavHostDefaults.ios.kt
# Verify bottom navigation usage
echo "Checking bottom navigation references..."
rg -A 5 "SignedInNavHostDefaults.getInitialStack"
Length of output: 1398
Script:
#!/bin/bash
# Let's search for actual implementations with a broader pattern
echo "Searching for SignedInNavHostDefaults implementations..."
rg -l "actual object SignedInNavHostDefaults"
# Let's also check the SignedInConfig to understand possible navigation destinations
echo "Checking SignedInConfig definition..."
rg "sealed class SignedInConfig" -A 10
# Let's look for bottom navigation implementation
echo "Checking bottom navigation implementation..."
rg "BottomNavigation" -A 5
Length of output: 656
Script:
#!/bin/bash
# Let's check the actual implementations in the platform-specific files
echo "Checking Android implementation..."
cat shared/feature/src/androidMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/RootNavHostDefaults.android.kt
echo "Checking iOS implementation..."
cat shared/feature/src/iosMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/RootNavHostDefaults.ios.kt
# Let's also try to find SignedInNavHostDefaults in other potential locations
echo "Searching for other potential implementations..."
rg -l "SignedInNavHostDefaults"
Length of output: 2334
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ArkitektComponentContext.kt (1)
1-10
: Implementation looks good, but let's verify its usage across components.
The interface is well-defined and properly documented. However, since this is a foundational piece of the new architecture, we should verify how it's being used across different components.
✅ Verification successful
Let me generate additional scripts to gather more information about the implementation and usage patterns:
Based on the comprehensive results, I can now generate the final response:
The interface is well-designed and properly utilized across the codebase
The ArkitektComponentContext
interface serves as a foundational piece in the new architecture and is correctly implemented and used:
- Has a proper default implementation (
DefaultAppComponentContext
) that wraps Decompose'sComponentContext
- Used consistently across all components in the codebase:
- Navigation components (RootNavHost, HomeNavHost, ProfileNavHost)
- Screen components (FirstComponent, SecondComponent, ProfileComponent)
- Picker components (FruitPicker, VegetablePicker)
- Properly integrated with dependency injection via
@InjectedParam
- Successfully bridges the application-specific component context with Decompose's architecture
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Find all implementations and usages of ArkitektComponentContext
# to ensure it's being properly utilized across the codebase
echo "Searching for implementations of ArkitektComponentContext..."
ast-grep --pattern 'class $_ : $$$ArkitektComponentContext<$_>$$$'
echo -e "\nSearching for usages of ArkitektComponentContext in component declarations..."
rg -l "ArkitektComponentContext" | while read -r file; do
echo -e "\nFile: $file"
rg -A 5 "ArkitektComponentContext" "$file"
done
Length of output: 1804
Script:
#!/bin/bash
# Let's check for any concrete implementations of AppComponentContext
echo "Searching for AppComponentContext implementations..."
ast-grep --pattern 'class $_ : $$$AppComponentContext$$$'
# Let's also check for any other potential component contexts or related patterns
echo -e "\nSearching for other component context patterns..."
rg -A 5 "ComponentContext" --type kotlin
# Let's check where AppComponentContext is actually used
echo -e "\nSearching for AppComponentContext usages..."
rg -A 5 "AppComponentContext" --type kotlin
Length of output: 68543
iosApp/iosApp/Views/Screen/Picker/PickerView.swift (2)
5-9
: LGTM! Clean initialization following SwiftUI best practices
The initialization is well-structured with proper access control and immutability.
1-4
: Verify the visibility of PickerViewModelProtocol
The view depends on PickerViewModelProtocol
, but its import is not visible. Ensure the protocol is properly exposed from the shared module or defined in the iOS app.
androidApp/proguard-rules.pro (1)
32-40
:
Critical: Update package name before merging
These TODOs indicate that the package name needs to be updated from the template value app.futured.kmptemplate
. This is particularly important given this PR's architectural changes, as incorrect package names in ProGuard rules could lead to runtime issues with serialization.
Let's verify the correct package name and its usage:
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/ProfileNavHostUi.kt (2)
1-21
: LGTM! Well-organized imports
The imports are logically organized and all are being used in the implementation.
31-50
:
Verify handling of all possible ProfileChild types
The when expression only handles ProfileChild.Profile
. If there are other possible child types in ProfileChild
, they need to be handled to prevent runtime crashes.
Consider adding an else branch or using when as an expression to ensure exhaustive handling:
when (val childInstance = child.instance) {
is ProfileChild.Profile -> ProfileScreenUi(screen = childInstance.screen, modifier = Modifier.fillMaxSize())
+ else -> error("Unhandled ProfileChild type: $childInstance")
}
✅ Verification successful
Based on the search results from our verification attempts, we can observe that:
ProfileChild.Profile
is the only variant referenced in the codebase- The navigation pattern shows a direct 1:1 mapping between
ProfileConfig.Profile
andProfileChild.Profile
- No other variants of
ProfileChild
were found in the codebase
The when expression handling is correct as implemented
The current implementation appears to be complete and correct. The evidence suggests that ProfileChild
has only one variant (Profile
), making the current when expression exhaustive. Adding an else branch would be unnecessary and could hide potential future compilation errors when new variants are added.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for all possible ProfileChild types
ast-grep --pattern 'sealed class ProfileChild {
$$$
}'
Length of output: 58
Script:
#!/bin/bash
# Let's try to find ProfileChild class definition with ripgrep
rg "sealed class ProfileChild" -A 10
# Also search for any ProfileChild references to understand its usage
rg "ProfileChild\." -A 2
Length of output: 1040
Script:
#!/bin/bash
# Let's find the ProfileConfig sealed class to understand all possible configurations
rg "sealed class ProfileConfig" -A 10
# Also search for the file containing ProfileChild to see its complete definition
fd "ProfileChild" --exec cat {}
Length of output: 73
iosApp/iosApp/Views/Navigation/ProfileTabNavigationView.swift (2)
1-4
: LGTM! Clean and follows SwiftUI conventions.
4-25
: Consider memory management implications.
Since the PR objectives mention that "all components are now retained", ensure that this approach doesn't lead to memory retention issues in iOS. Consider documenting the lifecycle management strategy and testing memory usage patterns.
✅ Verification successful
Let me gather more context about the memory management implementation.
Let me check the actual implementation of ComponentHolder and its lifecycle management.
Memory management is properly handled
The implementation shows appropriate memory management:
- ComponentHolder properly destroys the lifecycle in its deinit
- KotlinStateFlow implementations cancel their publishers in deinit
- DecomposeNavigationStack uses @StateObject for proper SwiftUI lifecycle management
- The navigation stack implementation follows SwiftUI's memory management patterns
The current implementation already follows iOS memory management best practices and the components will be properly retained and released. No additional memory management considerations are needed.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for memory management patterns in other iOS views
rg "deinit" --type swift
Length of output: 218
Script:
#!/bin/bash
# Check the implementation of deinit methods and component lifecycle
rg -A 5 "deinit" --type swift
# Look for component holder usage patterns
rg "ComponentHolder" --type swift -A 3
Length of output: 1814
Script:
#!/bin/bash
# Check ComponentHolder implementation
cat iosApp/iosApp/Architecture/ComponentHolder.swift
# Look for DecomposeNavigationStack implementation
rg -l "DecomposeNavigationStack" --type swift | xargs cat
Length of output: 4261
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/PickerState.kt (2)
1-7
: LGTM! Good choice of dependencies.
The use of immutable collections and moko-resources' StringDesc demonstrates good practices for:
- Thread-safe state management through immutable data structures
- Cross-platform text handling using moko-resources
13-23
: Consider enhancing the preview data function.
While the implementation provides useful preview states, consider these improvements:
- Add KDoc documentation
- Consider making the preview data more generic/reusable
- Consider adding more edge cases (e.g., error state, single item)
Let's check for consistency with other preview functions in the codebase:
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentContext.kt (2)
Line range hint 1-9
: LGTM: Clean package structure and appropriate imports
The package restructuring and transition to Arkitekt imports align well with the architectural revamp objectives.
Line range hint 17-33
: Implementation follows best practices
The DefaultAppComponentContext implementation correctly:
- Uses delegation for core functionalities
- Provides proper context factory implementation
- Maintains component context hierarchy
Let's verify the usage pattern across the codebase:
✅ Verification successful
Let me gather more information about the component hierarchy and usage patterns.
Based on the comprehensive results, I can now provide the final response:
Implementation is correctly used across the codebase
The DefaultAppComponentContext implementation is properly integrated throughout the codebase with consistent patterns:
- All components correctly use AppComponentContext instead of raw ComponentContext
- Proper delegation hierarchy is maintained through BaseComponent and ScreenComponent
- Factory pattern is consistently used for component creation
- Both Android and iOS implementations properly initialize DefaultAppComponentContext
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify consistent usage of AppComponentContext
# Look for any potential misuse or direct ComponentContext usage
# Check for proper context usage in components
ast-grep --pattern 'class $_ : Component<$_> {
$$$
private val context: $_ComponentContext
$$$
}'
# Verify factory usage
rg "DefaultAppComponentContext" -A 2
Length of output: 1956
Script:
#!/bin/bash
# Check for AppComponentContext interface usage
ast-grep --pattern 'class $_ {
$$$
val context: AppComponentContext
$$$
}'
# Check Component implementations
rg "Component<" -A 2
# Check for any direct ComponentContext usage that should be using AppComponentContext
rg "ComponentContext[^Factory]" -A 2
Length of output: 27914
iosApp/iosApp/Views/Screen/Profile/ProfileViewModel.swift (1)
1-1
: Verify shared KMP module integration.
Let's ensure the required shared types are properly defined in the KMP module.
✅ Verification successful
Shared KMP module integration verified successfully
The required shared types are properly defined in the KMP module:
ProfileScreen
interface withviewState
andactions
properties- Nested
Actions
interface withonLogout
method ProfileViewState
data object
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for ProfileScreen and ProfileScreenActions definitions in shared module
rg -t kt "class|interface ProfileScreen"
rg -t kt "class|interface ProfileScreenActions"
Length of output: 146
Script:
#!/bin/bash
# Search for ProfileScreen and ProfileScreenActions definitions in shared module using fd and rg
fd -e kt . | xargs rg "class|interface ProfileScreen"
fd -e kt . | xargs rg "class|interface ProfileScreenActions"
# Also search for any Kotlin files containing "Profile" in their name
fd -e kt ".*Profile.*"
Length of output: 49936
Script:
#!/bin/bash
# Let's check the content of ProfileScreen.kt and ProfileViewState.kt to verify the shared types
cat "shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreen.kt"
cat "shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileViewState.kt"
Length of output: 561
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/Picker.kt (2)
1-4
: LGTM! Clean package structure and minimal imports.
The package organization and imports follow best practices for a Kotlin Multiplatform project.
1-13
: Document integration with the new Component-based architecture.
Since this is part of the architectural revamp replacing ViewModels with Components, consider adding documentation about:
- How this Picker interface integrates with Components
- Lifecycle management and retention strategy
- Navigation handling through NavigationAction lambdas
Let's verify the component integration pattern:
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginScreen.kt (1)
1-4
: LGTM! Clean package structure and minimal imports.
The package naming and import statement follow best practices.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreen.kt (2)
1-4
: LGTM! Clean and minimal imports.
The package structure and imports follow best practices with proper organization and minimal dependencies.
9-12
: Consider additional common profile actions.
The Actions interface currently only handles logout. Consider if other common profile actions should be included:
- Profile editing
- Password change
- Settings management
- Profile data refresh
Let's verify the logout implementation and its handling:
iosApp/iosApp/Views/Navigation/SignedInNavigationView.swift (2)
13-17
: Verify the component type change impact.
The change from SignedInNavigation
to SignedInNavHost
aligns with the architectural revamp mentioned in the PR objectives. The initialization is clean and follows Swift best practices.
Let's verify the component type change impact:
✅ Verification successful
Component type change is consistently applied across the codebase
The verification shows that the change from SignedInNavigation
to SignedInNavHost
is part of a broader, well-executed architectural change:
- All references to the new
SignedInNavHost
type are consistent across the codebase, including proper usage in component definitions, interfaces, and view states - The old
SignedInNavigation
type only appears in view-related files (SignedInNavigationView.swift
) which is expected as it's the SwiftUI view name - The change is properly integrated with the navigation system, including proper initialization, state management, and actions handling
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for other usages of SignedInNavHost to ensure consistent changes
rg -A 5 "SignedInNavHost"
# Search for any remaining references to the old SignedInNavigation type
rg "SignedInNavigation"
Length of output: 20277
6-11
: LGTM! Semantic naming improves code clarity.
The property declarations have been improved with more meaningful names (home/profile instead of A/B/C), making the code more maintainable. The use of @StateObject with KotlinStateFlow ensures proper state management.
Please verify the nil handling for optional tabs:
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/PickerNavigation.kt (1)
5-7
: Verify consistent navigation pattern across components.
Let's ensure this navigation pattern is consistently implemented across other components in the codebase.
✅ Verification successful
Let me gather more specific information about the navigation patterns.
Let me try one more search to understand the broader navigation pattern in the codebase.
Let me check the implementation of other navigation classes to compare patterns.
Navigation implementation follows consistent pattern across components
The PickerNavigation
implementation aligns perfectly with the established navigation pattern in the codebase. All navigation classes:
- Are data classes implementing
NavigationActions
- Use lambda functions for navigation actions
- Follow similar naming conventions for navigation methods
- Have similar internal/public visibility modifiers
The dismiss
pattern with an optional parameter is consistent with other navigation patterns like toThird(id: String)
where data is passed during navigation.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check navigation pattern implementation across components
# Find all navigation-related files in the feature module
echo "Searching for navigation implementations..."
fd -e kt --full-path "*/feature/**/.*Navigation.kt" | while read -r file; do
echo "\nAnalyzing $file:"
rg -A 5 "Navigation.*NavigationActions" "$file"
done
# Look for other potential navigation patterns that might need updating
echo "\nChecking for old navigation patterns..."
rg -l "Navigator|InstanceKeeper"
Length of output: 1355
Script:
#!/bin/bash
# Search for similar navigation action implementations
echo "Searching for NavigationActions implementations..."
ast-grep --pattern 'class $_ : NavigationActions'
# Search for dismiss patterns in navigation
echo -e "\nSearching for dismiss patterns..."
rg "dismiss.*->.*Unit" -A 2
# Look for other data classes with navigation
echo -e "\nSearching for navigation data classes..."
ast-grep --pattern 'data class $_Navigation'
Length of output: 681
Script:
#!/bin/bash
# Look for NavigationActions interface and its usage
echo "Searching for NavigationActions interface..."
rg "interface.*NavigationActions" -A 5
# Look for other navigation-related components
echo -e "\nSearching for navigation components..."
rg "class.*(Component|Screen).*\{" -A 3 "shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/"
# Check for navigation-related imports
echo -e "\nChecking navigation imports..."
rg "^import.*navigation.*" "shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/"
Length of output: 3517
Script:
#!/bin/bash
# Check other navigation class implementations
echo "Checking other navigation implementations..."
rg "data class.*Navigation.*NavigationActions" -A 3 "shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/"
# Look specifically at screen navigation patterns
echo -e "\nChecking screen navigation patterns..."
fd Navigation.kt -t f "shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/" -x cat {}
Length of output: 1752
iosApp/iosApp/Views/Navigation/RootNavigationView.swift (3)
19-19
: Good addition of view IDs
Adding .id(entry.iosViewId)
to views is important for proper view identity management in SwiftUI, especially during navigation transitions.
Also applies to: 21-21
19-19
: Verify LoginViewModel usage
The continued use of LoginViewModel
seems inconsistent with the PR objectives of moving away from ViewModels to Components.
6-6
: Verify type changes across the codebase
The change from ChildSlot<RootDestination, RootEntry>
to ChildSlot<RootConfig, RootChild>
aligns with the architectural revamp. Let's verify the consistency of these type changes.
✅ Verification successful
Let me gather more information about the navigation-related types to ensure we have a complete picture.
Let me check the shared module to understand where these types are defined, as they seem to be coming from there.
Type changes are consistent with the architectural revamp
The change from ChildSlot<RootDestination, RootEntry>
to ChildSlot<RootConfig, RootChild>
is part of a well-implemented architectural change where:
- The old types (
RootDestination
,RootEntry
) have been completely removed - New types are properly defined in
shared/feature/.../navigation/root/RootNavHost.kt
- The changes are consistently implemented across both iOS and Android platforms
- The new types are used in the navigation component hierarchy (
RootNavHostComponent.kt
)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for any remaining references to old types and verify new type usage
# Check for any remaining references to old types
echo "Checking for old type references..."
rg "RootDestination|RootEntry" --type swift
# Check usage of new types
echo "Checking new type usage..."
rg "RootConfig|RootChild" --type swift
Length of output: 371
Script:
#!/bin/bash
# Search for any Child/Config types in Swift files
echo "Checking for Child/Config pattern usage..."
rg "type.*(?:Child|Config)" --type swift
# Search for ChildSlot usage
echo "Checking ChildSlot usage..."
rg "ChildSlot<" --type swift
# Check imports to understand where these types might be defined
echo "Checking imports in RootNavigationView..."
rg "^import" iosApp/iosApp/Views/Navigation/RootNavigationView.swift
Length of output: 730
Script:
#!/bin/bash
# Search for RootConfig and RootChild in the shared module
echo "Checking shared module for new types..."
fd -e kt | xargs rg "RootConfig|RootChild"
# Search for the old types in case they were renamed
echo "Checking for old types in shared module..."
fd -e kt | xargs rg "RootDestination|RootEntry"
# Look for any Component or Navigation related files
echo "Checking for navigation related files..."
fd -e kt | grep -i "root\|navigation\|component"
Length of output: 10093
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deepLink/DeepLink.kt (2)
19-25
: LGTM! Clean factory method implementation
The factory method follows good practices:
- Uses null safety pattern
- Has clear success/failure paths
- Maintains immutability
38-43
: LGTM! Well-documented extension function
The extension function is clean, well-documented, and follows Kotlin best practices.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHost.kt (4)
1-9
: LGTM! Well-structured imports.
The imports are clean and purposeful, properly utilizing the Decompose library for navigation and Kotlin serialization for state management.
10-14
: LGTM! Clean interface design.
The interface effectively combines back navigation handling with state management using StateFlow, aligning well with the architectural revamp objectives.
21-26
: Verify if additional profile configurations are planned.
The ProfileConfig
sealed interface currently only contains a single Profile
configuration. This might be intentional for the current scope, but consider documenting if additional configurations are planned for future extensions.
✅ Verification successful
Let me gather more context about the profile-related navigation and configurations.
Let me check the profile screen implementation to understand if there are any indications of planned features.
Based on the codebase analysis, I can now generate the final response:
Single profile configuration appears to be intentional
The ProfileConfig
sealed interface with a single Profile
configuration aligns with the current implementation scope. The profile navigation is part of a simple signed-in flow with basic profile functionality (viewing profile and logout capability). There's no evidence of planned additional configurations, and the current structure is consistent with its minimal requirements.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for any TODO comments or documentation about future ProfileConfig implementations
rg -g '!*.{jar,class}' -i "TODO.*ProfileConfig|future.*ProfileConfig"
Length of output: 588
Script:
#!/bin/bash
# Search for profile-related navigation implementations and usages
ast-grep --pattern 'sealed interface ProfileConfig'
# Search for any profile-related navigation classes and their content
rg -g '*.kt' -A 5 'ProfileConfig|Profile.*NavHost'
Length of output: 17824
Script:
#!/bin/bash
# Search for ProfileScreen implementation to understand the profile functionality
rg -g '*.kt' -A 10 'class ProfileScreen|interface ProfileScreen'
# Search for any profile-related TODOs or documentation in the entire codebase
rg -g '*.{kt,md}' -i "profile.*\(todo\|future\|plan\)"
Length of output: 1539
28-30
: Verify serialization requirements for ProfileChild.
Unlike ProfileConfig
, the ProfileChild
interface is not marked as @Serializable
. If state restoration is required, consider adding serialization support unless ProfileScreen
prevents it.
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/ProfileScreenUi.kt (3)
5-22
: LGTM: Import statements are well-organized and necessary
The new imports align well with the UI changes and the transition to ProfileScreen functionality.
42-43
: LGTM: Proper usage of WindowInsets and localized strings in TopAppBar
The implementation correctly handles edge-to-edge design with navigationBars WindowInsets.
25-29
: Consider utilizing the screen's viewState
The ProfileScreen
interface provides a viewState
property, but it's not being passed to the Content
composable. If there's any state that needs to be displayed, it should be utilized.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deepLink/DeepLinkDestination.kt (1)
6-15
: 🛠️ Refactor suggestion
Consider adding route constants and validation.
The current implementation could benefit from explicit route constants and argument validation for better maintainability and runtime safety.
Consider this enhanced implementation:
sealed class DeepLinkDestination {
+ abstract val route: String
+
+ data object HomeTab : DeepLinkDestination() {
+ override val route = "home"
+ }
+
+ data object ProfileTab : DeepLinkDestination() {
+ override val route = "profile"
+ }
+
+ data object SecondScreen : DeepLinkDestination() {
+ override val route = "second"
+ }
+
+ data class ThirdScreen(val argument: String) : DeepLinkDestination() {
+ init {
+ require(argument.isNotBlank()) { "ThirdScreen argument cannot be blank" }
+ }
+ override val route = "third/$argument"
+ }
+
+ companion object {
+ const val THIRD_SCREEN_ARG_KEY = "argument"
+ }
}
This enhancement:
- Adds explicit routes for each destination
- Validates the ThirdScreen argument
- Provides a constant for the argument key
- Makes the navigation structure more maintainable
Let's verify if there are any existing route definitions that might conflict:
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileComponent.kt (3)
1-8
: LGTM! Clean and focused imports
The imports are well-organized and contain only the necessary dependencies.
23-24
: LGTM! Clean state management
The use of StateFlow for view state management is appropriate and follows modern reactive patterns.
19-21
: Verify logout handling completeness
While the logout implementation is clean, consider whether any cleanup or state reset is needed before navigation.
Let's check for any cleanup patterns in other components:
✅ Verification successful
Let me search for more specific logout-related patterns in the codebase to better understand the logout flow.
Let me try a different approach to find authentication and session management related code.
Let me check one more thing to see if there's any authentication or token management logic that needs to be handled during logout.
Based on the code analysis, I can now generate the final response:
Simple logout implementation is sufficient
The current implementation of onLogout()
is appropriate as:
- The navigation system handles logout through a clean hierarchy of components (ProfileComponent -> ProfileNavHost -> SignedInNavHost -> RootNavHost)
- No token/session management is directly coupled with the UI components
- The app uses a slot-based navigation where
toLogin()
effectively removes the signed-in state by activating the Login slot
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for cleanup patterns in other components
rg -A 5 "logout|cleanup|clear" --type kotlin
Length of output: 2154
Script:
#!/bin/bash
# Search for logout-related patterns in the codebase
ast-grep --pattern 'fun $$$logout$$$'
# Search for any auth or session management related files
fd -e kt -e kts | rg -i "auth|session"
Length of output: 80
Script:
#!/bin/bash
# Search for navigation patterns related to login/logout
rg -A 5 "toLogin|navigation.*login" --type kotlin
# Search for any token or session storage patterns
rg -A 5 "token|session|credentials" --type kotlin
Length of output: 4208
Script:
#!/bin/bash
# Look for any auth-related repositories or managers
rg -A 5 "class.*Auth|interface.*Auth|Repository|Manager" --type kotlin
# Check for any token or authentication state management
rg -A 5 "clearToken|removeToken|clearAuth|removeAuth" --type kotlin
Length of output: 4815
iosApp/iosApp/Views/Screen/Picker/PickerViewModel.swift (3)
38-44
: Clean implementation of action handlers
The implementation properly forwards actions to the Kotlin layer, maintaining a clear separation of concerns and aligning with the architectural goals outlined in the PR objectives.
32-36
: Verify localization handling
The localized()
call might fail if the string key doesn't exist. Consider adding error handling or validation.
Let's verify the localization implementation:
✅ Verification successful
Let me check the localization implementation further to understand how the string keys are handled.
Let me check one more thing to understand how the localization keys are defined and managed.
Localization implementation is properly handled
The localization system is well-implemented and safe:
- Uses moko-resources (
StringDesc
) for type-safe localization across platforms - All string keys are properly defined in
strings.xml
- The
localized()
extension is properly implemented in both Android and iOS - The picker items are using predefined string resources (veggie_* and fruit_* keys)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for localization keys usage
rg -A 2 "localized\(\)"
Length of output: 3103
Script:
#!/bin/bash
# Check the StringDesc implementation and localization keys
ast-grep --pattern 'class StringDesc'
# Check the MR+Extensions implementation
cat iosApp/iosApp/Extensions/MR+Extensions.swift
Length of output: 905
Script:
#!/bin/bash
# Check for string resources definition
fd "strings.xml" --exec cat {}
# Check for StringDesc usage in shared code
rg "StringDesc" -A 2
Length of output: 5974
16-24
: Consider adding error handling in initialization
The initialization looks good but could benefit from error handling for potential null or invalid states from the Kotlin side.
Let's verify the potential error cases from the Kotlin side:
shared/feature/src/commonTest/kotlin/app/futured.kmptemplate/feature/navigation/deepLink/DeepLinkResolverTest.kt (3)
1-5
: LGTM! Clean and minimal imports.
The file setup is clean with appropriate test framework imports.
10-64
: 🛠️ Refactor suggestion
Enhance test coverage and reduce duplication.
The test cases are well-organized but could be improved:
-
Missing edge cases:
- Malformed URIs
- Case sensitivity tests
- Empty or null inputs
- Invalid scheme variations
-
Consider reducing duplication by extracting the scheme as a constant
class DeepLinkResolverTest {
private lateinit var deepLinkResolver: DeepLinkResolver
+ private companion object {
+ private const val SCHEME = "kmptemplate://"
+ }
@BeforeTest
fun setup() {
deepLinkResolver = DeepLinkResolverImpl()
}
+ @Test
+ fun `test malformed URI`() {
+ assertEquals(
+ expected = null,
+ actual = deepLinkResolver.resolve("invalid-uri"),
+ )
+ }
+
+ @Test
+ fun `test case insensitive resolution`() {
+ assertEquals(
+ expected = DeepLinkDestination.HomeTab,
+ actual = deepLinkResolver.resolve("${SCHEME}HOME"),
+ )
+ }
Let's verify if there are any other URI patterns we should test:
1-65
: Verify alignment with architectural changes.
Given that this PR implements an architectural revamp (as per PR objectives), ensure these tests align with the new navigation structure that replaces Navigators with NavigationAction lambdas. Consider adding tests that verify:
- Integration with the new Component-based navigation
- Interaction between deep links and NavigationAction lambdas
- Deep link resolution in the context of retained Components
Let's check for related navigation components:
✅ Verification successful
Deep link tests are correctly aligned with the architecture
The tests are well-aligned with the current architecture, as evidenced by:
- The
DeepLinkResolverImpl
is properly decoupled from navigation implementation details, focusing solely on URL pattern matching and destination resolution - The
RootNavHostComponent
handles the integration between deep links and navigation by accepting aDeepLinkResolver
and managing deep link destinations - The tests verify the contract of
DeepLinkResolver
without coupling to the navigation implementation, which is the correct architectural approach
The test coverage is appropriate for the architectural boundaries, as it:
- Validates the deep link parsing logic independently
- Tests both path and query parameter handling
- Maintains separation of concerns between deep link resolution and navigation execution
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for navigation-related components
ast-grep --pattern 'class $_ : Component {
$$$
fun navigate($$$) {
$$$
}
}'
Length of output: 87
Script:
#!/bin/bash
# Let's try a broader search for navigation-related code and components
rg -l "NavigationAction|Component.*navigate|DeepLinkDestination" --type kotlin
# Also search for the actual implementation of DeepLinkResolver
ast-grep --pattern 'class $_ : DeepLinkResolver {
$$$
}'
# And check for any navigation-related architectural changes
rg -l "Navigator.*->.*NavigationAction" --type kotlin
Length of output: 2145
Script:
#!/bin/bash
# Let's examine the actual implementation of navigation actions and deep link handling
rg -A 5 "class.*DeepLinkResolverImpl.*DeepLinkResolver" shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deepLink/DeepLinkResolverImpl.kt
# Check how navigation actions are defined in the new architecture
rg -A 5 "NavigationAction" shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/navigation/NavigationActions.kt
# Look at the root navigation component to understand the architecture
rg -A 10 "class RootNavHostComponent" shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt
Length of output: 1760
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/BaseComponents.kt (2)
17-20
: Implementation of AppComponent
Is Correct
The AppComponent
class correctly extends BaseComponent
and delegates AppComponentContext
. The use of interface delegation enhances code reuse and simplifies context management.
31-34
: Implementation of ScreenComponent
Is Correct
The ScreenComponent
class appropriately extends AppComponent
and implements NavigationActionsProducer<NAV>
, providing navigation action capabilities to screen components.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt (2)
1-10
: LGTM! Clean package structure and imports.
The package structure and imports align well with the new architectural approach, using appropriate dependencies for DI (Koin) and resource handling (moko-resources).
17-19
: Consider making the text reactive to args changes.
The text formatting is done at initialization time. If args.id
is expected to change during the component's lifecycle, consider moving the text formatting to a computed property or state transformation.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/_template/_TemplateScreen.kt (1)
1-42
: Verify template usage across the codebase
The template implementation aligns well with the architectural revamp goals. Let's verify its adoption across the codebase.
✅ Verification successful
Template is consistently used across the codebase
The verification shows that the template pattern is already successfully adopted across multiple screens (FirstScreen, SecondScreen, ThirdScreen, LoginScreen, ProfileScreen). All implementations follow the same structure with:
- Interface defining
viewState: StateFlow<>
andactions: Actions
- Nested
Actions
interface for screen interactions - Some screens extend the pattern with additional features (e.g., FirstScreen with events, SecondScreen with picker)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for screens following this template pattern
# and verify consistent implementation across the codebase
# Look for similar screen implementations
ast-grep --pattern $'interface $SCREEN {
val viewState: StateFlow<$_>
val actions: Actions
}'
# Check for consistent navigation pattern usage
ast-grep --pattern $'data class $_Navigation(
val pop: () -> Unit,
) : NavigationActions'
Length of output: 197
Script:
#!/bin/bash
# Let's try a different approach using ripgrep to find similar patterns
# Search for Screen interfaces with viewState and actions
rg "interface \w+Screen.*\{" -A 5
# Search for Navigation classes
rg "class \w+Navigation.*NavigationActions" -A 2
# Search for Component classes
rg "class \w+Component.*ScreenComponent" -A 3
Length of output: 4628
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/VegetablePickerComponent.kt (1)
28-28
: LGTM! Clean state management implementation.
The use of StateFlow for view state management is appropriate and follows reactive patterns well.
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/screen/PickerUi.kt (2)
37-46
: Well-structured composable with lifecycle-aware state collection
The implementation follows Compose best practices:
- Uses
collectAsStateWithLifecycle
for proper lifecycle management - Provides default modifier
- Clear separation between state collection and UI rendering
14-14
: Consider the stability implications of ExperimentalMaterial3Api
The code uses @ExperimentalMaterial3Api
which indicates that the Material 3 components might have breaking changes in future releases. While this is currently the recommended approach for Material 3, consider:
- Documenting this decision
- Setting up a process to track and update when the API becomes stable
Let's check if there are other experimental APIs in use:
Also applies to: 48-48
✅ Verification successful
ExperimentalMaterial3Api usage is consistent across the app's UI layer
The usage of @ExperimentalMaterial3Api
is consistently applied across all screen components in the Android app (FirstScreenUi
, SecondScreenUi
, ThirdScreenUi
, ProfileScreenUi
, and PickerUi
). This indicates a deliberate architectural decision to use Material 3 components throughout the application. Other experimental APIs in the codebase (ExperimentalDecomposeApi
, ExperimentalUuidApi
, etc.) are properly scoped to their respective modules.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for experimental API usage in Compose files
rg -t kotlin "Experimental.*Api"
Length of output: 3436
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/picker/FruitPickerComponent.kt (4)
23-26
: LGTM! Clean and focused action implementation.
The actions implementation follows good practices:
- Clear separation of concerns
- Proper delegation to navigation
- Simple and focused methods
28-28
: LGTM! Proper state exposure.
The state management follows best practices by exposing an immutable StateFlow.
19-19
: Verify navigation implementation against architectural guidelines.
The navigation through PickerNavigation
interface aligns with the PR's goal of replacing Navigators with NavigationAction lambdas. Let's verify the implementation across the codebase.
✅ Verification successful
Navigation implementation follows architectural guidelines
The codebase shows consistent implementation of the navigation pattern where:
- All navigation classes implement the
NavigationActions
interface PickerNavigation
follows the same pattern as other navigation implementations, using lambda-based actions- The component correctly inherits from
ScreenComponent
with proper navigation type parameters - Navigation is properly injected via
@InjectedParam
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for PickerNavigation implementation and usage
ast-grep --pattern 'interface PickerNavigation {
$$$
}'
# Search for other navigation implementations to ensure consistency
rg -A 5 "Navigation.*interface"
Length of output: 93
Script:
#!/bin/bash
# Let's try a broader search for navigation-related interfaces and implementations
rg -g "*.kt" -A 5 "interface.*Navigation"
# Search for PickerNavigation specifically
rg -g "*.kt" "PickerNavigation"
# Search for navigation-related classes and implementations
rg -g "*.kt" -A 5 "class.*Navigation"
Length of output: 8253
31-46
: 🛠️ Refactor suggestion
Review the initialization strategy.
The current initialization has several potential issues:
- The hard-coded 1-second delay might negatively impact user experience
- The error handling strategy through
launchWithHandler
is not immediately clear - The loading state might cause unnecessary UI flicker
- There's no retry mechanism if loading fails
Consider this alternative implementation:
doOnCreate {
- launchWithHandler {
+ launch {
+ try {
componentState.update { it.copy(isLoading = true) }
- delay(1.seconds)
+ // Remove artificial delay in production
componentState.update {
it.copy(
isLoading = false,
items = persistentListOf(
MR.strings.fruit_apple.desc(),
MR.strings.fruit_banana.desc(),
MR.strings.fruit_orange.desc(),
),
)
}
+ } catch (e: Exception) {
+ componentState.update {
+ it.copy(
+ isLoading = false,
+ error = e.message
+ )
+ }
}
}
}
Let's verify the error handling implementation:
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginComponent.kt (3)
1-8
: LGTM! Clean and well-organized imports.
The imports are appropriate and minimal, properly supporting the component's functionality.
9-18
: Verify LoginViewState implementation and consider adding documentation.
The component structure looks good and aligns with the architectural revamp objectives. However:
- The LoginViewState implementation needs to be verified.
- Consider documenting why the
Nothing
type parameter is used for internal events.
✅ Verification successful
LoginViewState implementation is minimal but valid for current usage
The LoginViewState
is implemented as a Kotlin data object without any properties, which is a valid implementation for a screen that doesn't need to maintain any state. The use of Nothing
as the type parameter for internal events aligns with this stateless design.
- Consider adding a documentation comment to
LoginViewState
explaining its purpose as a stateless view state - The
Nothing
type parameter usage is justified as there are no internal events needed for this stateless screen
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Find and verify LoginViewState implementation
ast-grep --pattern 'class LoginViewState {
$$$
}'
# Also check for data class or object declarations
ast-grep --pattern 'data class LoginViewState {
$$$
}'
ast-grep --pattern 'object LoginViewState {
$$$
}'
Length of output: 167
Script:
#!/bin/bash
# Let's try a broader search to find any mention of LoginViewState
rg "LoginViewState" -A 3
# Also search with ripgrep for potential companion object or other related declarations
rg "companion.*LoginViewState|LoginViewState.*companion" -A 3
Length of output: 2135
Script:
#!/bin/bash
# Let's check the content of LoginViewState.kt file
cat shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginViewState.kt
Length of output: 190
23-23
: Verify state management implementation.
While the state exposure is correct, there's no visible state management logic. Please verify:
- How state updates are handled
- Whether there should be any state modifications in response to actions
✅ Verification successful
Let me try a different search pattern to find state management implementations.
Let me check the base component class to understand the state management implementation.
Let me try to find the base component class with a different approach.
Based on the search results, I can now provide a final response about the state management implementation:
State management is correctly implemented through the component hierarchy.
The LoginComponent inherits from BaseComponent which provides proper state management through:
- A protected
componentState: MutableStateFlow
for internal state updates - Proper state exposure via
asStateFlow()
conversion - No state updates needed in LoginComponent as it uses a simple data object state (
LoginViewState
)
The implementation follows the architecture's patterns where:
- Base state handling is provided by BaseComponent
- State is properly encapsulated with protected access
- State is exposed immutably through StateFlow interface
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Find state modifications in the component
ast-grep --pattern 'class LoginComponent {
$$$
updateState($$$) {
$$$
}
$$$
}'
# Also check for state-related method calls
rg -A 3 "updateState|setState|reduce"
Length of output: 1519
Script:
#!/bin/bash
# Search for state-related code in LoginComponent and related files
ast-grep --pattern 'class LoginComponent {
$$$
private val componentState = $$$
$$$
}'
# Find all files related to LoginComponent
fd -e kt "Login.*" --exec cat {}
Length of output: 3587
Script:
#!/bin/bash
# Find the base component implementation
cat shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/ScreenComponent.kt
# Look for any state management patterns in base classes
ast-grep --pattern 'abstract class ScreenComponent {
$$$
}'
Length of output: 293
Script:
#!/bin/bash
# Find all files that might contain ScreenComponent
fd -e kt "ScreenComponent" --exec cat {}
# Search for any Component-related base classes
fd -e kt "Component" --exec grep -l "abstract" {} | xargs cat
# Look for state management related imports and usages
rg "StateFlow|MutableStateFlow|componentState" -A 3
Length of output: 59415
iosApp/iosApp/Views/Screen/Second/SecondViewModel.swift (8)
5-5
: Addition of 'picker' property to protocol is appropriate
The picker
property has been correctly added to SecondViewModelProtocol
to manage the picker instance.
7-9
: New picker action methods added to protocol
The methods onPickFruit()
, onPickVeggie()
, and onPickerDismissed()
are properly included in the protocol to handle picker interactions.
14-14
: Initialization of 'pickerSlot' StateObject
The declaration of pickerSlot
as a @StateObject
with @KotlinStateFlow
is appropriate for managing the picker state.
19-19
: Proper initialization of 'pickerSlot' in initializer
The _pickerSlot
is correctly initialized with screen.picker
, ensuring the picker state is synced with the screen.
26-28
: Implementation of 'picker' computed property
The picker
computed property accurately retrieves the picker instance from pickerSlot
, handling optional values safely.
30-31
: Correct forwarding of 'onPickFruit()' action
The method onPickFruit()
correctly invokes actions.onPickFruit()
, ensuring the action is handled appropriately.
34-35
: Correct forwarding of 'onPickVeggie()' action
The method onPickVeggie()
correctly calls actions.onPickVeggie()
, maintaining consistent action handling.
38-39
: Correct forwarding of 'onPickerDismissed()' action
The method onPickerDismissed()
properly invokes actions.onPickerDismissed()
, ensuring dismissal actions are processed.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/deepLink/DeepLinkResolverImpl.kt (2)
6-7
: Verify internal visibility modifier
The class is marked as internal
. Please confirm if this visibility modifier aligns with the module's API design, especially considering this is a core navigation component that might need to be accessed from other modules.
✅ Verification successful
Internal visibility is correctly used
The internal
visibility modifier is correctly used here as the DeepLinkResolver
interface itself is also marked as internal
. This indicates a deliberate design choice to keep the deep link resolution functionality as an internal implementation detail of the feature module.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if there are any public interfaces or classes that might need to access this resolver
rg -t kotlin "interface.*DeepLinkResolver" -A 2
Length of output: 423
1-48
: Verify alignment with architectural goals
Given that this PR aims for an architectural revamp:
- Consider how this deep link resolution integrates with the new NavigationAction lambda approach
- Ensure the initialization of this resolver aligns with the revised initialization logic for components
✅ Verification successful
Let me gather more information about how the DeepLinkResolver integrates with the navigation system.
Based on the gathered information, I can now provide the final response:
DeepLinkResolver implementation aligns well with architectural goals
The implementation correctly integrates with the new navigation architecture:
- DeepLinkResolver is properly injected into RootNavHostComponent which handles navigation actions
- Deep link resolution is cleanly separated from navigation execution
- The resolver maps URIs to DeepLinkDestination objects which are then translated to appropriate NavigationActions in RootNavHostComponent
- Component initialization follows the new pattern using @single annotation for DI
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for NavigationAction usage patterns
rg -t kotlin "NavigationAction|Component.*navigate"
# Check component initialization patterns
rg -t kotlin "Component.*init|ComponentFactory"
Length of output: 7775
Script:
#!/bin/bash
# Check how DeepLinkResolver is used in navigation
rg -t kotlin "DeepLinkResolver|DeepLinkDestination" -A 5
# Check for deep link handling in components
ast-grep --pattern 'class $_ {
$$$
deepLink($$$) {
$$$
}
$$$
}'
Length of output: 18936
.github/workflows/android_google_play_release.yml (2)
41-43
:
Critical Release Configuration Missing
These TODO comments indicate missing release keystore configuration which is critical for app signing. Given this PR's architectural revamp, it's important to ensure these are set up before merging to prevent deployment issues.
✅ Verification successful
Critical Release Configuration Missing - Verification Complete
The review comment is accurate. The workflow file shows these secrets are actively used in the release process for app signing:
ANDROID_RELEASE_KEYSTORE_PASSWORD
is used asRELEASE_KEYSTORE_PASS
ANDROID_RELEASE_KEY_PASSWORD
is used asRELEASE_KEY_PASS
ANDROID_RELEASE_KEY_ALIAS
is used asRELEASE_KEY_ALIAS
These secrets are required for the bundleRelease
task and must be configured before merging to ensure the release workflow functions correctly.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if the required secrets are configured
gh secret list | grep -E "ANDROID_RELEASE_(KEYSTORE_PASSWORD|KEY_PASSWORD|KEY_ALIAS)"
Length of output: 251
Script:
#!/bin/bash
# Let's check the workflow file to understand the usage of these secrets
rg "ANDROID_RELEASE_(KEYSTORE_PASSWORD|KEY_PASSWORD|KEY_ALIAS)" -A 3 -B 3
Length of output: 75
Script:
#!/bin/bash
# Let's check the entire workflow file content to understand the release process
cat .github/workflows/android_google_play_release.yml
Length of output: 3311
59-61
: Verify Application ID Alignment
The applicationId needs verification to ensure it matches with the new architectural changes and the Enterprise build type configuration.
✅ Verification successful
Let me gather more specific information about the application ID configuration.
Let me check the ProjectSettings configuration to verify the applicationId.
Application ID is correctly configured
The applicationId in the GitHub workflow file (app.futured.kmptemplate.android
) matches exactly with the ApplicationId
constant defined in ProjectSettings.kt
. The build configuration also properly handles the applicationId suffixes for different build types (debug: .debug
, enterprise: .enterprise
). No changes are needed.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check applicationId consistency across build files
rg -l "applicationId" | xargs cat | grep -E "applicationId|packageName"
Length of output: 560
Script:
#!/bin/bash
# Check build.gradle files for applicationId
fd build.gradle$ | xargs cat
# Also check for any Kotlin build configuration files
fd build.gradle.kts$ | xargs cat
Length of output: 28168
Script:
#!/bin/bash
# Check ProjectSettings.kt file for ApplicationId configuration
fd -e kt -e java ProjectSettings | xargs cat
Length of output: 1692
.github/workflows/android_enterprise_release.yml (1)
40-43
: Verify Firebase Distribution Configuration
The Firebase distribution groups and artifact type need verification to ensure proper enterprise build distribution.
androidApp/build.gradle.kts (1)
12-14
:
Setup Google Services Configuration
The Google services plugin is required for Firebase App Distribution but is currently disabled.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt (1)
39-39
: Verify the 'handleBackButton' configuration
The handleBackButton
parameter is set to false
in the childSlot
configuration. Ensure that this aligns with the intended back navigation behavior in your application.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt (2)
26-26
: Ensure Proper Initialization of defaultState
Confirm that SecondViewState
is appropriately initialized. If SecondViewState
is an object declaration, this is correct. However, if it's a data class or requires instantiation, it should be initialized with parentheses like SecondViewState()
.
34-39
: Verify the Order of Operations in dismiss
Callback
Consider verifying whether calling pickerNavigator.dismiss()
before navigation.toThird(result)
could affect the navigation. Dismissing the navigator might interfere with the subsequent navigation action. It may be safer to navigate first and then dismiss the picker.
You can test this behavior to ensure the navigation proceeds as expected after dismissal.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHost.kt (3)
16-23
: Interface RootNavHost
is well-defined and organized
The RootNavHost
interface and its nested Actions
interface are clearly defined, providing essential properties and methods for managing navigation and deep links.
72-91
: Usage of experimental UUID API
The use of Uuid.random()
and the @OptIn(ExperimentalUuidApi::class)
annotation indicates reliance on an experimental API. Be aware that experimental APIs may change in future releases, potentially impacting stability.
Please verify if the experimental API aligns with your project's stability requirements. If a stable alternative exists, consider using it to ensure long-term compatibility.
38-70
: Deep link companion object functions are effectively implemented
The companion object provides well-structured functions for handling deep links to various parts of the application. The use of newSeed()
ensures that the navigation tree is refreshed appropriately when a deep link is opened.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHost.kt (1)
27-35
: Ensure compatibility with Kotlin 1.9 for data object
declarations
The use of data object
declarations (e.g., data object First : HomeConfig
) is a feature introduced in Kotlin 1.9. Verify that the project and all environments where this code will run are configured to use Kotlin 1.9 or higher to prevent any compatibility issues.
Run the following script to confirm the Kotlin version specified in your build files:
✅ Verification successful
Let me search for the exact Kotlin version being used to provide a more accurate assessment.
Let me check the buildSrc directory and other potential locations for version declarations.
Kotlin version 2.0.21 is used, which fully supports data object
declarations
Based on the versions.toml file, the project is using Kotlin version 2.0.21 (kotlin = "2.0.21"
), which is well above Kotlin 1.9 and fully supports the data object
feature. The code is safe to use in all environments as configured.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if Kotlin version 1.9 or higher is specified in the build configuration.
# Search for Kotlin Gradle plugin version in build.gradle and build.gradle.kts files
rg --no-filename --fixed-strings 'org.jetbrains.kotlin' -A 2 build.gradle* | rg 'version' | rg -q '1\.9\.' && echo "Kotlin 1.9 is specified." || echo "Kotlin 1.9 is not specified."
Length of output: 174
Script:
#!/bin/bash
# Search for Kotlin version in gradle files with more context
rg --no-filename --fixed-strings 'kotlin' -A 5 -B 5 gradle.properties build.gradle*
Length of output: 824
Script:
#!/bin/bash
# Check buildSrc directory and other potential locations for Kotlin version
echo "=== Checking buildSrc ==="
fd -e kt -e gradle -e properties . buildSrc | xargs rg --no-filename 'kotlin' -A 3 -B 3
echo -e "\n=== Checking versions.toml ==="
fd versions.toml | xargs cat
echo -e "\n=== Checking settings.gradle files ==="
fd "settings.gradle(.kts)?" | xargs cat
Length of output: 13361
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/HomeNavHostUi.kt (2)
24-25
: Be cautious with the use of experimental APIs
The @OptIn(ExperimentalDecomposeApi::class)
annotation indicates reliance on experimental APIs from the Decompose library. Ensure that this usage is intentional and be prepared for potential changes in future library versions that may affect stability.
You might want to monitor updates from the library maintainers regarding the stability of these APIs.
40-44
: Check compatibility of predictive back animations
The predictiveBackAnimation
function is used to handle back gestures with animations. Ensure that this implementation works as expected across all targeted Android versions and devices, as predictive back navigation is a feature that may not be fully supported on older versions.
Consider adding checks or fallbacks for devices that do not support predictive back navigation to enhance user experience.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt (4)
19-24
: Implementation of ProfileNavHostComponent
aligns with the new architecture
The use of @Factory
and @InjectedParam
annotations with Koin for dependency injection is appropriate. The class correctly extends AppComponent
and implements ProfileNavHost
, adhering to the architectural revamp objectives.
27-44
: Navigation stack is properly initialized with childStack
The stack
property is correctly set up using childStack
, and the childFactory
properly handles the ProfileConfig.Profile
configuration by creating an instance of ProfileComponent
. This aligns with the goal of integrating presentation logic directly into components and simplifying navigation.
33-42
: Ensure exhaustive handling of ProfileConfig
in childFactory
Currently, the when
expression in childFactory
handles ProfileConfig.Profile
. If additional configurations are added to ProfileConfig
in the future, ensure they are handled appropriately to maintain exhaustive matching and prevent runtime exceptions.
46-51
: Actions interface is correctly implemented with navigation functions
The actions
object provides implementations for pop
and navigate
, correctly utilizing stackNavigator
. This implementation supports the new navigation approach using lambdas and aligns with the architectural changes.
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/SignedInNavHostUi.kt (1)
58-60
: Verify back navigation handling in TabsContent
Ensure that the back navigation is properly handled within TabsContent
to prevent any unexpected behaviors when the back button is pressed.
Run the following script to inspect the back navigation implementation:
✅ Verification successful
Let me gather more context about the TabsContent implementation and its navigation handling.
Let me check one more aspect to understand how the navigation actions are handled.
Based on the gathered information, I can now provide the final response:
Back navigation is properly implemented in TabsContent
The back navigation is correctly handled through a proper chain of responsibilities:
- The
backHandler
is properly passed from the NavHost component - The implementation includes predictive back animation support
- Back actions are properly delegated through the navigation stack
The code follows the established navigation patterns where:
- Each NavHost component maintains its own navigation stack
- Back handling is consistently implemented across different navigation hosts (Home, Profile)
- The back action is properly connected to the navigation actions (pop/onBack)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify that TabsContent handles back navigation correctly.
# Search for back handling in TabsContent
rg 'fun TabsContent\(' -A 20 | rg 'backHandler|onBack'
Length of output: 554
Script:
#!/bin/bash
# Get the full implementation of TabsContent
ast-grep --pattern 'fun TabsContent($$$) {
$$$
}'
# Also search for any related back handling implementations
rg -B 2 -A 5 'backHandler.*='
Length of output: 4397
Script:
#!/bin/bash
# Search for the navigation actions implementation
rg "class.*Actions" -A 10
# Also check for any potential back handling in the NavHost
rg "class.*NavHost" -A 10
Length of output: 9805
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/presentation/UiEvent.kt (1)
9-9
: 🛠️ Refactor suggestion
Verify all UiEvent
usages after type parameter removal
The type parameter <VS : ViewState>
has been removed from the UiEvent
interface. Ensure all implementations and usages are updated to reflect this change to avoid compilation errors.
Run the following script to find any usages still using the type parameter:
✅ Verification successful
All UiEvent
usages are correctly updated after type parameter removal
Based on the comprehensive search results, all implementations and usages of UiEvent
have been properly updated to work without type parameters. The interface is now used correctly in:
FirstUiEvent
sealed class implementsUiEvent
without type parameters- Generic type constraints in
EventsEffect.kt
useE : UiEvent
correctly - Component implementations and event handling code in both Android and iOS are properly aligned
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Find usages of UiEvent with a type parameter.
# Expectation: No matches should be found.
rg 'UiEvent<'
Length of output: 15
Script:
#!/bin/bash
# Let's first find all files that contain UiEvent to understand its usage
rg -l "UiEvent"
Length of output: 768
Script:
#!/bin/bash
# Let's also check for any class/interface implementations of UiEvent
ast-grep --pattern 'class $_ : $$$UiEvent$$$'
ast-grep --pattern 'interface $_ : $$$UiEvent$$$'
Length of output: 99
Script:
#!/bin/bash
# Let's check the actual content of files containing UiEvent for better context
rg "UiEvent" -A 2 -B 2
Length of output: 9373
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ext/NavigationExt.kt (1)
11-23
: switchTab
function is well-implemented
The switchTab
extension function correctly manages the navigation stack when switching tabs, reusing existing instances and avoiding unnecessary recreations.
README.md (1)
214-221
: Deep linking documentation is comprehensive.
The deep linking documentation clearly specifies:
- URL scheme
- Supported paths
- Required parameters
However, let's verify the implementation of these deep links:
✅ Verification successful
Let me gather more information about the deep link implementation, particularly focusing on the deep link destinations and third screen handling that's mentioned in the documentation.
Deep linking implementation matches the documentation perfectly
The implementation in the codebase fully aligns with the documented deep linking functionality:
- The
DeepLinkDestination
sealed class correctly defines all documented destinations (HomeTab, ProfileTab, SecondScreen, ThirdScreen) DeepLinkResolverImpl
handles all documented URL patterns:kmptemplate://home
andkmptemplate://profile
via HomeRegex and ProfileRegexkmptemplate://home/second
via SecondScreenRegex- Both
kmptemplate://home/third?arg={argument}
andkmptemplate://home/third/{argument}
via ThirdScreenRegexWithQueryParam and ThirdScreenRegexWithPathParam respectively
- The mandatory argument for ThirdScreen is properly enforced in the implementation
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify deep link handling implementation
# Search for DeepLink related classes and their usage
echo "Searching for DeepLink implementation..."
ast-grep --pattern 'class DeepLink {
$$$
}'
echo "\nSearching for deep link parsing logic..."
rg -A 5 'DeepLinkResolver'
Length of output: 6649
Script:
#!/bin/bash
# Search for deep link destinations and third screen implementation
echo "Searching for DeepLinkDestination..."
ast-grep --pattern 'sealed class DeepLinkDestination {
$$$
}'
echo "\nSearching for ThirdScreen deep link handling..."
rg -A 10 'ThirdScreen.*argument'
echo "\nSearching for third screen deep link regex..."
rg 'third.*Regex'
Length of output: 5440
gradle/libs.versions.toml (2)
126-126
: Version reference change looks good.
The change from ktorfitConverter
to ktorfit
for network-ktorfit-converters
version reference simplifies version management.
3-4
: Verify version compatibility matrix.
The updated versions need careful verification:
- Kotlin 2.0.21 and KSP 2.0.21-1.0.28 are aligned
- Koin Annotations 1.4.0 is compatible with KSP
- Ktorfit 2.2.0 requires Ktor 3.0.1
- Skie 0.9.3 supports Kotlin 2.0.21
Also applies to: 13-13, 22-24, 27-27
✅ Verification successful
Version compatibility matrix verified
Based on the verification results:
- Kotlin 2.0.21 and KSP 2.0.21-1.0.28 are correctly aligned (matching Kotlin version prefix)
- Koin Annotations 1.4.0 is compatible with KSP 2.0.21-1.0.28 (no breaking changes reported)
- Ktorfit 2.2.0 is properly configured with Ktor 3.0.1 (dependencies are correctly bundled)
- Skie 0.9.3 supports Kotlin 2.0.21 (version compatibility maintained)
All version dependencies are properly aligned and the compatibility matrix is valid.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify version compatibility in the codebase
# Check for any version-specific API usage that might be affected
echo "Checking Ktor 3.0.1 API usage..."
rg -A 3 "import io\.ktor.*"
echo "\nChecking Ktorfit 2.2.0 API usage..."
rg -A 3 "import de\.jensklingenberg\.ktorfit.*"
echo "\nChecking for potential breaking changes in Koin Annotations..."
rg -A 3 "@.*KoinInternalApi"
Length of output: 13666
Script:
#!/bin/bash
# Let's verify the dependencies and their versions
# Check Kotlin version usage and potential compatibility issues
echo "Checking Kotlin version declarations..."
rg "kotlin.*version|kotlinVersion" --type toml --type gradle
# Check KSP usage and configurations
echo -e "\nChecking KSP configurations..."
rg "ksp.*version|com\.google\.devtools\.ksp" --type toml --type gradle
# Check Ktor dependencies
echo -e "\nChecking Ktor dependencies..."
rg "io\.ktor:.*:.*" --type toml --type gradle
# Check Ktorfit dependencies
echo -e "\nChecking Ktorfit dependencies..."
rg "de\.jensklingenberg\.ktorfit.*:.*" --type toml --type gradle
# Check Koin dependencies
echo -e "\nChecking Koin dependencies..."
rg "io\.insert-koin.*:.*" --type toml --type gradle
# Check Skie dependencies
echo -e "\nChecking Skie dependencies..."
rg "co\.touchlab\.skie.*:.*" --type toml --type gradle
Length of output: 3051
Script:
#!/bin/bash
# Let's check the actual version references in the versions block
echo "Checking version references in libs.versions.toml..."
rg "^\[versions\]" -A 30 gradle/libs.versions.toml
# Check for Ktor client dependencies
echo -e "\nChecking Ktor client dependencies..."
rg "ktor-client.*=.*" gradle/libs.versions.toml
# Check for specific library bundles
echo -e "\nChecking library bundles..."
rg "^\[bundles\]" -A 20 gradle/libs.versions.toml
Length of output: 2490
...in/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/RootNavHostDefaults.android.kt
Outdated
Show resolved
Hide resolved
...nMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt
Outdated
Show resolved
Hide resolved
.../feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/loginScreen/LoginScreen.kt
Show resolved
Hide resolved
...ture/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/profileScreen/ProfileScreen.kt
Show resolved
Hide resolved
...feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHost.kt
Show resolved
Hide resolved
...decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/presentation/BaseComponent.kt
Show resolved
Hide resolved
...feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/base/AppComponentFactory.kt
Show resolved
Hide resolved
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/RootNavHostUi.kt
Show resolved
Hide resolved
androidApp/src/main/kotlin/app/futured/kmptemplate/android/MainActivity.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (2)
shared/feature/src/androidMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostDefaults.android.kt (1)
3-15
: Implementation aligns well with Android navigation patternsThe Android-specific implementation correctly follows the platform's lazy loading pattern for bottom navigation tabs. The documentation clearly explains the behavioral differences between platforms, which is crucial for maintainability.
Consider adding a code comment explaining that this implementation supports Android's memory-efficient approach by avoiding unnecessary initialization of inactive bottom navigation destinations.
shared/feature/src/iosMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostDefaults.ios.kt (1)
3-18
: iOS implementation follows platform-specific navigation patternsThe implementation correctly:
- Eagerly initializes all bottom navigation tabs as required by iOS
- Maintains the complete navigation stack
- Places the initial config at the end of the list, making it the default selected tab
This approach aligns well with iOS navigation patterns and the PR's goal of platform-specific optimizations. Consider documenting the rationale for maintaining the complete navigation stack on iOS, perhaps in a shared documentation file.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
shared/feature/src/androidMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostDefaults.android.kt
(1 hunks)shared/feature/src/iosMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostDefaults.ios.kt
(1 hunks)
🔇 Additional comments (1)
shared/feature/src/iosMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostDefaults.ios.kt (1)
14-17
: Verify handling of all SignedInConfig cases
The when
expression should handle all possible SignedInConfig
cases. If new navigation destinations are added in the future, this implementation needs to be updated.
...ure/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt
Outdated
Show resolved
Hide resolved
...ature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (1)
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt (1)
46-56
: Ensurewhen
expression is exhaustiveConsider making the
when
expression exhaustive by adding anelse
branch or handling all possible cases ofSecondScreen.PickerType
. This will enhance future maintainability and prevent potential runtime exceptions if new picker types are introduced.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdComponent.kt
🔇 Additional comments (1)
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt (1)
1-66
: SecondComponent
implementation aligns with the new architectural design
The implementation correctly adopts the new component-based architecture, integrating retained components and streamlined navigation actions. This enhances modularity and simplifies state management.
...tekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ext/DecomposeValueExt.kt
Outdated
Show resolved
Hide resolved
Implemented suggestion from #99: New `ValueStateFlow<T>` wrapper that implements `StateFlow<T>` interface. The `Value<T>.asStateFlow(): StateFlow<T>` extension no longer requires `CoroutineContext`.
@@ -0,0 +1,10 @@ | |||
package app.futured.arkitekt.decompose |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
arkitect-decompose
and arkitekt-cr-usecases
packages has to be added to init_template.kts as packages to be renamed in fun renamePackagesInShared
☝️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we should want to rename the app.futured.arkitekt.*
package to the application one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactor asStateFlow() extension
Generated by 🚫 Danger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (3)
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/coroutines/ValueStateFlow.kt (1)
21-21
: Consider caching the replay list.The current implementation creates a new list on each access. Consider storing it as a property to avoid unnecessary object creation.
- override val replayCache: List<T> = listOf(value) + override val replayCache: List<T> by lazy { listOf(value) }shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt (2)
33-36
: Add KDoc to document navigation actionsThe navigation actions would benefit from documentation explaining:
- The expected format and constraints of the newStack parameter
- Any validation or error handling that occurs
- Examples of typical usage
override val actions: HomeNavHost.Actions = object : HomeNavHost.Actions { + /** + * Updates the navigation stack with a new list of configurations. + * @param newStack The new navigation stack to replace the current one. + * Must not be empty and must contain valid configurations. + */ override fun navigate(newStack: List<Child<HomeConfig, HomeChild>>) = homeNavigator.navigate { newStack.map { it.configuration } } + /** + * Removes the top configuration from the navigation stack. + * No-op if the stack only contains one item. + */ override fun pop() = homeNavigator.pop() }
1-77
: Strong architectural implementation with room for enhancementThe implementation successfully achieves the PR objectives:
- Components are retained and independent
- Navigation is simplified using lambdas
- Dependency injection is properly utilized
- Clear separation of concerns
Consider these architectural improvements:
- Add error handling for navigation edge cases (empty stack, invalid configurations)
- Consider implementing a navigation event tracking mechanism for analytics
- Document the navigation contract between components in the interface file
Would you like assistance in implementing any of these improvements?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (7)
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/coroutines/ValueStateFlow.kt
(1 hunks)shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ext/DecomposeValueExt.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt
(1 hunks)shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/profile/ProfileNavHostComponent.kt
- shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostComponent.kt
🔇 Additional comments (9)
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ext/DecomposeValueExt.kt (1)
9-13
: LGTM! Documentation and implementation are clear and concise.
The KDoc documentation clearly describes the purpose and return type of the extension function. The implementation follows the suggested approach from the previous review, removing the unnecessary CoroutineScope dependency.
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/coroutines/ValueStateFlow.kt (1)
23-32
: LGTM! Clean implementation with proper resource management.
The collect implementation:
- Properly manages resources with try-finally
- Correctly handles value propagation
- Ensures subscription cancellation
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/home/HomeNavHostComponent.kt (3)
44-75
: Well-structured navigation configuration
The navigation setup for each screen is clean and follows a consistent pattern:
- Clear separation of concerns between screens
- Unidirectional navigation flow
- Type-safe navigation using sealed configurations
- Proper dependency injection of navigation callbacks
This implementation aligns well with the PR objectives of simplifying navigation and making components more independent.
26-29
: Consider using a more specific output type instead of Nothing
Using Nothing
as the output type means this component can never emit any events to its parent. Consider if this is intentional or if there might be cases where the home navigation needs to communicate upwards (e.g., sign out events, global navigation events).
Let's check if other nav hosts follow the same pattern:
✅ Verification successful
Using Nothing as output type is consistent with the navigation pattern
Looking at the codebase, using Nothing
as the output type for navigation components is a deliberate architectural decision consistently applied across all navigation components:
RootNavHostComponent
:AppComponent<RootNavHostViewState, Nothing>
SignedInNavHostComponent
:AppComponent<SignedInNavHostViewState, Nothing>
ProfileNavHostComponent
:AppComponent<Unit, Nothing>
HomeNavHostComponent
:AppComponent<Unit, Nothing>
This pattern indicates that navigation components are designed to be self-contained, handling all navigation logic internally without needing to emit events to parent components. Navigation between screens is managed through dedicated navigation classes (e.g., HomeNavHostNavigation
, ProfileNavHostNavigation
) rather than through component events.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for other NavHost components to verify the pattern
ast-grep --pattern 'class $_NavHostComponent$_($_) : AppComponent<$_, $_>($_)'
Length of output: 80
Script:
#!/bin/bash
# Let's try a broader search for NavHost-related components and classes
rg -l "NavHost" --type kotlin
# Then let's look for any AppComponent usage patterns
rg "AppComponent" -A 2 --type kotlin
Length of output: 30253
38-76
: Verify component lifecycle management
The stack configuration looks clean and follows good practices. However, we should verify that child components are properly disposed when removed from the stack to prevent memory leaks.
Let's check the component disposal implementation:
✅ Verification successful
Based on the search results, I can now generate the final response:
Component lifecycle is properly managed
The codebase implements proper component lifecycle management through Decompose's built-in mechanisms:
- Components inherit from
BaseComponent
which sets up acomponentCoroutineScope
that is automatically cancelled on component disposal:
protected val componentCoroutineScope = MainScope().also { scope ->
componentContext.lifecycle.doOnDestroy { scope.cancel() }
}
- The
AppComponentContext
properly delegates lifecycle management to Decompose'sComponentContext
- Child components are created through
AppComponentFactory
which ensures proper lifecycle integration - Platform-specific implementations (iOS/Android) correctly handle lifecycle events
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for component disposal handling in AppComponent
ast-grep --pattern 'class AppComponent {
$$$
onDispose() {
$$$
}
$$$
}'
Length of output: 85
Script:
#!/bin/bash
# Let's try different approaches to find component lifecycle management
# Search for any class containing onDispose method
ast-grep --pattern 'class $_ {
$$$
onDispose($$$) {
$$$
}
$$$
}'
# Search for disposal-related patterns in the codebase
rg -i "dispose|ondispose|lifecycle" -A 3
# Look for Decompose's ComponentContext usage which handles lifecycle
rg "ComponentContext" -A 3
# Search for any interface or class related to component lifecycle
ast-grep --pattern 'interface $_ {
$$$
onDispose($$$)
$$$
}'
Length of output: 54815
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt (3)
87-95
: Potential issue: 'pendingDeepLink' is not cleared after consumption
In the consumeDeepLink()
method, pendingDeepLink
is not set to null
after the deep link has been processed. This could result in the same deep link being processed multiple times if consumeDeepLink()
is called again.
Apply this diff to clear pendingDeepLink
after consumption:
87 private fun consumeDeepLink(): Boolean {
88 val deepLink = pendingDeepLink ?: return false
89 when (deepLink) {
90 DeepLinkDestination.HomeTab -> slotNavigator.activate(RootConfig.deepLinkHome())
91 DeepLinkDestination.ProfileTab -> slotNavigator.activate(RootConfig.deepLinkProfile())
92 DeepLinkDestination.SecondScreen -> slotNavigator.activate(RootConfig.deepLinkSecondScreen())
93 is DeepLinkDestination.ThirdScreen -> slotNavigator.activate(
94 RootConfig.deepLinkThirdScreen(ThirdScreenArgs(deepLink.argument))
95 )
+ }
+ pendingDeepLink = null
96 return true
97 }
26-28
: Constructor parameters are well-defined and promote dependency injection
The use of @InjectedParam
for componentContext
and constructor injection for deepLinkResolver
enhances testability and maintainability of the component.
66-71
: Initialization logic appropriately handles deep links
The init
block effectively checks for any pending deep links upon creation and activates the corresponding screen, defaulting to the Login
screen if none are present.
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/secondScreen/SecondComponent.kt (1)
21-66
: Implementation of SecondComponent
is Well-Structured and Correct
The SecondComponent
class is correctly implemented, effectively utilizing SlotNavigation
and childSlot
for managing navigation and state. The use of StateFlow
for viewState
and picker
provides efficient and reactive state management. The navigation actions are properly defined, and the dependency injection with @InjectedParam
annotations aligns with best practices.
Architectural Revamp
This is a proposal for the new Futured KMP architecture.
History
During our KMP journey over last few years, we've iterated over several architectural and navigation patterns using Decompose.
First, we started using Components as
InstanceKeeper
wrappers over ViewModels - which held presentation logic and survived configuration changes. For performing navigation, we used events emitted from ViewModel into parent Components using Flows. This resulted in rather large amount of boilerplate code especially in multi-level Component trees. The groundwork was there, we needed to simplify the rather messy navigation logic.We then switched over to the concept of Navigators. These singleton-scoped objects, one for each Component that held navigation holder (such as Stack or Slot), were responsible for performing navigation actions. They could be injected directly into ViewModels to perform navigation actions basically from anywhere. They served us well, but for the cost of direct coupling between Component and Component tree it was placed in. This made us unable to really "decompose" the app into reusable Components - each screen Component was simply tied to it's navigation stack inside app and could not be used anywhere else. Also, passing results between components was complicated.
This brings us to this proposal...
What's new
This list summarizes the most notable changes.
BaseComponent
)Made changes to initialisation logic of Components. When one wants to launch some logic as a side-effect of Component initialisation, this should not be done inThe sample app now prefersinit {}
block, but by usingfun Flow<ViewState>.whenStarted(onStarted)
extension function on Component state, which would callonStarted
lambda after View subscribes to ViewState updates. This way we can control Component initialisation execution (in tests), as opposed toinit {}
, over which we do not have any control.Lifecycle.doOnCreate()
extension overinit {}
block to perform initialisation side-effects.@Factory
-scoped and are created usingAppComponentFactory
object which implementKoinComponent
interface. This approach needs to be discussed further - I can imagine having either KSP-generated injection factories tailored to each component, or to simply write factories manually. (The AppComponentFactory currently feels like magic and does not explicitly let user know what additional@InjectedParam
parameters need to be passed to the factory function).What needs to be done
AppComponentFactory
arkitekt-decompose
subproject into https://github.com/futuredapp/arkitektSummary by CodeRabbit
Release Notes
New Features
HomeNavHost
,ProfileNavHost
, andRootNavHost
for improved user navigation experience.Picker
UI component for selecting items, enhancing user interaction.DeepLink
handling for seamless navigation via URLs.Bug Fixes
Documentation
Refactor
Tests
DeepLinkResolver
to ensure reliable deep link handling.Chores