Skip to content
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

Open
wants to merge 49 commits into
base: develop
Choose a base branch
from
Open

Architectural Revamp #99

wants to merge 49 commits into from

Conversation

matejsemancik
Copy link
Member

@matejsemancik matejsemancik commented Nov 26, 2024

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.

  • All components are now retained. This is assumed to be safe in a way we use and structure our architecture, because we never inject Android Activity, or any other dependencies that could potentially leak from Components. This change also enables us to implement features described in next points.
  • Components become "ViewModels". Since the Component itself is retained, it makes ViewModel previously kept in InstanceKeeper, unnecessary. All presentation logic is now moved directly to Component (subclass of BaseComponent)
  • Ditched the concept of Navigators. Instead, we provide a set of NavigationAction lambdas to each component. These actions are specific for each component and describe a set of navigation actions that can be performed by this component. It is responsibility of parent component to handle invocations of these functions by performing necessary mutations to the navigation, or handle them in any other suitable way. This change ultimately makes Components reusable and independent of the navigation tree they are placed in. This approach imposes little bit more of boilerplate as compared to Navigators, however not as much as compared to the first version with events. (In my opinion, this is small price to pay for flexibility).
  • 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 in init {} block, but by using fun Flow<ViewState>.whenStarted(onStarted) extension function on Component state, which would call onStarted lambda after View subscribes to ViewState updates. This way we can control Component initialisation execution (in tests), as opposed to init {}, over which we do not have any control. The sample app now prefers Lifecycle.doOnCreate() extension over init {} block to perform initialisation side-effects.
  • Components can inject dependencies using classic constructor injection. All components are @Factory-scoped and are created using AppComponentFactory object which implement KoinComponent 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).
  • Refactored the sample app and added navigation result example (Picker component)

What needs to be done

  • Document core architectural components
  • Refine API
  • (will be solved separately) Create a developer documentation (using mkdocs) describing common navigation and presentation use-cases for new team members
  • Make decision about AppComponentFactory
    • We will investigate KSP
  • Update README.md
  • (will be solved separately) Have discussion about extracting arkitekt-decompose subproject into https://github.com/futuredapp/arkitekt

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced new navigation components: HomeNavHost, ProfileNavHost, and RootNavHost for improved user navigation experience.
    • Implemented a Picker UI component for selecting items, enhancing user interaction.
    • Added DeepLink handling for seamless navigation via URLs.
    • Added new composable functions for home and profile navigation, improving the user experience.
  • Bug Fixes

    • Resolved issues related to deep link navigation and state management.
  • Documentation

    • Updated README to reflect architectural changes and new navigation structures.
  • Refactor

    • Streamlined navigation logic across various components for better maintainability.
    • Enhanced state management and error handling in API interactions.
    • Updated various components to use new navigation patterns and state management techniques.
  • Tests

    • Added unit tests for DeepLinkResolver to ensure reliable deep link handling.
    • Introduced tests for new navigation components to validate functionality.
  • Chores

    • Updated dependency versions for improved performance and security.

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.
Copy link

coderabbitai bot commented Nov 27, 2024

Walkthrough

The 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 Enterprise build type, alongside updates to ProGuard rules for Kotlin serialization.

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 SecretComponent, TabANavigationComponent, and TabBNavigationComponent, have been removed, indicating a consolidation of navigation logic. New classes and interfaces have been created to define navigation actions and states more explicitly, ensuring a robust and type-safe navigation experience.

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
Loading

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@matejsemancik matejsemancik self-assigned this Dec 1, 2024
@matejsemancik matejsemancik marked this pull request as ready for review December 1, 2024 20:29
Copy link

@coderabbitai coderabbitai bot left a 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 documentation

Adding 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,
 ) : NavigationActions
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/thirdScreen/ThirdScreenNavigation.kt (1)

5-7: Consider adding KDoc documentation

To 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,
 ) : NavigationActions
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostNavigation.kt (3)

5-7: Add KDoc documentation for better maintainability

