There are 2 alternatives. The path you already tri...
# ios
p
There are 2 alternatives. The path you already tried or using a high level abstraction bridge with interfaces and expect/actual. Where you define abstractions in kotlin, implement them in swift, then provides implementation from swift to kotlin either manually or with DI. Check bellow: https://www.emanuelepapa.dev/kotlin-multiplatform-and-swift-overcoming-interoperability-challenges-for-multiplatform-development/ // --- https://medium.com/@mofeejegi/calling-platform-specific-dependencies-in-kotlin-multiplatform-69dbc9a841a2 // --- https://medium.com/@OlgaDery/kmm-evaluation-part-1-how-to-deal-with-common-code-and-platform-level-dependencies-40fbbf1b754a
m
This makes sense to me, but the issue I am facing then is that no matter what choice I make I have to implement some logic in Swift. I can do that, but how do you actually build the Swift code and then use it from Kotlin? My issue is that I am trying to create a multiplatform library, so I cannot do stuff like provide a Swift implementation of a Kotlin interface to DI at runtime, since the library won't directly run. That's why I tried the wrapper approach, but it is completely unclear to me how you actually call into the Swift wrappers I wrote. What kind of artifact do you have to build from the Swift library? And then how do you generate bindings for it and import it into Kotlin?
p
I kinda did what you want to achieve. There is only one limitation/requirement. In general you are correct, you would have to distribute 2 libraries, one with the abstraction definitions, the other with the implementations. Your client will include the 2 and you will provide some integration steps. Like when to initialize the library in SwiftUi app and how to pass it to kotlin. Distribution wise you can select the alternatives bellow: 1. Provide a KMP library that users include in KMP and provide its counterpart which they include in Xcode, either with SPM or manually. The requirement here is that the umbrella protect name your client uses has to match the one used to build the SPM package. Because the SPM package was compiled linking to that name 2. For iOS only consumers, since they don't consume KMP libs(.klibs). You can build the 2 as SPMs or 2 frameworks. And they will import both. Again manually or using SPM
m
So what I am trying to do is distribute a KMP library for both Android and iOS, that in turn gets consumed by another library, which then gets consumed by a Capacitor plugin (so Kotlin for Android and Swift for iOS). If possible I would like to just statically link the required Swift libraries into whatever files get built under KMP/iOS. So if I need to wrap the Swift library again to actually call it from Kotlin, I would really like this to just get shipped with the KMP library itself. Is that what is being done in the example you linked? The current approach works just fine for internal APIs provided by Apple but I am now struggling to also integrate third party Swift libraries.
p
You are correct, the direct kotlin access to Apple APIs or 3rd party only works if they are objective-c or objective-c compatible. But pure swift non-objective-c compatible won't work, at least yet. In KMP side you can have as many layers of library dependency as you want, as long as you export your library in the top-most project (the umbrella project) it is fine. The swift side has to be integrated independently in Xcode, in the same project that imports your umbrella KMP project. Unfortunately shipping swift code or compiled swift code isn't supported yet. At least I am not aware, maybe someone from JB can correct me.
That would be ideal shipping one .klib with all swift inside!
The project I linked uses the solution I described above, the double distribution technique. Is kinda tedious for clients because the double import KMP and Swift Package and the umbrella name restriction but I think is all we can do right now 🤷‍♂️
m
Since the library is only consumed internally, that would not actually be an issue. I think I will try my luck with defining a common interface and implementing it in Swift then I suppose. In the example the implementation seems to be in this file here: https://github.com/pablichjenkov/firebase-kmp/blob/main/FirebaseAuthKmp/Sources/FirebaseAuthKmpWrapperImpl.swift Where does the
MacaoFirebaseAuthKmpWrapper
definition come from here? Is it imported with the
ComposeApp
import? I am not very familiar with how SPM works, so I don't really understand how the Kotlin interface is imported especially since there doesn't seem to be a dependency on anything but the Firebase SDK in the Package.swift file.
p
That's right,
ComposeApp
will provide(using
export
) the Kotlin abstractions needed in the iOS side. It is imported via direct
.framework
. Open the
project.pbxproj
file and look the property Framework_search_path, it should point to composeApp/build dir where the KMP .framework will be generated after
embedAndSignAppleFrameworkForXcode
gradle task finishes. So my project is basically Manual integration but you can opt for
local SPM integration
using
binaryTarget("path-to-your-xcframework")
. I just opted for manual integration because compilation of a
.framework
is faster than
.xcframework
. And SPM only supports xcframework. On the swift side, in Xcode, you will import your Swift Package which will bring automatically its depending packages. You can import a package directly in Xcode, or using a Package.swift declaration as in my project. There are many videos and tutorials on how to integrate SPM in an iOS project. Once Xcode has both parts needed compilation will be successful because it will be able to compile your implementation package against the KMP interfaces you provided