3 votes

Comment créer une bibliothèque kotlin multi-modules et multiplateforme pour iOS ?

J'essaie de créer une bibliothèque Kotlin multiplateforme pour être utilisée à la fois sur Android, JavaScript et iOS. Cette bibliothèque est composée de plusieurs modules afin d'être facilement extensible. Mon problème pour l'instant est uniquement avec Kotlin native.

Voici comment le projet est mis en place

  • Un module :common

    object MySingleton { fun doSomethingWithMyInterface(value: MyInterface) { // ... } }

    interface MyInterface { fun doSomething() }

  • Autres modules mettant en œuvre MyInterface

    class MyInterfaceExample : MyInterface { override fun doSomething() { // ... } }

J'ai configuré le build.gradle.kts pour :common comme suit :

plugins {
    kotlin("multiplatform")
    kotlin("native.cocoapods")
    id("maven-publish")
}

kotlin {
    targets {
        jvm()
        js().browser()
        ios("ios") {
            binaries {
                framework("CommonModule") {
                    baseName = "common"
                }
            }
        }
    }

    cocoapods {
        frameworkName = "CommonModule"
        summary = "My common module"
        homepage = "-"
        license = "-"

        ios.deploymentTarget = "10.0"
    }

    sourceSets {
        val iosX64Main by getting
        val iosArm64Main by getting
        val iosMain by getting {
            iosX64Main.dependsOn(this)
            iosArm64Main.dependsOn(this)
        }
    }
}

Et mes autres modules sont les suivants :

plugins {
    id("com.android.library")
    id("maven-publish")
    kotlin("multiplatform")
    kotlin("native.cocoapods")
}

android {
    compileSdkVersion(30)
}

kotlin {
    targets {
        js().browser()
        ios("ios") {
            binaries {
                framework("ExampleImplementation", listOf(DEBUG)) {
                    baseName = "example"
                    freeCompilerArgs += "-Xobjc-generics"
                    export(project(":common")) {
                        isStatic = true
                    }
                }
            }
        }
        android("android") {
            publishLibraryVariants("release")
        }
    }

    cocoapods {
        frameworkName = "ExampleImplementation"
        summary = "Example implementation of MyInterface"
        homepage = "-"
        license = "-"

        useLibraries()

        ios.deploymentTarget = "10.0"
        pod("MyDependency")
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                api(project(":common"))
            }
        }
        val iosX64Main by getting
        val iosArm64Main by getting
        val iosMain by getting {
            dependsOn(commonMain)
            iosX64Main.dependsOn(this)
            iosArm64Main.dependsOn(this)
        }
        val androidMain by getting {
            dependsOn(commonMain)
            dependencies {
                implementation("group:artifactId:1.0.0")
            }
        }
        val jsMain by getting {
            dependencies {
                implementation(npm("my_dependency", "1.0.0"))
            }
        }
    }
}

Les deux :common y :example Les modules génèrent un podspec qui ajoute un script à exécuter par le gestionnaire de cocoapodes :

<<-SCRIPT
                set -ev
                REPO_ROOT="$PODS_TARGET_SRCROOT"
                "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" :example:syncFramework \
                    -Pkotlin.native.cocoapods.target=$KOTLIN_TARGET \
                    -Pkotlin.native.cocoapods.configuration=$CONFIGURATION \
                    -Pkotlin.native.cocoapods.cflags="$OTHER_CFLAGS" \
                    -Pkotlin.native.cocoapods.paths.headers="$HEADER_SEARCH_PATHS" \
                    -Pkotlin.native.cocoapods.paths.frameworks="$FRAMEWORK_SEARCH_PATHS"
            SCRIPT

Lorsque j'ajoute ces modules dans un projet iOS comme suit :

pod 'common', :path => '~/Project/MyLibrary/common/'
pod 'example', :path => '~/Project/MyLibrary/example/'

Après avoir effectué une pod install Je pense que les deux cadres peuvent être ajoutés.

Le problème

Le cadre généré pour example ne dépend pas du cadre généré pour common . Et pourtant, :example génère une interface propre appelée ExampleCommonMyInterface qui ont la même signature que MyInterface en :common .

En outre, les deux comprennent des KotlinBase ce qui m'empêche de construire car je rencontre un problème avec les classes dupliquées.

J'ai essayé d'inclure uniquement :example mais il manque alors mon MySingleton (qui n'est pas un Singleton en Swift mais c'est une autre question) et d'autres classes qui :example ne dépend pas directement. De plus, à un moment donné, j'aimerais avoir plus d'un module dépendant de :common donc seulement l'inclusion de :example ne fonctionnerait que temporairement.

J'ai essayé beaucoup de choses depuis plusieurs jours, mais aucune ne fonctionne. De plus, la documentation est dispersée entre celle de kotlin multiplateforme et celle de kotlin native, ce qui m'a empêché de trouver des informations pertinentes sur ce que j'essayais de faire.

Je comprends que Kotlin multiplateforme n'est pas encore stable et que ce que j'essaie de réaliser n'est peut-être pas possible pour le moment. Néanmoins, j'apprécierais grandement que quelqu'un puisse m'éclairer sur la nature du problème et la façon dont je peux le résoudre.

3voto

Anders Points 514

Pour l'instant (Kotlin 1.5-M1), Kotlin/Native ne permet pas de générer des frameworks binaires où l'un dépend d'un autre. Il n'y a que deux configurations fonctionnelles supportées pour l'instant :

  1. en utilisant plusieurs cadres indépendants générés par K/N ; ou
  2. exporter tous vos modules Kotlin sous la forme d'un cadre monolithique généré par K/N (également connu sous le nom d'approche "umbrella framework" dans la communauté).

Si vous envisagez d'organiser votre code en une hiérarchie de modules Kotlin, (2) est votre seule option. Vous pouvez soit :

  1. n'utiliser que le produit d'encadrement de la example dont le produit cadre contient déjà tout le code de toutes ses dépendances (y compris les dépendances transitives et le runtime Kotlin), comme vous avez pu l'observer ; ou
  2. créer un nouvel exemple iosExport qui exporte tous les modules qui doivent être visibles par l'API dans votre base de code iOS.

Référence

La question du suivi des améliorations dans ce domaine : https://youtrack.jetbrains.com/issue/KT-42247

Ce sujet a été brièvement abordé dans la documentation officielle de KMM :

https://kotlinlang.org/docs/mpp-build-native-binaries.html#export-dependencies-to-binaries [...] Par exemple, supposons que vous écriviez plusieurs modules en Kotlin et que vous souhaitiez y accéder depuis Swift. Étant donné que l'utilisation de plusieurs frameworks Kotlin/Native dans une application Swift est limitée, vous pouvez créer un framework parapluie unique et y exporter tous ces modules.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X