Consider 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 action

The current name toLogin could be more specific about the action's purpose in the signed-in context, such as navigateToReauthentication or signOutAndNavigateToLogin.

 data class SignedInNavHostNavigation(
-    val toLogin: () -> Unit,
+    val navigateToReauthentication: () -> Unit,
 ) : NavigationActions

3-7: Implementation aligns well with the architectural revamp

The implementation follows the PR objectives by:

  1. Using simple lambda-based navigation instead of Navigator classes
  2. Keeping the navigation actions focused and specific to the signed-in state
  3. 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 documentation

To 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,
 ) : NavigationActions
shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/navigation/NavigationActions.kt (1)

3-7: Consider defining a base contract for NavigationActions

While 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 duration

While 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 goals

The 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 initialization

The 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 and HomeTabNavigationView 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 patterns

These 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 implementation

While the implementation is correct, consider these improvements for better maintainability and clarity:

  1. Add KDoc documentation explaining the purpose and usage of this state class in the new architecture
  2. Consider using a more meaningful default value or making it required in the constructor
  3. 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 Pattern

As part of the architectural revamp from MVVM to Component-based architecture, ensure that:

  1. This state class remains immutable and is only modified through the Component
  2. Consider implementing equals() and hashCode() explicitly if custom equality comparison is needed for state updates
  3. Plan for potential state expansion as features grow
  4. Consider adding a copy() method with named parameters for better state updates in the Component

Would 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 enhancements

The 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:

  1. Add clear documentation about the enum's purpose
  2. Provide a default tab for initial navigation
  3. 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 values

While 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 variant

Consider 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:

  1. The hardcoded navigation tabs might limit reusability across different signed-in experiences
  2. 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 constants

The 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 approach

The 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 buttons

While 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 states

The sheet presentation logic is clean, but consider handling potential edge cases:

  1. Add loading state handling while the picker is being initialized
  2. 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 objectives

The 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 goals

The 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:

  1. Evaluating if FirstViewModelProtocol should be replaced with a Component-based approach as mentioned in the PR objectives
  2. Reviewing if the events handling aligns with the new architectural pattern
  3. 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 implementation

The 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:

  • ApolloNetworkExceptionNetworkError.ConnectionError
  • ApolloHttpExceptionNetworkError.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 completeness

The 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,
 ) : NavigationActions
iosApp/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:

  1. Adding comments explaining the new navigation pattern and lifecycle management
  2. Documenting how navigation state should be preserved during scene phase changes
  3. 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 pattern

The 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 constant

While 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 documentation

The extension function change maintains consistency with EventsEffect. Consider adding KDoc documentation to explain its usage, similar to the well-documented EventsEffect 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,
 ) : NavigationActions
shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/signedIn/SignedInNavHostDefaults.kt (1)

3-3: Architectural alignment with PR objectives confirmed

The rename to SignedInNavHostDefaults aligns well with the PR's goal of removing Navigator classes in favor of NavigationAction lambdas. The NavHost pattern provides a more flexible and reusable navigation structure.

Consider documenting the following in the codebase:

  1. Migration guide from Navigator pattern to NavHost pattern
  2. 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 experience

While the implementation is functional, consider these improvements:

  1. Add accessibility identifiers for testing
  2. Center the ProgressView for better visual balance
  3. Consider supporting custom item views for more flexible presentation
  4. 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 objectives

The 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 architecture

Given the architectural changes (retained Components, NavigationAction lambdas, etc.), consider adding rules to:

  1. Preserve Component classes and their state
  2. Protect navigation-related lambdas from obfuscation
  3. 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.Serializable
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/ProfileNavHostUi.kt (2)

22-27: Document the experimental API usage

The 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 practices

The 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 properties

Example:

+/// 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: ProfileNavHostActions
shared/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 integration

While 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 guidelines

As this represents a significant architectural shift from Koin to Arkitekt, consider:

  1. Adding migration guidelines for existing components
  2. Documenting any breaking changes
  3. 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:

  1. Replace String with a type-safe model for picked items
  2. 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: Refactor childFactory to reduce code duplication

