Next gitportal (related) question ... I have run i...
# touchlab-tools
b
Next gitportal (related) question ... I have run into a weird issue. I have an expect/actual implementation for an extension property on a class, with the expect declared in a file in commonMain named "NetworkConfig.kt", and the actual in androidMain also in a file named "NetworkConfig.kt". This has been in my repo for some time, and builds (using KmmBridge) have been fine. Now I'm (experimenting with) migrating to gitportal, and when the kmp module is added to and built in my android app with gitportal, I get an error that I have a duplicate JVM class name:
Duplicate JVM class name 'com/my/org/network/config/NetworkConfigKt' generated from: NetworkConfigKt, NetworkConfigKt
Anyone seen this before? There is actually a general KMP issue filed a long time ago on you track describing the same issue. So I guess maybe this behavior is expected (see what I did there? 😉), but like I said, when I build my kmp repo on it's own I don't get this error at all, so I'm not sure what the difference is.
It's easy enough to just rename the file in one of the source sets to mitigate the problem. I'm more just curious if anyone knows why it would work fine in my kmp repo, but not in my android repo.
k
Well, I'd say less of a "GitPortal" issue than a KMP issue, obviously, but moving to direct source builds I guess causes this. I know of this issue. For a long time writing libraries, I'd make sure to rename the JVM-side files because KMP would do this. I'm not sure why it wouldn't do this for a library but would in a source-build case. If nothing has changed in the source, then my guess is KMP acts differently when the dependency is of
project
type vs a binary. Perhaps in a library, the compiler will merge the two and "erase" the
expect
one as a distinct build artifact, but it can't (or won't) when used as a
project
dependency. To avoid changing anything on the consuming side, you'd want to rename the
expect
file, and leave the
actual
. If you don't care, rename the
actual
. I usually do
NetworkConfigJvm.kt
or similar.
Man, slack
Editing.
How are things otherwise?
b
otherwise pretty good, but I'm still just getting started. I have yet to really put it through its paces. As you know, I've got maybe a bit of a special case where I need to retain the ability to build my KMP repo as a standalone library (producing aar and xcframework artifacts) as well as using it (as source with gitportal) in my own apps. So far I've got it going pretty good. I do have a little weirdness in my setup, mainly because I'm still relying on KmmBridge for the publishing of the libraries. But it's not too weird.
I'm kind of going through the same process I've been through countless times in my career when exposed to something new. I remember doing the same thing at the Android Dev Summit when they announced Compose, when Google announced AngularJS, The first time I saw SASS for stylesheets. Heck, the first time CSS stylesheets even became a thing. 1. This is a lot different and weird. I don't like it. It seems worse. 2. Now that I've used it a little bit, I can understand why they would do this, but it still seems like a lot of extra work. It's not for me. 3. Now that I've used it some more, it's not too bad. I'm still not sure it's any BETTER than the old thing, but it's not worse, just different. 4. Oh wow, how did I survive before this? It's so much easier and faster and just better in every way! It littereally solves EVERY problem! 5. OK, so it doesn't solve every problem, and it has it's own issues. But still, it's the best tool out there for the job I need to get done, and it's pretty darn solid. With Gitportal, I'm just about to stage 3 :D
k
GitPortal unidirectional mode isn't really any "better", so I'm not sure you'll get to 4/5, but it depends on what you compare it to. If you want iOS devs to see the Kotlin source and debug it, then sharing source as a concept is better. For GitPortal (again, unidirectional), to get to 4/5, you'd need to try sharing source with git submodule. Then you would experience 4/5.
For KMMBridge, it should Just Work™️ with or without GitPortal. However, v1 of HMMBridge has been sitting in testing mode for longer than I'd like. Mostly because I'm busy with everything else. It isn't any different WRT GitPortal, but it's simpler to use. Also, it focuses on SPM instead of both SPM and CocoaPods (CocoaPods still works, but the new tutorials don't deal with it at all).
This week's CocoaPods post I think helps justify KMMBridge mostly focusing on SPM in v1: https://blog.cocoapods.org/CocoaPods-Support-Plans/
... CocoaPods is in maintenance mode.
👍 4
💯 1
b
Yeah, my KMP repo is setup to do both SPM and Cocoapods. Our existing iOS app uses cocoapds, and it was working ok. I've also got a new "demo" app that I've been working on all this stuff with, and in that app I was using SPM and really it worked pretty good. I had a decent local dev flow setup that allowed me to work with kmp and ios pretty easily and without too many headaches. We're talking about doing an app redesign and "rewrite" next year, and hopefully this "demo" app becomes the base architecture for that. > If you want iOS devs to see the Kotlin source and debug it, then sharing source as a concept is better. That's the main allure of gitportal to me at the moment. If my iOS devs can see the kotlin source, and step through it in the xcode debugger, I'm going to have a much easier time getting them on board.
@kpgalligan I do have a question regarding my dual usage of KmmBridge and Gitportal ... Wonder if you might have a better idea how to handle this ... Since I'm using KmmBridge in my kmp repo, all of the gradle configs in there have the github packages setup (
addGithubPackagesRepository()
) in them: And since those same modules are pulled into my android/ios apps as source, I have to add the kmmbridge plugin to the android app as well, so it can find that extension method. Currently I'm using a project property that only exists in my kmp repo to control whether or not that method gets called:
Copy code
if (project.property("BUILD_MODE") == "library") {
    addGithubPackagesRepository()
}
That's good enough to get me up and running. But it feels wrong to have the kmmbridge plugin in my android app, even though it's not really being used there. In the long term, I'm going to look for a way to not have to do that.
k
That's the main allure of gitportal to me at the moment.
For GitPortal in unidirectional mode, that would be the only benefit in my mind. If the iOS devs aren't going to browse and debug Kotlin, publishing binaries is much easier to deal with.
> I have to add the kmmbridge plugin to the android app as well Hmm. Very good point. Without thinking this through at all, a Gradle property like
SETUP_GITHUB_PACKAGES_REPOSITORY
which KMMBridge would read and do the equivalent of
addGithubPackagesRepository()
internally might work. Then if you're pulling in the KMP modules directly from the Android repo's root
setup.gradle.kts
file, the KMP repo's
gradle.properties
file would simply be ignored.
Kind of hacky. I'll think about that problem, though.
Of course, there are other KMMBridge config values. That works in this specific case, but you couldn't use the iOS umbrella modules for anything Android, ever. In more complex builds, I tend to like an entry point for both platforms, as they need to pass in various things.
b
Yeah, I'm kind of waffling around with that too as I get things setup. Before I started with Gitportal, both the ios and android app's used the umbrella module as the point of entry for the kmp provided api's. In general terms, that entry point functions kind of like a Dagger/Hilt DI Module would, providing instances of the various classes that make up the public api of the kmp project. Right now in my gitportal setup, I'm not including the umbrella module from my kmp repo in my android project. Instead my Hilt DI modules fill that function, and they provide instances of classes from the kmp modules. For now, that seems ok. To me, it goes back to the conversation that the KMP code IS part of my app source, so it makes sense that I would provide it's objects the same way I would other objects in my android app, via the Hilt DI system. However, the one thing that I lose with this setup is some abstractions. Before, the fact that my kmp library has a network module, and database storage module, etc was completely abstracted away from the consuming application which was kind of nice. The app just calls
SomeRepo.fetchSomeData()
.. and the data is returned. It doesn't know or care where it comes from. And more importanly, the app can't call some network api method directly. With the gitportal setup, there's nothing to stop an unruly dev from getting an instance of the network object directly and start making api calls with it. It's not a huge deal in my case, because the same devs who are writing the app code are also the devs who are managing the kmp code as well and they have pretty detailed knowledge of all of it. But that was one of the nice things about using binaries. I could enforce that abstraction and "protect the developer from himself".
k
both the ios and android app's used the umbrella module as the point of entry for the kmp provided api's
I also definitely use this pattern, which would make my unconsidered solution above "not great". Then again. While you'd need to include KMMBridge as
apply false
in the root
build.gradle.kts
file, because the umbrella absolutely needs it applied regardless, you could avoid the extra declarations in the other modules with the above solution. I hope you didn't just type that out towards the end as I'm replying as I read...
To me, it goes back to the conversation that the KMP code IS part of my app source
Ah. Conceptual nuance here. GitPortal bidirectional is designed such that you consider the KMP as part of your app source. GitPortal unidirectional simply mimics the way a published library works, but with source instead of binaries. Since the unidirectional flow explicitly prohibits (or is supposed to prohibit) KMP changes being committed from the apps' repos, it's conceptually a separate library. I'm thinking this is a near term post topic. I don't remember if the following concept made it into my published posts or not. Paraphrased something like the following:
Now that we have the unidirectional flow set up, you might find it frustrating and unnecessary that the KMP code is right there, yet you're not allowed to change it. I agree. It seems dumb. Well, it is. After writing early versions of the tool and using them, I found the whole concept weirdly formal. That feeling is more obvious because with a binary, you don't directly experience it. It serves to highlight just how bad a library flow can be for KMP and feature editing. That's why there's a bidirectional mode...
If you want to try bidirectional, you can without really blowing anything up. That's also one of the benefits of the design of the tool.
It's not a huge deal in my case, because the same devs who are writing the app code are also the devs who are managing the kmp code as well and they have pretty detailed knowledge of all of it
I would say this supports the argument of "try it out". Kind of depends on how many devs and if that's likely to disrupt anybody's workflow, but I can summarize the real/conceptual differences and what "trying it" and "rolling it back" would mean.
You can also publish versioned releases of the library directly from the KMP repo. It might change the workflow of that a bit, but I'm not sure of the current workflow.
b
Yeah, I plan to try the bidirectional mode as well. Still just getting my feet wet with the unidirectional mode for now.
👍 1
Following up here. I have a fairly decent setup in place now that allows me to use gitportal for source builds of my apps AND kmmbridge to publish lib binaries separately. I don't have to reference the kmmbridge plugin in my android app at all. I can also pull my "umbrella" module into the android app, and use it as the entry point to my kmp code, the same way that I do for iOS. Perhaps best of all, that allows me to keep the "internal" modules of the kmp repo internal and not expose them to the android app.
👍 1
At a very high level, what I did was add a gradle property to the kmp repo's gradle.properties file:
KMP_BUILD_MODE=library
When I compile from source in the android and ios apps, I pass a different value ("android", or "ios"). On android, I just set the same property in it's gradle.properties file with a value of "android". On iOS I pass in the property via the Run Script build phase the builds the source:
-PKMP_BUILD_MODE=ios
My KMP repo was already setup using a convention plugin to apply common configuration logic to each of it's modules. I refactored and extended that a bit. Now I have 2 convention plugins, one for all of the modules, and another that is used only for the "umbrella" module. Those plugins read the KMP_BUILD_MODE property. When I'm not doing a library build, I can exclude KmmBridge and all of it's configuration entirely, so no need to include kmmbridge in my android project. When I'm doing an android build, I can exclude all of the ios targets/sourceSets, which allows me to pull in my umbrella module. Not sure I'd call the solution "elegant", but it works, keeps things encapsulated in the right places, and is pretty easy to work with and reason about.
k
Elegance is a luxury. As long as it works 🙂