--- description: Enforces Jetpack Compose best practices for code organization, performance, and maintainability. This rule provides guidelines for writing efficient and idiomatic Compose code. globs: *.kt --- - **Code Organization and Structure** - **Directory Structure:** - Organize composables by feature or screen. Each feature should have its own directory. - Example: app/ src/ main/ java/ com/example/app/ feature_a/ FeatureAScreen.kt FeatureAViewModel.kt components/ FeatureAButton.kt FeatureATextField.kt feature_b/ ... - **File Naming Conventions:** - Use PascalCase for composable function names (e.g., `MyComposable`). - Use descriptive names that clearly indicate the composable's purpose. - Name files after the primary composable they contain (e.g., `MyComposable.kt`). - **Module Organization:** - Separate UI code (composables) from business logic (ViewModels, repositories). Use modules to enforce this separation. - Consider using feature modules for large applications to improve build times and maintainability. - **Component Architecture:** - Design composables as small, reusable components. - Follow the single responsibility principle: each composable should have a single, well-defined purpose. - Use a hierarchical component structure to build complex UIs from smaller, simpler components. - **Code Splitting Strategies:** - Break down large composables into smaller, more manageable parts. - Use inline functions judiciously. Overuse can hurt performance. - Consider using `derivedStateOf` to only recompose when necessary based on derived state. - **Common Patterns and Anti-patterns** - **Design Patterns:** - **State Hoisting:** Move state as high as possible in the composable tree to maximize reusability and testability. Pass state down as parameters. - **Unidirectional Data Flow:** Data flows down the composable tree, and events flow up. This makes it easier to reason about the state of the UI. - **Composition Local:** Use sparingly to provide implicit dependencies to composables. Avoid overusing as it can make dependencies less explicit. - **ViewModel:** Use a ViewModel to hold UI state and handle business logic. This separates the UI from the data layer and makes the UI easier to test. - **Recommended Approaches:** - Use `remember` to cache expensive calculations and avoid unnecessary recompositions. - Use `mutableStateOf` or `rememberSaveable` to store UI state. - Use `LaunchedEffect` or `rememberCoroutineScope` for side effects (e.g., network requests, database access). - Use `animate*AsState` functions for smooth animations. - **Anti-patterns and Code Smells:** - **Reading State from Too High a Scope:** Avoid reading state too high in the composition tree, as this can cause unnecessary recompositions. - **Mutable State in Composables without remember:** If you're not using remember, every recomposition will create a new state, leading to unexpected behavior. - **Long Composable Functions:** Break large composables into smaller, more manageable parts. - **Hardcoded Values:** Avoid hardcoding values directly in composables. Use resources or constants instead. - **Unnecessary Recomposition:** Profile your code to identify and eliminate unnecessary recompositions. Tools like the Layout Inspector can help. - **State Management Best Practices:** - Choose the right state management solution for your needs (e.g., `remember`, `mutableStateOf`, `StateFlow`, `LiveData`). - Keep state as immutable as possible. Use `copy()` to create new state objects instead of modifying existing ones. - Use `derivedStateOf` to derive state from other state objects. - **Error Handling Patterns:** - Use `try-catch` blocks to handle exceptions in composables. - Display error messages to the user in a clear and informative way. - Use a central error handling mechanism to log errors and prevent crashes. Consider using a `Snackbar` or a dedicated error display composable. - **Performance Considerations** - **Optimization Techniques:** - **`remember`:** Cache expensive calculations and resources. - **`derivedStateOf`:** Only recompose when derived state changes. - **`SnapshotFlow`:** Collect data from mutable state without recomposing. - **`CompositionLocalProvider`:** Provides an alternative way to pass data if recomposition is a bottleneck. Use with caution as it can make dependencies implicit. - **`Skippable Composable Functions`:** Compose Compiler performs the best optimization for functions that are skippable. To allow skipping, a composable must meet the following criteria: - All parameters passed to the composable are stable. - The composable's result is the same given the same parameters. - **Inline Functions (with care):** Useful for small functions but can increase bytecode size if overused. - **Memory Management:** - Avoid creating large objects in composables. - Release resources when they are no longer needed. - Use `WeakReference` to avoid memory leaks. - **Rendering Optimization:** - Use `Modifier.drawBehind` and `Modifier.drawWithContent` for custom drawing. - Avoid overdraw by using `Modifier.clip` and `Modifier.background`. - Use `Spacer` to control layout instead of adding padding to multiple elements. - **Bundle Size Optimization:** - Use R8 to shrink and obfuscate your code. - Remove unused resources. - Use dynamic feature modules to deliver features on demand. - **Lazy Loading Strategies:** - Use `LazyColumn` and `LazyRow` to display large lists of data. - Use `rememberLazyListState` to persist the scroll position of lazy lists. - **Security Best Practices** - **Common Vulnerabilities:** - **Input Validation:** Sanitize user input to prevent injection attacks. - **Data Protection:** Encrypt sensitive data at rest and in transit. - **Secure API Communication:** Use HTTPS to encrypt communication between the app and the server. - **Input Validation:** - Validate user input to prevent injection attacks and other security vulnerabilities. - Use regular expressions or other validation techniques to ensure that input is in the expected format. - **Authentication and Authorization Patterns:** - Use a secure authentication and authorization mechanism to protect sensitive data and functionality. - Use OAuth 2.0 or OpenID Connect for authentication. - Use role-based access control (RBAC) for authorization. - **Data Protection Strategies:** - Encrypt sensitive data at rest and in transit. - Use a strong encryption algorithm. - Store encryption keys securely. - **Secure API Communication:** - Use HTTPS to encrypt communication between the app and the server. - Use certificate pinning to prevent man-in-the-middle attacks. - **Testing Approaches** - **Unit Testing Strategies:** - Test composables in isolation using `ComposeTestRule`. - Verify that composables render correctly and handle user input as expected. - Use `composeTestRule.setContent { ... }` to set the content of the test. - Use `composeTestRule.onNodeWithText("...").performClick()` to simulate user interactions. - **Integration Testing:** - Test the interaction between different composables and ViewModels. - Use `Hilt` or `Koin` to inject dependencies into tests. - **End-to-end Testing:** - Test the entire application flow from the user's perspective. - Use `UIAutomator` or `Espresso` for end-to-end testing. - **Test Organization:** - Organize tests by feature or screen. - Create a separate test module for each feature module. - **Mocking and Stubbing:** - Use `Mockito` or `Mockk` to mock dependencies in unit tests. - Use `Fake` implementations for integration tests. - **Common Pitfalls and Gotchas** - **Frequent Mistakes:** - Forgetting to use `remember` for state. - Reading state from too high a scope. - Not handling errors properly. - Not optimizing performance. - **Edge Cases:** - Handling different screen sizes and orientations. - Handling different locales and languages. - Handling different accessibility requirements. - **Version-Specific Issues:** - Be aware of breaking changes in new versions of Jetpack Compose. - Use the latest version of Jetpack Compose to take advantage of new features and bug fixes. - **Compatibility Concerns:** - Ensure that your app is compatible with different Android versions. - Use Jetpack Compose libraries that are compatible with your target Android version. - **Debugging Strategies:** - Use the Android Studio debugger to step through your code and inspect variables. - Use the Layout Inspector to inspect the composable tree and identify performance bottlenecks. - Use the Profiler to measure the performance of your app. - **Tooling and Environment** - **Recommended Development Tools:** - Android Studio - Kotlin Compiler - Jetpack Compose Libraries - **Build Configuration:** - Use Gradle to manage dependencies and build your app. - Configure the Kotlin compiler to use the latest version of Jetpack Compose. - **Linting and Formatting:** - Use `ktlint` or `detekt` to enforce code style guidelines. - Use `Prettier` to format your code automatically. - **Deployment Best Practices:** - Use the Google Play Store to distribute your app. - Use a staged rollout to gradually release your app to users. - **CI/CD Integration:** - Use a CI/CD system to automate the build, test, and deployment process. - Use `GitHub Actions` or `CircleCI` for CI/CD.