The childFactory contains repeated code for creating child components for each SignedInConfig. 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 in homeTab and profileTab properties

Both homeTab and profileTab properties use similar code to retrieve the first instance of a specific SignedInChild. You could create a generic helper function to eliminate duplication and make the code more concise.


78-85: Ensure onTabSelected handles already selected tabs efficiently

In 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 calling switchTab to optimize performance.


23-28: Add documentation for the SignedInNavHostComponent class

Providing 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:

  1. Clean separation of concerns with distinct state and actions
  2. Reactive state management with StateFlow
  3. Interface-first approach enabling better testability
  4. 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 naming

The change from RootNavigation to RootNavHost 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.onDeepLink

This 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 validation

The 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 documentation

As this is part of the architectural revamp, it would be valuable to include documentation showing:

  1. Example regex patterns for common deep link scenarios
  2. Integration with the new Component-based architecture
  3. 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 value

A 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 states

The 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:

  1. Adds type-safe result handling
  2. Maintains consistency with the architectural revamp
  3. 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 maintainability

The 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 patterns

The 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.profileScreen
iosApp/iosApp/Views/Navigation/HomeTabNavigationView.swift (2)

6-12: Consider memory management implications

The 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 actions

The 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 clarity

Consider 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 integration

The 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:

  1. Add KDoc documentation for the test class describing its purpose
  2. 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 Descriptions

In the AppComponent KDoc, the parameter VS 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 in ScreenComponent.


23-30: Consistent Terminology for Parameter Descriptions

In the ScreenComponent KDoc, VS is described as "The type of the view state." Ensure consistency with the AppComponent 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 in onBack(), consider making it private and only exposing necessary navigation actions through the Actions 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 patterns

While 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 documentation

The 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 template

While 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 usage

The 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 indicator

Consider 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 items

Consider these improvements:

  1. Add content description for better accessibility
  2. 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 configurations

Consider adding:

  1. Multiple preview configurations for different themes and sizes
  2. 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 goals

The 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 maintainability

Adding 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 expressions

In 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: Refactor childFactory to reduce code duplication

The 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:

  1. Checking if navigation should be allowed (e.g., form validation, loading state)
  2. 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:

  1. Adding KDoc comments explaining the component's role in the new architecture
  2. Documenting how to test this component, especially regarding the initialization logic mentioned in the PR objectives
  3. 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 API

You are opting into the ExperimentalUuidApi by using the @OptIn(ExperimentalUuidApi::class) annotation to utilize Uuid.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 Architecture

Given 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 components
  • shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/ui/ - UI components
  • shared/arkitekt-decompose/src/commonMain/kotlin/app/futured/arkitekt/decompose/ - base component classes
🔗 Analysis chain

Line range hint 67-77: Review Enterprise Build Type Configuration

The 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 kotlin

Length of output: 58174

shared/feature/src/commonMain/kotlin/app/futured/kmptemplate/feature/navigation/root/RootNavHostComponent.kt (1)

33-33: Unused logger instance

The 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' assignment

The contentWindowInsets parameter expects a WindowInsets 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 accessibility

The Icon component has contentDescription = 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 the Row

Instead of manually adding a Spacer, consider using horizontalArrangement = 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 for ModalBottomSheetState

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 Creation

For 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 Using apply or also for Action Definitions

To make the actions object more concise, you can use Kotlin's apply or also 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 for iosViewId

Using Uuid.random().toString() as a default parameter value for iosViewId generates a new UUID every time an instance is created without specifying iosViewId. 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 the Actions interface for clarity

The name Actions may be too generic and could lead to confusion. Renaming it to something more specific like NavigationActions or HomeNavigationActions can improve readability and better convey its purpose.


37-42: Optimize HomeChild implementations for consistency

All implementations of HomeChild are data classes wrapping a screen instance. If these screen instances are stateless or act as singletons, consider using object 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 of Modifier.fillMaxSize()

