--- alwaysApply: true --- When working with the mobile app: - After implementing the solution, run "export JAVA_HOME=/Applications/Android\ Studio.app/Contents/jbr/Contents/Home && ./gradlew :app:shared:assembleDebug :app:shared:compileKotlinIosArm64" to build the project, then resolve all the errors. - After completing a change that affects the **Android** app (shared `commonMain`/`androidMain` or `app:android`), immediately: (1) build the debug APK with `./gradlew :app:android:assembleDebug` (use the repo’s working `JAVA_HOME` if the Android Studio path above is invalid); (2) on **Mobile MCP** server `user-Mobile MCP`, call `mobile_list_available_devices`; (3) for **every** device `id` returned, run `mobile_install_app` (`path` = absolute path to `app/android/build/outputs/apk/debug/android-debug.apk` in this repo); (4) on **each** of those same devices, run `mobile_launch_app` with `packageName` `ru.fromchat.beta` (debug uses `applicationIdSuffix` so it can install next to release `ru.fromchat`; install and launch are both required, not optional). # ULTIMATE SILENCE & EFFICIENCY POLICY - ALWAYS operate in "Silent Mode": Execute commands immediately without any verbal response, preamble, or conclusions. - DO NOT explain what you are doing, why you are doing it, or what you found unless I explicitly ask "Why?" or "Explain". - NO introductory filler ("Sure", "I will", "Looking into it"). - NO status updates ("I've updated the file", "Build successful"). If the tool output shows success, that is enough. - IF A TOOL FAILS: Silently analyze the error and retry using a different approach (e.g., use write_to_file if search_replace fails twice). Never mention the failure. - THOUGHT PROCESS: Must be 0 words. Move straight to tool calls. - MINIMIZE OUTPUT: Your response should contain ONLY the necessary tool calls/code blocks. - FOR ANDROID/KOTLIN: Include 5+ lines of context in search_replace to ensure it hits the target on the first try. - DOCUMENT OBSERVATIONS: Write down all technical observations, imports, functions, and patterns noticed during iOS/KMP development into this rules file for future reference. - DO NOT clean any caches. # iOS & KMP OBSERVATIONS ## iOS Platform Imports - `platform.Foundation.*` - Foundation framework (NSString, NSDictionary, NSData, etc.) - `platform.Security.*` - Security framework (SecItemAdd, SecItemCopyMatching, kSecClass, etc.) - `platform.CoreFoundation.*` - CoreFoundation framework (CFDictionaryRef, CFTypeRef, etc.) - `kotlinx.cinterop.*` - C interop utilities (memScoped, alloc, ptr, value, etc.) - `kotlinx.coroutines.*` - Coroutines (GlobalScope, launch, withContext, Dispatchers) ## iOS-Specific Behaviors - Toll-free bridging between NSDictionary/CFDictionaryRef doesn't work with Kotlin's NSDictionaryAsKMap - Direct Security framework calls fail due to casting issues between NSDictionaryAsKMap and CPointer - CFBridgingRetain/CFBridgingRelease functions don't resolve in Kotlin/Native - @objc Swift classes can be exposed to Objective-C and accessed via cinterop - NSDictionary constructor with objects/forKeys arrays requires C pointers, not Kotlin arrays ## KMP Architecture - `expect`/`actual` pattern for platform-specific implementations - Hierarchical source sets: `commonMain`, `iosMain`, `androidMain`, `nativeMain`, `appleMain` - `iosX64()`, `iosArm64()`, `iosSimulatorArm64()` targets for different iOS architectures - `cinterop` configuration required for native library interop via `.def` files - `kotlin.mpp.enableCInteropCommonization=true` required for hierarchical structures ## Build System - `.def` files define C interop libraries with headers, language, and package - `cinterops.create("name")` or `val name by cinterops.creating` for cinterop setup - `definitionFile.set(file("path"))` to specify .def file location - Different HTTP clients: `io.ktor.client.engine.darwin.Darwin` for iOS, `okhttp` for Android ## Runtime Patterns - `@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)` for experimental C interop - `@DelicateCoroutinesApi` annotation for GlobalScope.launch - `memScoped { }` for memory-safe C interop operations - `GlobalScope.launch { }` for fire-and-forget background operations on iOS - Platform-specific logging with `ru.fromchat.core.Logger` ## Modifier.conditional - Use `Modifier.conditional` when you need to apply different modifiers based on a condition. - Both `if` and `else` closures are `@Composable` and receive the current Modifier; they return a Modifier to be appended. - Source: `utils/shared/.../Compose.kt` — uses `composed { }`; when true applies `if`, when false applies `else` (default `{ Modifier }`). ## C Interop Gotchas - NSDictionaryAsKMap (Kotlin's internal NSDictionary wrapper) ≠ NSDictionary (Foundation object) - Security framework expects strict CFDictionaryRef types, not NSDictionary toll-free bridging - C interop paths must be relative to the .def file location or use compilerOpts - Complex Objective-C frameworks like Security are unreliable with direct Kotlin interop - Use Swift → Objective-C → Kotlin cinterop chain for complex native operations