plugins { // AGP must apply before recipe.kotlin.multiplatform — the latter calls androidTarget(), // which requires the Android Gradle Plugin to already be on the project. alias(libs.plugins.androidApplication) id("recipe.kotlin.multiplatform") alias(libs.plugins.composeMultiplatform) alias(libs.plugins.composeCompiler) alias(libs.plugins.composeHotReload) alias(libs.plugins.kotlinSerialization) // CocoaPods is shipped inside the Kotlin Gradle plugin already on the classpath via // `recipe.kotlin.multiplatform`. Applying via `alias(libs.plugins.kotlinCocoapods)` // would request a fresh version and fail with "already on the classpath", so we // apply it by id only. The catalog still owns the shared Kotlin version. id("org.jetbrains.kotlin.native.cocoapods") id("recipe.quality") } // Top-level project version is required by the Kotlin CocoaPods plugin when no explicit // `version` is set inside the `cocoapods { ... }` block. Mirrors `server/build.gradle.kts` // — Gradle artifact metadata only, NOT a library/plugin pin (per `verify-no-version-literals.sh`). group = "dev.ulfrx.recipe" version = "1.0.0" android { namespace = "dev.ulfrx.recipe" compileSdk = libs.versions.android.compileSdk .get() .toInt() defaultConfig { applicationId = "dev.ulfrx.recipe" minSdk = libs.versions.android.minSdk .get() .toInt() targetSdk = libs.versions.android.targetSdk .get() .toInt() versionCode = 1 versionName = "1.0" // AppAuth-Android (D-01) bundles a manifest entry for its // `RedirectUriReceiverActivity` that requires `${appAuthRedirectScheme}` to be // resolved at merge time. Pin it to the Phase 2 redirect scheme so simply // pulling AppAuth into the classpath (Plan 02-01) doesn't break AGP's manifest // merger before Plan 02-04 lands the full `` registration. // Must match `dev.ulfrx.recipe.shared.Constants.OIDC_REDIRECT_URI` byte-for-byte. manifestPlaceholders["appAuthRedirectScheme"] = "recipe" } packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } buildTypes { getByName("release") { isMinifyEnabled = false } } compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } } kotlin { // The Kotlin CocoaPods plugin (D-01) configures the iOS framework on the iOS targets // declared by `recipe.kotlin.multiplatform`. `baseName = "ComposeApp"` / `isStatic = true` // keep existing Swift `import ComposeApp` working. The AppAuth iOS pod version comes // from the version catalog so this build file stays free of literal pins. cocoapods { summary = "Recipe Compose Multiplatform shared framework" homepage = "https://github.com/ulfrxdev/recipe" ios.deploymentTarget = "15.0" podfile = project.file("../iosApp/Podfile") framework { baseName = "ComposeApp" isStatic = true } pod("AppAuth") { version = libs.versions.appauth.ios .get() } } sourceSets { commonMain.dependencies { implementation(project.dependencies.platform(libs.koin.bom)) implementation(libs.koin.core) implementation(libs.koin.compose) implementation(libs.koin.composeViewmodel) implementation(libs.kermit) implementation(libs.compose.runtime) implementation(libs.compose.foundation) implementation(libs.compose.material3) implementation(libs.compose.ui) implementation(libs.compose.components.resources) implementation(libs.compose.uiToolingPreview) implementation(libs.androidx.lifecycle.viewmodelCompose) implementation(libs.androidx.lifecycle.runtimeCompose) implementation(projects.shared) // Phase 2: Ktor client + serialization + secure settings (D-13, D-16, D-17). // The MPP variant of `ktor-serialization-kotlinx-json` is required here; the // server module keeps the `-jvm` variant via `libs.ktor.serializationKotlinxJson`. implementation(libs.ktor.clientCore) implementation(libs.ktor.clientAuth) implementation(libs.ktor.clientContentNegotiation) implementation(libs.ktor.clientLogging) implementation(libs.ktor.serializationKotlinxJsonMpp) implementation(libs.kotlinx.serializationJson) implementation(libs.multiplatform.settings) implementation(libs.multiplatform.settings.coroutines) } commonTest.dependencies { // 02-07: kotlinx.coroutines.test.runTest is the multiplatform-safe // alternative to runBlocking (which is JVM/Native-only and breaks the // wasmJs test target). All commonTest coroutine tests use it. implementation(libs.kotlinx.coroutinesTest) } androidMain.dependencies { implementation(libs.compose.uiToolingPreview) implementation(libs.androidx.activity.compose) implementation(libs.koin.android) // Phase 2 Android: AppAuth-Android + AndroidX Security Crypto for the // SecureAuthStateStore actual (D-01, D-13). EncryptedSharedPreferences is // accepted technical debt per Open Question #1; the Keystore-backed // implementation can replace it without touching AuthSession. implementation(libs.appauth) implementation(libs.androidx.security.crypto) implementation(libs.ktor.clientOkhttp) } iosMain.dependencies { // Phase 2 iOS: Darwin engine for Ktor; AppAuth-iOS arrives via the // CocoaPods block above so the shared framework links it directly. implementation(libs.ktor.clientDarwin) } jvmMain.dependencies { implementation(compose.desktop.currentOs) implementation(libs.kotlinx.coroutinesSwing) // Phase 2 Desktop: CIO is the JVM Ktor engine for the dev-mode auth stub // (D-02). The full stub lives in Plan 02-04; this just makes the engine // available so `composeApp:run` still compiles in Phase 2. implementation(libs.ktor.clientCio) } } } dependencies { debugImplementation(libs.compose.uiTooling) } // Adding `group = "dev.ulfrx.recipe"` (required by the Kotlin CocoaPods plugin to render // the podspec) shifts the Compose Resources `Res` class package from // `recipe.composeapp.generated.resources` to `dev.ulfrx.recipe.composeapp.generated.resources`, // breaking the Phase 1 `App.kt` import. Lock the historical package so this plan's wiring // changes don't cascade into UI code; Plan 02-04+ replaces `App.kt`'s template body anyway. compose.resources { packageOfResClass = "recipe.composeapp.generated.resources" }