I have an existing Kotlin Android and Swift iOS projects.
I would like to gradually migrate these projects to Kotlin Multiplatform.
In order to do that, I moved some of the internal logic of the Android project into a common source set.
Now, I would like the Swift project to start using this logic but the problem is that the visibility of all this logic is internal
ant therefore it doesn’t get packaged inside the iOS framework produced by the KMM project and can’t be used in Swift.
If I make it public, it is available to Swift but also becomes part of the Android API and I don’t want that.
What would be your recommended way to share internal logic between Android and existing Swift code?
Can you clarify more why you don’t want the logic to be accessible from your android project but still want it to be common?
If you want something to only be accessible in iOS i think you should put it in the iOS module.
Or if your common code depends on uncommon structures you might use expect
declarations
Additionally you might take advantage of @OptionalExpectation
if your expect
declarations only applicable in iOS
To clarify, I do want it to be accessible to the Android as internal. This is a library project and therefore anything public becomes a part of the API and I don’t want that.
If you want it to be internal
in android and public
in iOS you might make the declaration in the common module internal
and have a public
delegate in iOS
or vice versa.
If you want it to be internal
in both but still accessible you might instead rely on the package naming convention (for example com.example.internal
). Normally, people won’t use library code that is in a private package.
Additionally, you might add an OptIn
requirement on the internal components so nobody use it even by mistake
I do want them to be internal
in Android and public
in iOS.
The firs solution you propose is probably the proper one but it means duplicating the APIs of hundreds of classes and then maintaining these duplicates. It will make the transition much harder and longer and maybe even not worth it .
The OptIn
option sounds like a viable solution but it’s still a workaround rather than a real solution.
Thanks a lot @LSafer for your help !
I’m not completely sure as I’ve never really used it in a library project, but I think @PublishedApi
is worth a try — it would make your internal members effectively public so I’d expect iOS Swift code to be able to access them, but when imported in a Kotlin project it should behave as if the members were internal, thus invisible. If you want to make sure the members cannot be used by another JVM-targeting language (e.g., Java, Scala), you might want to try the @JvmSynthetic
annotation as well.
Alternatively, you might well choose to take the common logic out of the project into a separate, “business-logic” Gradle project, make members public, and depend on the “business-logic” project in Android using implementation
(and not api
), and in iOS the same way as you’re currently depending on the project of the common source set.