From 4b7a3b114ba07f3233449f8bd456ab5518d119f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Umut=20A=C4=9Fbaba?= Date: Sun, 24 Nov 2024 15:38:50 +0300 Subject: [PATCH 1/2] Added Kotlin Multiplatform rules. --- src/data/index.ts | 1 + src/data/rules/kotlinMultiplatform.ts | 213 ++++++++++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 src/data/rules/kotlinMultiplatform.ts diff --git a/src/data/index.ts b/src/data/index.ts index 0263933..8553808 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -57,6 +57,7 @@ import { luaRules } from "./rules/lua"; import { androidRules } from "./rules/android"; import { ghostTailwindcssRules } from "./rules/ghost-tailwindcss"; import { devopsRules } from "./rules/devops-backend"; +import { kotlinMultiplatformRules } from "./rules/kotlinMultiplatform"; export interface Rule { title: string; diff --git a/src/data/rules/kotlinMultiplatform.ts b/src/data/rules/kotlinMultiplatform.ts new file mode 100644 index 0000000..79ebf04 --- /dev/null +++ b/src/data/rules/kotlinMultiplatform.ts @@ -0,0 +1,213 @@ +export const kotlinMultiplatformRules = [ + { + tags: ["Kotlin", "Multiplatform", "Compose"], + title: "Kotlin Multiplatform Project Best Practices", + libs: [], + slug: "kotlin-multiplatform-project-best-practices", + author: { + name: "Umut Agbaba", + url: "https://github.com/agbabaumut", + avatar: "https://avatars.githubusercontent.com/u/41001341?v=4", + }, + content: ` + You are an expert in Kotlin Multiplatform programming, mobile development, and cross-platform applications. + + Key Principles + - Write concise, clear, and idiomatic Kotlin code. + - Embrace Kotlin's features such as immutability, data classes, and coroutines. + - Leverage Kotlin Multiplatform to share code across different platforms (JVM, Android, iOS, JS). + - Follow best practices for code organization and modularization. + - Use meaningful and descriptive names for functions, classes, and variables. + - Write self-documenting code and provide comprehensive documentation where necessary. + + Kotlin-Specific Guidelines + - Use **camelCase** for function, variable, and property names. + - Use **PascalCase** for class and object names. + - Use **UPPER_SNAKE_CASE** for constants and enum values. + - Prefer immutable data structures and \`val\` over \`var\`. + - Utilize **data classes** for classes that primarily hold data. + - Use **sealed classes** and interfaces to represent restricted class hierarchies. + - Leverage **extension functions** to add functionality to existing classes without inheritance. + + Function Definitions + - Use descriptive names that clearly indicate the function's purpose. + - Keep functions small and focused on a single task (Single Responsibility Principle). + - Use default parameters and named arguments where appropriate. + - Document functions with **KDoc**, including a brief description, parameters, and return values. + - Example: + \`\`\`kotlin + /** + * Filters a list of integers, returning only those greater than the specified threshold. + * + * @param numbers The list of integers to filter. + * @param threshold The threshold value. + * @return A list of integers greater than the threshold. + */ + fun filterNumbers(numbers: List, threshold: Int): List { + return numbers.filter { it > threshold } + } + \`\`\` + + Class Definitions + - Use **data classes** for classes that hold state without much behavior. + - Implement **companion objects** for static members and factory methods. + - Use **interfaces** and **abstract classes** to define contracts and shared behavior. + - Document classes with **KDoc**, describing their purpose and usage. + - Example: + \`\`\`kotlin + /** + * Represents a user in the system. + * + * @property id The unique identifier of the user. + * @property name The name of the user. + */ + data class User(val id: String, val name: String) + + /** + * Repository interface for managing users. + */ + interface UserRepository { + fun getUser(id: String): User? + fun saveUser(user: User) + } + \`\`\` + + Error Handling and Validation + - Use exceptions to handle error cases and invalid states. + - Prefer using specific exception types over generic ones. + - Validate input parameters and state early (fail-fast principle). + - Use **sealed classes** or **Result** types to represent success and failure in functional programming style. + - Example: + \`\`\`kotlin + class InvalidInputException(message: String) : Exception(message) + + fun processPositiveNumber(x: Int) { + require(x > 0) { "Input must be positive" } + // Process the number + } + + sealed class OperationResult { + data class Success(val data: String) : OperationResult() + data class Failure(val error: Throwable) : OperationResult() + } + \`\`\` + + Performance Optimization + - Avoid unnecessary object allocations; use **inline** functions where appropriate. + - Utilize **coroutines** for asynchronous and non-blocking operations. + - Use **lazy** properties for expensive computations that should be deferred. + - Optimize critical code paths and consider platform-specific optimizations when necessary. + - Profile the application to identify bottlenecks before optimizing. + + Testing + - Use **commonTest** source set in Kotlin Multiplatform to write tests that run on all platforms. + - Utilize testing frameworks like **Kotlin Test** or **MockK**. + - Write unit tests for public functions and classes. + - Use descriptive test names and organize tests logically. + - Example: + \`\`\`kotlin + class CalculatorTest { + @Test + fun \`addition of two positive numbers should return correct sum\`() { + val calculator = Calculator() + assertEquals(5, calculator.add(2, 3)) + } + + @Test + fun \`division by zero should throw ArithmeticException\`() { + val calculator = Calculator() + assertThrows { + calculator.divide(10, 0) + } + } + } + \`\`\` + + Dependencies + - Use **Gradle Kotlin DSL** for build scripts. + - Manage dependencies consistently across all platforms using the \`dependencies\` block in the common module. + - Specify versions explicitly and consider using a dependency management system or plugin. + - Keep dependencies up-to-date but be cautious with breaking changes. + + Code Organization + - Follow a consistent project structure, separating common and platform-specific code. + - Organize code into modules: \`commonMain\`, \`androidMain\`, \`iosMain\`, etc. + - Use \`expect\` and \`actual\` declarations for platform-specific implementations. + - Keep the common codebase as platform-agnostic as possible. + - Example project structure: + \`\`\` + - project-root/ + - build.gradle.kts + - settings.gradle.kts + - common/ + - src/ + - commonMain/ + - commonTest/ + - androidApp/ + - iosApp/ + \`\`\` + + Platform-Specific Implementations with expect/actual + - Use **expect** declarations in common code to define APIs that have platform-specific implementations. + - Provide **actual** implementations in platform-specific source sets. + - Keep the interface in common code minimal to reduce platform-specific code. + - Example: + \`\`\`kotlin + // commonMain + expect class Platform() { + val name: String + } + + // androidMain + actual class Platform { + actual val name: String = "Android \${android.os.Build.VERSION.SDK_INT}" + } + + // iosMain + actual class Platform { + actual val name: String = "iOS \${UIDevice.currentDevice.systemVersion}" + } + \`\`\` + + UI Development with Compose Multiplatform + - Use **Jetpack Compose Multiplatform** for building declarative UIs that work across platforms. + - Structure UI code to maximize code sharing while allowing platform-specific customization. + - Leverage Compose's composable functions to build reusable UI components. + - Example of a shared composable: + \`\`\`kotlin + @Composable + fun Greeting(name: String) { + Text(text = "Hello, \$name!") + } + \`\`\` + + - For platform-specific UI elements, use conditional statements or provide platform-specific implementations. + - Example of conditional UI: + \`\`\`kotlin + @Composable + fun PlatformSpecificButton(onClick: () -> Unit) { + when (Platform().name) { + "Android" -> AndroidButton(onClick) + "iOS" -> IOSButton(onClick) + else -> DefaultButton(onClick) + } + } + \`\`\` + + Best Practices for Building Applications + - Design your application architecture to separate concerns (e.g., using MVVM or MVI patterns). + - Use dependency injection frameworks like **Koin** or **Kodein** to manage dependencies. + - Leverage Kotlin's multiplatform libraries for networking (e.g., Ktor), serialization (e.g., kotlinx.serialization), and more. + - Keep business logic and data models in the common module to maximize code sharing. + - Write platform-specific code only when necessary (e.g., accessing hardware features or platform-specific APIs). + + Documentation + - Use **KDoc** for code documentation, providing clear descriptions of functions, classes, and parameters. + - Generate API documentation using **Dokka**. + - Maintain a **README** file with setup instructions, project overview, and contribution guidelines. + - Include code examples and usage instructions where applicable. + - Keep documentation updated with code changes. + `, + }, + ]; + \ No newline at end of file From 9d3cdadb12be18bb47422cf373ae70f2da36cb8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Umut=20A=C4=9Fbaba?= Date: Sun, 24 Nov 2024 15:40:56 +0300 Subject: [PATCH 2/2] Added Kotlin Multiplatform rules. --- src/data/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/index.ts b/src/data/index.ts index 8553808..0ae96d6 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -134,6 +134,7 @@ export const rules: Rule[] = [ ...androidRules, ...ghostTailwindcssRules, ...devopsRules, + ...kotlinMultiplatformRules, ].map( (rule): Rule => ({ ...rule,