In the when expression, each screen composable is provided with modifier = 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 clarity

Adding 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 in syncData function

In 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 in observeCounter function

In the observeCounter function's onError 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 clarity

The 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 tag

Hardcoding 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 scope

Instead 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.coroutineScope
androidApp/src/main/kotlin/app/futured/kmptemplate/android/ui/navigation/SignedInNavHostUi.kt (1)

89-97: Ensure consistent naming conventions for composable functions

Verify that the naming of composable functions like HomeNavHostUi and ProfileNavHostUi 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 public

The internal visibility modifier might be too restrictive if other modules need to create components. Consider making it public unless there's a specific reason for internal visibility.


22-31: Consider adding parameter validation and documentation for varargs

The vararg parameters could lead to confusion about the expected parameter order and types. Consider:

  1. Adding parameter validation
  2. 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 information

The 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 method

The 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:

  1. Brief descriptions of each screen's responsibility
  2. Navigation state management examples
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d5ac3aa and 1e619da.

📒 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:

  1. That this is a mock implementation
  2. The purpose of the error
  3. 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:

  1. Since the PR aims to integrate presentation logic directly into Components, this ViewModel could potentially be merged into its corresponding Component.
  2. 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 and first_screen_title are used exclusively in FirstView
  • generic_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 and RootNavHostComponent
  • 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 in HomeNavHostComponent.kt with proper implementations for both pop and toThird actions
  • SecondComponent correctly uses both navigation actions:
    • pop() is called in onBack()
    • 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
🏁 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 and ProfileScreenActions
🏁 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-parameterized UiEvent
  • 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:

  1. The Android implementation creates destinations lazily for bottom navigation
  2. The iOS implementation includes all possible bottom navigation tabs

Let's verify the platform-specific implementations:

Consider the following architectural recommendations:

  1. Since this is a shared module, document any required platform-specific initialization in the README
  2. 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's ComponentContext
  • 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: ⚠️ Potential issue

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: ⚠️ Potential issue

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:

  1. ProfileChild.Profile is the only variant referenced in the codebase
  2. The navigation pattern shows a direct 1:1 mapping between ProfileConfig.Profile and ProfileChild.Profile
  3. 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:

  1. Add KDoc documentation
  2. Consider making the preview data more generic/reusable
  3. 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 with viewState and actions properties
  • Nested Actions interface with onLogout 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:

  1. How this Picker interface integrates with Components
  2. Lifecycle management and retention strategy
  3. 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:

  1. Adds explicit routes for each destination
  2. Validates the ThirdScreen argument
  3. Provides a constant for the argument key
  4. 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:

  1. Missing edge cases:

    • Malformed URIs
    • Case sensitivity tests
    • Empty or null inputs
    • Invalid scheme variations
  2. 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:

  1. Integration with the new Component-based navigation
  2. Interaction between deep links and NavigationAction lambdas
  3. 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:

  1. The DeepLinkResolverImpl is properly decoupled from navigation implementation details, focusing solely on URL pattern matching and destination resolution
  2. The RootNavHostComponent handles the integration between deep links and navigation by accepting a DeepLinkResolver and managing deep link destinations
  3. 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<> and actions: 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:

  1. Documenting this decision
  2. 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:

  1. The hard-coded 1-second delay might negatively impact user experience
  2. The error handling strategy through launchWithHandler is not immediately clear
  3. The loading state might cause unnecessary UI flicker
  4. 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:

  1. The LoginViewState implementation needs to be verified.
  2. 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:

  1. How state updates are handled
  2. 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:

  1. A protected componentState: MutableStateFlow for internal state updates
  2. Proper state exposure via asStateFlow() conversion
  3. 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:

  1. Consider how this deep link resolution integrates with the new NavigationAction lambda approach
  2. 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: ⚠️ Potential issue

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 as RELEASE_KEYSTORE_PASS
  • ANDROID_RELEASE_KEY_PASSWORD is used as RELEASE_KEY_PASS
  • ANDROID_RELEASE_KEY_ALIAS is used as RELEASE_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: ⚠️ Potential issue

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 implements UiEvent without type parameters
  • Generic type constraints in EventsEffect.kt use E : 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 and kmptemplate://profile via HomeRegex and ProfileRegex
    • kmptemplate://home/second via SecondScreenRegex
    • Both kmptemplate://home/third?arg={argument} and kmptemplate://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:

  1. Kotlin 2.0.21 and KSP 2.0.21-1.0.28 are aligned
  2. Koin Annotations 1.4.0 is compatible with KSP
  3. Ktorfit 2.2.0 requires Ktor 3.0.1
  4. 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:

  1. Kotlin 2.0.21 and KSP 2.0.21-1.0.28 are correctly aligned (matching Kotlin version prefix)
  2. Koin Annotations 1.4.0 is compatible with KSP 2.0.21-1.0.28 (no breaking changes reported)
  3. Ktorfit 2.2.0 is properly configured with Ktor 3.0.1 (dependencies are correctly bundled)
  4. 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

