Closed haydenkaizeta closed 1 year ago
Getting the same issue on iOS when running unit tests on iOS even with isStatic = true
.
I have the same problem with isStatic = true
on my M2 Mac during :shared:linkDebugTestIosArm64
. A colleague with the same codebase on M1 Mac is able to build it just fine.
Interesting, I am also using M2
Correction, it doesn't build on any of my colleagues machines. That includes M1, M2 and Intel macs.
You got this issue because you use types on your public API from skia lib, and skia is not exported. However, you do not want to export skia, because that will produce a large framework.
Better just remove all of the compose-related class references from your public interfaces. Because you do not want to export skia or compose.
@warnyul what you you mean by public API? If I take the example multiplatform project and set that to false it gives me that build error. I need it to be false to get SQLDelight working. At this point in time I would also be willing to export skia but that seemed like a massive hassle, and it seems compose should not require setting the isStatic
variable to true long term since KMP decided to set it to false
You can try having cocoapods and SQDelight in different modules. Integrate SQDelight in shared and have another module named iosEntryPoint or iosAppKt that depends on shared. The iosAppKt is the one that uses cocoapods to build your framework to be used in iosApp swift module.
@pablichjenkov Seems like your suggestion is the only way of having SQLDelight and Compose Multiplatform in one project. Could you elaborate on how the Compose Multiplatform template should be modified to accommodate this? How does the build.gradle look like for the module named iosEntryPoint? Is this module also "shared"?
FYI: I'm successfully using SQLDelight v2.0.0-rc02 and Compose MP in https://github.com/chrisbanes/tivi with isStatic = true
Hi @sw-dev1 . You can structure the module dependency in many ways. Just make sure that SQDelight and cocoapods plugins are not in the same module. See bellow for 2 examples
1- Create a new module(copy-paste) and just have commonMain and iOSMain as targets. Name this module iOSKotlinBridge or whatever name you want. Make the swift-Xcode project depend on the framework produced by the cocoapods plugin in iOSKotlinBridge. Then make iOSKotlinBridge depend on module shared where you have the SQdelight integration. Module dependency will look like: Xcode -> iOSKotlinBridge(podframework by cocoa plugin) -> shared(sqdelight)
2- Create a new module and name it whatever name you want to, lets say my-service-x-api. These module will have all the targets you wish to handle. Integrate sqdelight and ktor here. Then use cocoapods in module shared and make module shared depend on my-service-x-api. Then your Xcode project will depend on shared as conventional. Check link bellow for this pattern https://github.com/pablichjenkov/kmp-amadeus-api Module dependency will look like: Xcode -> shared(podframework by cocoa plugin) -> my-service-x-api(sqdelight)
I prefer number 2 because the convention of using shared as the common ui module where the different platform modules will depend on. But you can pick whatever structure you like. It is all about module inter-dependency. I advice checking above repo by chrisbanes. It is very usefull.
By the way @sw-dev1 , this project seems to mix both plugins(cocoa + SQDelight ) in the same module with no issue. Check what you have different. https://github.com/daniaviladomingo/kmm/blob/master/composeApp/build.gradle.kts
By the way @sw-dev1 , this project seems to mix both plugins(cocoa + SQDelight ) in the same module with no issue. Check what you have different. https://github.com/daniaviladomingo/kmm/blob/master/composeApp/build.gradle.kts
Think the difference is that this uses isStatic = true
.
Maybe the problem as the title says here (Requirement of isStatic = true
) is due to sqldelight coroutines extensions. The example daniaviladomingo avoids using them by wrapping the database queries in additional coroutines, thus can compile CMP and SQLDelight MP on iOS with one static cocoapod.
The example by chrisbanes is using sqldelight coroutines extensions, but employs additionally Room as a database as well as many modules, some similarly named like "common" and "shared". Is this level of complexity necessary in order to achieve simple common front-end and back-end (UI and database) on Android and iOS?
The example chrisbanes is using sqldelight coroutines extensions, but employs additionally Room as a database as well as many modules, some looking similarly like "common" and "shared".
Nope, Tivi uses SQLDelight on all platforms (Android, iOS, JVM). Room isn't used at all (as of about 4 months ago).
@pablichjenkov indeed, taking your project as a starter example I was able to get SQLDelight+Compose working on iOS and Android. Many thanks!
Thanks @haydenkaizeta glad to hear that it helped you. I use it as a template whenever consuming a new service API. Feel free to file an issue if you find a problem or contribute to it if you want new functionality. 👍🏻
Fixed in 1.5.0-beta01
Hi @pablichjenkov, thanks for your detailed elaboration. I followed this
2- Create a new module and name it whatever name you want to, lets say my-service-x-api. These module will have all the targets you wish to handle. Integrate sqdelight and ktor here. Then use cocoapods in module shared and make module shared depend on my-service-x-api. Then your Xcode project will depend on shared as conventional. Check link bellow for this pattern https://github.com/pablichjenkov/kmp-amadeus-api Module dependency will look like: Xcode -> shared(podframework by cocoa plugin) -> my-service-x-api(sqdelight)
adapting it to Xcode -> shared(podframework by cocoa plugin) -> sharedDatai(sqdelight).
Problem: Xcode does not recognize the classes in sharedData.
Let me note that they are not recognized in the android app either via the interdependency shared -> sharedData. I had to add the dependency on sharedData
as a direct dependency to the build.gradle.ktx
of the android app. Looking at your example https://github.com/pablichjenkov/kmp-amadeus-api you are doing that, too. Where does the understanding come from that for Android the interdependency on sharedData
via commonMain
in shared
is ignored but for iOS it is not?
Hi @sw-dev1 , in general that is the way transitive dependencies work, unless you use something different, in gradle for instance: api() , which will expose your library dependencies in the compile classpath of your library's client. I really prefer not exposing my dependencies from the library but make the client use them explicitly. But in any case, you should be able to do the same using cocoapods or swift package manager.
I can confirm with Compose Multiplatform (CMP) 1.5.0-beta01 isStatic=true
is no longer required for CMP. Thus one cocoapod can now host both CMP and SQLDelight.
Note: iOS build works only from Xcode, not from AS Giraffe | 2022.3.1
Hi @pablichjenkov, thanks again for your efforts. Now that this issue is closed providing a straightforward solution (with )1.5.0-beta01 isStatic=true is no longer required for CMP), a workaround Is no longer necessary.
But I would like to understand why your solution did not work for me.
Your iOS app depends on AmadeusDemoKt
(the name of the amadeus-api cocoapod) only, and not on shared
, so it is 1), but shared
depends on amadeus-api
, so it is 2). Do you see it differently?
Neither CMP depends on SQLDelight nor vice versa. Looks to me that introducing an artificial dependency layer works around the problem that KMM cannot build and link two separate frameworks for iOS: one cocoapod and one SPM or alternatively two cocoapods or SPM. Am I mistaken?
Hi @sw-dev1 , Point 1:
Your iOS app depends on AmadeusDemoKt(the name of the amadeus-api cocoapod) only, and not on shared
AmadeusDemoKt is the ios framework created by the shared module so I do directly depend on it. I just changed the default name, check on the build.gradle file the following code. But the iOS App indeed depends on the shared module.
framework {
baseName = "AmadeusDemoKt". // <-- custom name , default name is 'shared'
isStatic = true
}
Point 2: That is correct, shared depends on amadeus-api
In regards to the this:
Neither CMP depends on SQLDelight nor vice versa. Looks to me that introducing an artificial dependency layer works around the problem that KMM cannot build and link two separate frameworks for iOS: one cocoapod and one SPM or alternatively two cocoapods or SPM. Am I mistaken?
Well, actually I don't have my module interrelation layout that way because of the limitation described in this ticket. I actually have this structure because of a CLEAN architecture thing. I want my data layer separated from the UI layer. The shared module I just use it for UI, the different services and data related stuff I keep it in other modules/libraries. I advice you doing the same the benefits are countless.
Hi @pablichjenkov, thanks again for you quick response.
OK, the module shared
is accessible in Android as module shared
and it is accessible in iOS as framework AmadeusDemoKt
. The module amadeus-api
is accessible in Android as module amadeus-api
and it is accessible in iOS how?
Hi @sw-dev1 ,
In Android, JVM and JS, the library which shared
module depends on, is not included as part of the .JAR/.JS build output but in the case of iOS, if shared depends on implementation(project("local-project"))
the dependency is included in the output xcframework. If you take a look under
shared/build/bin/iosArm64/podDebugFramework/AmadeusDemoKt.framework/Headers/AmadeusDemoKt.h
you see the different classes from the amadeus-api
module are present there, with a prefix(Amadeus_api).
The task that produces the xcframework seems to figure out that is a local project and package it along with shared stuff. That is what I see in practicality, don't ask me for the theory behind 🤷♂️. That is why the iOSApp can see some classes of the amadeus-api module.
Although to be honest this is a design flaw that I have to fix. The application modules should only see shared
and not knowing anything about amadeus-api
I just gotta fix it in Android, JVM and JS by doing the same I do in the iOS case, writing the code that creates the database in the shared module, platform specific sourceset.
Hi @pablichjenkov, This is clear now. Thanks for your openness.
I would like to work with your example https://github.com/pablichjenkov/kmp-amadeus-api, which I also find very useful, but it doesn't compile on my machine (MBP M1) and Xcode cannot open the iOS app. I hope it is OK if I open a ticket in that repo? Secondly, you have a very good point about clean code. In my humble opinion KMP poses some challenges on that. Which repo or forum would be appropriate to open (or join) a discussion about it?
Hi @sw-dev1 Great to hear! The idea is having a "copy/paste" or "git clone" kinda project that you would use anytime you need to consume an API. Yeah, open the issues there, to not overwhelm this thread.
In regards to the CLEAN arch I believe KMP helps because it forces you to rely on abstraction and that is a big win. It doesn't let you take platform shortcuts no. The nature of being multiplatform enforces abstraction, and I see that really good.
Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.
When setting
and try to build an iOS app I get a long list of messages ending in:
To my knowledge
isStatic=false
is required to get SQLDelight working on multiplatform, so it would be great to resolve this.Affected platforms Select one of the platforms below:
Versions
To Reproduce Steps and/or the code snippet to reproduce the behavior:
isStatic = true
from iOS sample projectExpected behavior