-
Notifications
You must be signed in to change notification settings - Fork 564
Only one Kotlin framework can be loaded currently #2423
Comments
What's the reason behind this? |
@SvyatoslavScherbina They don't depend on each other nor have something in common, and both of those frameworks can be used in other apps. I think there's a workaround, creating tasks in the build.gradle to adjust for specific needs like, build A, build B or build A&B in only one .framework But thats not optimal, we can't share several kotlin-native created libraries if we can't use more than one Kotlin framework per project |
@solivares1 That's what I've had to do to utilize multiple kotlin libraries as a single framework: I actually support several apps, needing different features. They are both ios & android apps. So, I've ended up with a function in gradle called: Then I have a block that looks similar to this:
This enables me to dynamically include different source sets based on what I pass to the gradle build. It's definitely not ideal, but it's a sufficient work around. This yields a single framework per application/platform. But, with the appropriate feature sets per application/platform. |
@solivares1 thank you for providing the details. |
We (Square) have another use case, one we think is likely to be a factor for any large codebase (which is where multiplatform is very desirable!) Our mobile apps are large, numerous, and highly modularized. On iOS, we ship multiple apps that are all built from different combinations of shared modules. For instance, we might have a printing module that's used by several apps (but not all). We'd like to have our iOS modules consume our kotlin-native libraries as dependencies, but the single-framework limitation makes that difficult. We could include all kotlin-native code for all apps in a single framework, but that would mean shipping unnecessary code, as @solivares1 pointed out. Or we could build a different framework for each app, but then we'd have to change the build for our shared printing module to use the appropriate framework depending on which app it's being included in – this is complicated and makes it impossible to build the printing module independently. Either way, we lose modularity and create unnecessary and unclear dependencies. Ideally, our kotlin-native libraries would appear in our build dependency graph just like any other library. |
Do I understand correctly that you shared printing module may use different combinations of Kotlin modules depending on which application it is being included in? |
No, but the printing module itself may or may not be included in any of our applications. If we build a single Kotlin framework that supplies all (Kotlin) dependencies for all of our applications, then the printing module will need to depend on 1) non-printing code and 2) non-printing code that may not even be needed for an app that the printing module is included in. Imagine this scenario (not real, but an example). Dependencies flow right to left, ie application depends on iOS module, iOS module depends on kotlin-native module.
A single kotlin framework means that 1) printing cannot depend directly on kotlin-print, but instead will depend on a framework that also includes unrelated application-level code (kotlin-rtl, kotlin-rst) and 2) the retail application will end up including kotlin-rst, which is application-level code for restaurants. |
Thank you for detailed explanation! |
Yes, certainly. There would be dependencies among the kotlin-native modules. The example above is not complex enough to illustrate it well, but you could imagine both direct and transitive dependencies from iOS modules to kotlin-native modules, and we'd like for those to resolve normally. |
Unfortunately, this kinda conflicts with our current vision and efforts. |
Any suggestions for how we might be able to adopt? Is there any known way to resolve this at build time? e.g. specify dependencies from iOS modules onto kotlin libraries/modules, and from kotlin modules onto each other, and resolve to a single framework when building? We have many, many places where shared code makes a great deal of sense, so we're motivated to solve this issue – but we've been working hard to modularize (for obvious reasons) and need to maintain that progress. Edit: It would be helpful to better understand the "worlds" issue. Can you explain (or point to) a bit more about the ObjC/Swift custom class problem? |
Are your iOS modules static or dynamic libraries?
Consider Kotlin module1, containing class |
Our modules are all frameworks (mostly defined in the same repo w/ cocoapods) which are statically linked into our executables. Thanks for the (very clear) single-world explanation. |
The ability to include several frameworks in projects (in iOS, for example) that were created using Kotlin/Native will be very useful for developers who provide their solutions in the form of frameworks or libraries. In this case, consumers of such frameworks can use Kotlin/Native for their own code, as well as connect third-party solutions. |
I believe that the option to load multiple Kotlin frameworks would help the adoption of multiplatform. Our company is an agency that makes different apps for various customers. But some, mainly infrastructure parts, of the apps are the same so both Android and iOS teams have built many libraries to address that. Android libraries are published and distributed through our company’s Maven Nexus. iOS libraries are distributed through CocoaPods. We started to re-write some of these libraries to Kotlin Multiplatform. For Android it works still the same and projects, that depend on them do not need to change anything, they don’t even notice that the library is now shipped as multiplatform. We’d like to have similar experience for iOS projects as it is possible to build the multiplatform libraries to frameworks and also distribute through CocoaPods. Due to the one Kotlin Framework limitation we can‘t just swap the libraries when the project uses more than one of them. Our current workaround is to publish the native variants of multiplatform libraries as .klib artifacts to Maven Nexus and each customer project has to create its own „integration“ Kotlin Multiplatform module for iOS Kotlin Multiplatform libraries, that depends on all libraries the project needs, export them and build framework that contains all Kotlin Multiplatform dependencies the project needs. It works but it requires a non-trivial change to the project structure that need to be maintained and this barrier don’t allow us to use this approach on more (all) projects we are currently working on. |
@xsveda could you provide more details on your setup? Are your libraries independent? E.g. do their APIs depend on each other or have common API dependencies? |
The libraries are not independent and their APIs may depend on each other. Our applications are well modularized (some have around 60 modules) and we use three layers of module types:
Most of these modules are candidates to be re-done as multiplatform. They form a tree of dependencies where some of them are transitive. The root of the tree is an Android or iOS app that picks all the |
Thanks! |
Btw, do you distribute these libraries as different pods or as subspecs of a single pod? And could you clarify what do you mean by "requires a non-trivial change to the project structure that need to be maintained" here? |
We distribute them as standalone pods and maintain them in separate repositories with separate versioning.
Our current aim is to start building libraries shared between different apps using Kotlin Multiplatform without any change to those projects. Current version of a library is being built from Swift codebase and distributed via Cocoapods - we want to build the next library version from Kotlin Multiplatform codebase. Your suggestion to build all Am I understanding it correctly? If so, few more details come to my mind:
|
could we use a Kotlin/Native module(let's name it |
@xsveda Unfortunately, the idea described above doesn't look feasible with separate versioning. |
@SvyatoslavScherbina Yes, the ones that are shared across different applications have separate versioning and release cycle. |
Hey folks, is there any updates? We (Shopify) sharing the same concern, single framework limitation destroys high modularization of shareable code. As for now we have only 2 KN modules that independent from each other but that for sure not scalable. Having a |
Do you expect your modules to be independent further? Or form independent groups? Would support for multiple framework help you if it had the restriction described above?
Could you clarify?
Is your Swift code (that consumes Kotlin modules) modularized too? If so, how do you manage dependencies between your Swift modules? |
It's hard to say from our current state, as we still experimenting, but they might. For now we see couple independent KN modules that produces 2 iOS frameworks that will be consumed by iOS app. But with the current limitation we must generate one framework that will aggregate (depend on) all KN modules and produce one framework, right? That means we can't have them as independent modularized features.
All our swift code lives in monorepo, so basically we don't need to have package manager as any feature is subproject of the monorepo. |
Ok, thanks for the details. |
Another use case that this issue prevents is the use of Kotlin multiplatform to develop SDKs. Purely as an example, consider Firebase, which has client libraries for Android, iOS, Javascript and C++. It would be a huge win for Firebase, or others developing similar cross platform services, to be able to develop a single SDK in Kotlin, and distribute it to users on any of their platforms without the user having to know or care that it's a kotlin-mpp project. An ideal scenario that I was looking into for a recent internal project of mine was to have something akin to a The Ideally one would be able to create such a project, and serve the In an ideal world, which I realize probably isn't feasibly at this point due the need for all K/N libraries to be compiled with the same version of K/N, the resulting assets, an aar for Android, a Cocoapod for iOS, a .so for C/C++, or an npm package for JS, would not contain the entire Kotlin world, but would depend on a stdlib style artifact that JetBrains maintains which provides that functionality. Then the native build systems could manage that dependency themselves. Short of that, for the native side, at least, would it not be feasible to add to the generated header files a standard |
With proper configuration, the end app will have two copies of the pod only if the pod is static and at least one of the frameworks is dynamic, just like with Swift or Objective-C. |
Seems to be my case too: 2 directly independent modules (though both depend on the same 3rd module) cause:
Not sure how it will be resolved (taking into account the comment), waiting for 1.3.70 ... |
Create a fake `app-ios-lib` module as a workaround for JetBrains/kotlin-native#2423. Update `app-ios` project due to changed framework name. Still not using Kotlin classes for REST + JSON though (for now).
Create a fake `app-ios-lib` module as a workaround for JetBrains/kotlin-native#2423. Update `app-ios` project due to changed framework name. Still not using Kotlin classes for REST + JSON though (for now).
Move to coroutines. Create a fake `app-ios-lib` module as a workaround for JetBrains/kotlin-native#2423. Update `app-ios` project due to changed framework name. Add Ktor-client. Add tests. Use server version "2.0". Bump self version to "1.0.0".
Move to coroutines. Create a fake `app-ios-lib` module as a workaround for JetBrains/kotlin-native#2423. Update `app-ios` project due to changed framework name. Add Ktor-client. Add tests. Use server version "2.0". Bump self version to "1.0.0".
Move to coroutines. Create a fake `app-ios-lib` module as a workaround for JetBrains/kotlin-native#2423. Update `app-ios` project due to changed framework name. Add Ktor-client. Add tests. Use server version "2.0". Bump self version to "1.0.0".
We have kotlin 1.3.70 EAP now, but I couldn't find any info about this issue. What is the status? Will it also work with Cocoapods? (So we could use 2 or more cocoapods created with kotlin native) |
We plan to fix multiple debug cocoapods-plugin-produced frameworks in 1.4. To make frameworks dynamic you can add the following fragment to the end of your kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
binaries.withType<org.jetbrains.kotlin.gradle.plugin.mpp.Framework> {
isStatic = false
}
}
} |
Was anyone able to use multiple kmp modules with the kotlin 1.3.70 in iOS project (without using a bridge module solution)? I was trying to use 2 different modules and create as podspec with cocoapods but no luck. |
Please see my comments above. |
Does anybody have an example of a build.gradle that wraps multiple kotlin native frameworks for cocoapods? |
While using "Umbrella" module technique, the class names are prefixed with module names. Ultimately iOS code is infested with "Factory" and "Winery" prefixes. Thanks for your efforts, guys! |
For Swift it should be enough to export these Kotlin modules explicitly. See the documentation for more details on exporting: https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#exporting-dependencies-to-binaries |
@SvyatoslavScherbina Works like charm! Thank you so much, now the code looks neat! |
@SvyatoslavScherbina Should this issue be closed or is there still functionality coming? |
Regardless of this, there are still cases discussed above that aren't supported currently. |
@SvyatoslavScherbina Should static frameworks work in 1.4.0? I'm getting |
@kpgalligan no, this problem is not fixed yet. Please disable compiler caches to workaround: add
to What is your case, why do you need multiple static debug frameworks? (Release ones work ok). |
Client would like to know. Personally, I want to understand bounds and implications. For example, if you publish a library from KMP for iOS use, you need to at least publish debug as dynamic or run the risk that a user of your library will also want to use KMP and run into linker errors. Can explain that in better detail later, but I want to understand the caching and how things work with release first. Individual client users can use an umbrella, but I've found that they don't all like the idea that they need to, depending on the size of the org. If two teams work on truly different parts of the code, they'd prefer to not comingle dev and release timelines, let's say. In the case of this particular team, they're sticking with dynamic frameworks purely because of this issue. If the plan is to eventually make this work, I don't think that would be a factor, as this is only an issue as they scale, but if this is something deeper and won't be fixed, it's good to know. |
Thank you for the details!
We don't use compiler caches for release binaries. If you publish a debug binary, disabling compiler caches is generally a good idea. For example, it would reduce the size of the binary.
When using compiler caches for static binaries, precompiled
As I've already mentioned, this already works, you just need to disable compiler caches. |
Thank you, that actually clears the situation up quite a bit. |
Created new (meta) issues for the remaining problems: Feel free to report more issues: https://kotl.in/issue. |
Also this one: https://youtrack.jetbrains.com/issue/KT-42254 |
So.. i managed to make two kotlin-native frameworks for Swift but when trying to add them on the project i got the following message.
(Only one Kotlin framework can be loaded currently)
Do we have any update on this?
I prefer not to merge both frameworks into one, is there another workaround?
The text was updated successfully, but these errors were encountered: