英文:
Gradle Multiproject and Kotlin Multiplatform - KMM Framework with API dependency on sibling project
问题
Context
我正在一个项目上工作,在这个项目中,Web服务和移动客户端尽可能共享Kotlin代码。"api"和"client"子项目依赖于"models"子项目。
API子项目是Web服务。据我了解,它使用来自Models子项目的JVM输出。
客户端子项目用于iOS、macOS和Android应用程序的消费。再次根据我有限的了解,移动应用程序使用Kotlin/Native和Kotlin/JVM输出。
include("models", "api", "client")
"client"的缩写gradle.build.kts示例
plugins {
alias(libs.plugins.kotlin.kmp)
...
}
kotlin {
android()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "AwesomeFramework"
}
}
listOf(
macosArm64(),
macosX64()
).forEach {
it.binaries.framework {
baseName = "AwesomeFramework"
}
}
sourceSets {
all {
languageSettings.optIn("kotlin.experimental.ExperimentalObjCName")
languageSettings.optIn("kotlin.experimental.ExperimentalObjCRefinement")
}
val commonMain by getting {
dependencies {
api(project(":models"))
....
}
}
val commonTest by getting {
dependencies { ... }
}
val androidMain by getting
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val macosX64Main by getting
val macosArm64Main by getting
val macMain by creating {
dependsOn(commonMain)
macosX64Main.dependsOn(this)
macosArm64Main.dependsOn(this)
}
}
}
android {
...
}
"models"的缩写gradle.build.kts示例
plugins {
alias(libs.plugins.kotlin.kmp)
...
}
kotlin {
jvm()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "AwesomeModels"
}
}
listOf(
macosArm64(),
macosX64()
).forEach {
it.binaries.framework {
baseName = "AwesomeModels"
}
}
sourceSets {
val commonMain by getting {
dependencies { ... }
}
val commonTest by getting {
dependencies { ... }
}
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
val macosX64Main by getting
val macosArm64Main by getting
val macMain by creating {
dependsOn(commonMain)
macosX64Main.dependsOn(this)
macosArm64Main.dependsOn(this)
}
}
}
我认为以这种方式设置项目可以使Models子项目保持相对稳定,不需要重建。如果我通过这种配置让事情变得更加复杂,欢迎提出建议。
The Problem
我不理解在KMM中 dependencies { api(project(":models")) }
是如何工作的。
在Models项目中,我在iosMain源集中有特定于iOS的代码。当我构建iOS应用程序(在Xcode中)时,我看到Client使用的类型和Model类型。这些类型以"Models"为前缀(例如,ModelsUser
)。
然而,我看不到Client项目不使用的方法、函数和类型。此外,Model子项目中的特定于iOS的代码不可用。我认为这有点有道理 - 将依赖设置为api(...)
可能会导出尽可能少的符号。
那么,也许我应该在我的iOS应用程序中同时消费Model项目?换句话说...
import AwesomeFramework
import AwesomeModels
然而,问题是没有任何东西可以提供两个框架之间的链接。从Client生成的ModelsUser
类与从Model子项目生成的User
类是独立且不同的。🤷♂️
我可以将Models和Client项目合并在一起,但我想了解为什么这是最"正确"的解决方案。
我已经达到了我(非常有限的)Gradle和Kotlin Multiplatform知识的尽头。寻找可以填补空白的解决方案或资源。
英文:
Context
I'm working on a project where the web service and mobile clients share as much Kotlin code as possible. The "api"
and "client"
subprojects have a dependency on the "models"
subproject.
The API subproject is the web service. As far as I understand it, it consumes the JVM output from the Model subproject.
The Client subproject is for consumption by the iOS, macOS, and Android apps. Again, according to my kindergarten-level understanding, the mobile apps consume Kotlin/Native and Kotlin/JVM output.
include("models", "api", "client")
Abbreviated gradle.build.kts
for "client"
plugins {
alias(libs.plugins.kotlin.kmp)
...
}
kotlin {
android()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "AwesomeFramework"
}
}
listOf(
macosArm64(),
macosX64()
).forEach {
it.binaries.framework {
baseName = "AwesomeFramework"
}
}
sourceSets {
all {
languageSettings.optIn("kotlin.experimental.ExperimentalObjCName")
languageSettings.optIn("kotlin.experimental.ExperimentalObjCRefinement")
}
val commonMain by getting {
dependencies {
api(project(":models"))
....
}
}
val commonTest by getting {
dependencies { ... }
}
val androidMain by getting
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val macosX64Main by getting
val macosArm64Main by getting
val macMain by creating {
dependsOn(commonMain)
macosX64Main.dependsOn(this)
macosArm64Main.dependsOn(this)
}
}
}
android {
...
}
Abbreviated gradle.build.kts
for "models"
plugins {
alias(libs.plugins.kotlin.kmp)
...
}
kotlin {
jvm()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "AwesomeModels"
}
}
listOf(
macosArm64(),
macosX64()
).forEach {
it.binaries.framework {
baseName = "AwesomeModels"
}
}
sourceSets {
val commonMain by getting {
dependencies { ... }
}
val commonTest by getting {
dependencies { ... }
}
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
val macosX64Main by getting
val macosArm64Main by getting
val macMain by creating {
dependsOn(commonMain)
macosX64Main.dependsOn(this)
macosArm64Main.dependsOn(this)
}
}
}
I thought setting my project up this way would allow for the Models subproject to remain fairly stable and not require rebuilds. Open to suggestions if I've made things harder on myself with this configuration.
The Problem
I do not understand how dependencies { api(project(":models")) }
works for KMM.
In the Models project, I have iOS specific code in the iosMain
source set. When I build the iOS app (in Xcode), I see the Client types and the Model types that Client uses. These types are preceded with Models
(e.g., ModelsUser
).
However, I don't see methods, functions, and types the Client project doesn't use. Furthermore, the iOS specific code in the Model subproject isn't available. I think this kinda makes sense - setting the dependency to api(...)
is probably going to export the smallest number of symbols possible.
So, maybe I should also consume the Model project inside of my iOS app? In other words...
import AwesomeFramework
import AwesomeModels
However, the problem is there's nothing to provide a linkage between the two frameworks. The ModelsUser
class emitted from Client is separate and distinct from the User
class emitted from the Models subproject. 🤷🏼♂️
I could smash the Models and Client projects together, but I would like to understand why that's the most "correct" solution.
I've reached the end of my (very limited) Gradle and Kotlin Multiplatform knowledge. Looking for solutions or resources that would help fill in the gaps.
答案1
得分: 1
你对项目设置的理解是正确的。
为了完全将models
导出到ObjC/Swift,你需要导出依赖项:
it.binaries.framework {
baseName = "AwesomeFramework"
export(project(":models"))
}
来源:https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#export-dependencies-to-binaries
目前,由Kotlin生成的框架确实被认为包含唯一的声明。这就是为什么AwesomeFramework
中的User
模型与AwesomeModels
中的模型不同的原因。
请关注KT-42250以获取更新。
英文:
Your understanding of the project setup is correct.
In order to fully export the models
to ObjC/Swift you'll need to export the dependency:
it.binaries.framework {
baseName = "AwesomeFramework"
export(project(":models"))
}
Source: https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#export-dependencies-to-binaries
At the moment frameworks generated by Kotlin are indeed considered to contain unique declarations. Which is why your User
model from AwesomeFramework
isn't the same as the one from AwesomeModels
.
Please follow KT-42250 for updates.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论