Copy link

@coderabbitai coderabbitai bot left a 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 patterns

The 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 patterns

The 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1e619da and 9244cc7.

📒 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.

Syntey
Syntey previously approved these changes Dec 5, 2024
Copy link

@coderabbitai coderabbitai bot left a 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: Ensure when expression is exhaustive

Consider making the when expression exhaustive by adding an else branch or handling all possible cases of SecondScreen.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

📥 Commits

Reviewing files that changed from the base of the PR and between 9244cc7 and b2123eb.

📒 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.

Syntey
Syntey previously approved these changes Dec 5, 2024
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
Copy link
Collaborator

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 ☝️

Copy link
Member Author

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

6 Warnings
⚠️ Feature or fix PR title should include JIRA-ID and short description.
⚠️ Feature or fix PR branch name should include JIRA-ID and short description.
⚠️ This pull request is too big.
⚠️ KLIB resolver: The same 'unique_name=annotation_commonMain' found in more than one library: /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/feature/build/kotlinTransformedMetadataLibraries/nativeMain/androidx.annotation-annotation-1.7.0-commonMain-kNhS6A.klib, /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/feature/build/kotlinTransformedMetadataLibraries/nativeMain/org.jetbrains.compose.annotation-internal-annotation-1.7.0-commonMain-BD0V2w.klib
⚠️ KLIB resolver: The same 'unique_name=resources_commonMain' found in more than one library: /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/feature/build/kotlinTransformedMetadataLibraries/commonMain/dev.icerock.moko-resources-0.24.0-commonMain-3vAcDQ.klib, /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/resources/build/classes/kotlin/metadata/commonMain
⚠️ KLIB resolver: The same 'unique_name=runtime_commonMain' found in more than one library: /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/feature/build/kotlinTransformedMetadataLibraries/commonMain/org.jetbrains.compose.runtime-runtime-1.7.0-commonMain-9pDeVQ.klib, /Users/runnerm1/actions-runner/_work/kmp-futured-template/kmp-futured-template/shared/feature/build/kotlinTransformedMetadataLibraries/nativeMain/app.cash.sqldelight-runtime-2.0.1-commonMain-wyRdBg.klib
2 Messages
📖 iosAppUITests: Executed 1 test, with 0 failures (0 expected) in 16.034 (16.034) seconds
📖 iosAppTests: Executed 1 test, with 0 failures (0 expected) in 0.001 (0.001) seconds

Generated by 🚫 Danger

Copy link

@coderabbitai coderabbitai bot left a 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 actions

The 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 enhancement

The 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:

  1. Add error handling for navigation edge cases (empty stack, invalid configurations)
  2. Consider implementing a navigation event tracking mechanism for analytics
  3. 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

📥 Commits

Reviewing files that changed from the base of the PR and between b2123eb and 730ce55.

📒 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 a componentCoroutineScope 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's ComponentContext
  • 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants