Hello ! Any help would be welcomed as I am struggl...
# multiplatform
m
Hello ! Any help would be welcomed as I am struggling with this new issue. I have a multi-modular KMP project building just fine on the Android part. The launch is going through the composeApp module (aka app for Android) and I want to use the module in iosApp to call my main composable App (such a classic way to go). I am using spm4kmp https://github.com/frankois944/spm4Kmp to bridge any Swift code to Kotlin, importing libraries such as FirebaseCore, Auth and Firestore. Whenever I try to link my composeApp with composeApplinkDebugFrameworkIosX64 in order to use it in iosApp, I have this issue: ld: framework 'FirebaseCore' not found
Build.gradle (composeApp)
Copy code
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.multiplatform)
    alias(libs.plugins.compose.multiplatform)
    alias(libs.plugins.hobbymatchmaker.buildlogic.application)
    id("io.github.frankois944.spmForKmp") version "0.8.1"
}

multiplatformConfig {
    useCoil()
    useDecomposeWithCompose()
    useFirebase()
}

kotlin {
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.compilations {
            val main by getting {
                cinterops.create("nativeIosShared")
            }
        }
    }

    sourceSets {
        commonMain.dependencies {
            // Logger
            implementation(libs.findLibrary("napier").get())

            // Modules
            implementation(project(Modules.AUTHENTICATION_DATA))
            implementation(project(Modules.AUTHENTICATION_DOMAIN))
            implementation(project(Modules.COMMON))
            implementation(project(Modules.DATABASE))
            implementation(project(Modules.DESIGN))
            implementation(project(Modules.DI))
            implementation(project(Modules.LOGIN_DOMAIN))
            implementation(project(Modules.LOGIN_PRESENTATION))
            implementation(project(Modules.MOVIE_DATA))
            implementation(project(Modules.MOVIE_DOMAIN))
            implementation(project(Modules.MOVIE_PRESENTATION))
            implementation(project(Modules.MOVIE_DETAIL_DATA))
            implementation(project(Modules.MOVIE_DETAIL_DOMAIN))
            implementation(project(Modules.MOVIE_DETAIL_PRESENTATION))
            implementation(project(Modules.NAVIGATION_DOMAIN))
            implementation(project(Modules.NAVIGATION_PRESENTATION))
            implementation(project(Modules.NETWORK))
            implementation(project(Modules.SESSION_DATA))
            implementation(project(Modules.SESSION_DOMAIN))
            implementation(project(Modules.SPLASHSCREEN_PRESENTATION))
        }


        androidMain.dependencies {
            // AndroidX
            api(libs.findLibrary("appcompat").get())
            implementation(libs.findLibrary("activity-compose").get())

            // Facebook
            implementation(libs.findLibrary("facebook-android-sdk").get())

            // Firebase to make :generateDebugAndroidTestLintModel pass
            implementation("com.google.firebase:firebase-auth-ktx:23.2.0")
            implementation("com.google.firebase:firebase-common-ktx:21.0.0")
            implementation("com.google.firebase:firebase-firestore:25.1.2")

            // Koin
            implementation(libs.findLibrary("koin-android").get())

            // Timber
            implementation(libs.findLibrary("timber-android").get())
        }
    }
}

android {
    namespace = "com.msoula.hobbymatchmaker"

    val secretsPropertiesFile = rootProject.file("secrets.properties")
    val secretProperties = Properties()

    if (secretsPropertiesFile.exists()) {
        secretProperties.load(FileInputStream(secretsPropertiesFile))
    }

    defaultConfig {
        manifestPlaceholders["facebookApplicationID"] =
            secretProperties["facebook_application_id"] ?: ""
        manifestPlaceholders["facebookClientToken"] =
            secretProperties["facebook_client_token"] ?: ""

        buildConfigField(
            "String",
            "FIREBASE_APP_ID",
            "\"${secretProperties["firebase_application_id"]}\""
        )
        buildConfigField(
            "String",
            "FIREBASE_API_KEY",
            "\"${secretProperties["firebase_api_key"]}\""
        )

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables.useSupportLibrary = true
    }
}

compose.resources {
    publicResClass = true
    packageOfResClass = "com.msoula.hobbymatchmaker"
    generateResClass = always
}

swiftPackageConfig {
    create("nativeIosShared") {
        minIos = "18.0"
        dependency {
            remotePackageVersion(
                url = URI("<https://github.com/firebase/firebase-ios-sdk.git>"),
                products = {
                    add("FirebaseCore", exportToKotlin = true)
                    add("FirebaseAuth", exportToKotlin = true)
                    add("FirebaseFirestore", exportToKotlin = true)
                },
                version = "11.12.0"
            )
            remotePackageVersion(
                url = URI("<https://github.com/google/GoogleSignIn-iOS>"),
                products = {
                    add("GoogleSignIn", exportToKotlin = true)
                },
                version = "8.0.0"
            )
        }
    }
}
Convention plugin, ios part:
Copy code
internal fun KotlinMultiplatformExtension.configureIOSApplication() {
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "ComposeApp"
            isStatic = false
            linkerOpts("-framework", "FirebaseCore")
            linkerOpts("-framework", "FirebaseAuth")
            linkerOpts("-framework", "FirebaseFirestore")
        }
    }
}
c
Where do you apply your convention plugin?
m
In top of the build.gradle
Copy code
alias(libs.plugins.hobbymatchmaker.buildlogic.application)