Kotlin 与 Objective-C 框架的多平台/本机互操作性

Posted

技术标签:

【中文标题】Kotlin 与 Objective-C 框架的多平台/本机互操作性【英文标题】:Kotlin multiplatform/native interoperability with Objective-C framework 【发布时间】:2019-05-29 01:11:30 【问题描述】:

我正在尝试在多平台项目中从 Kotlin 调用 Swift/Objective-C 代码。调用平台代码没有问题。但是,当我尝试调用某个库(或框架,由于我不是 ios 开发人员而不确定如何正确调用它)时,它会失败。 Docs 表示,如果正确导出,可以调用 Objective-C 代码和 Swift:

Kotlin/Native 提供与 Objective-C 的双向互操作性。如果将 Objective-C 框架和库正确导入构建(默认导入系统框架),则可以在 Kotlin 代码中使用 Objective-C 框架和库。参见例如Gradle 插件文档中的“使用 cinterop”。如果使用 @objc 将 Swift 库的 API 导出到 Objective-C,则可以在 Kotlin 代码中使用 Swift 库。尚不支持纯 Swift 模块。

但它没有说明如何正确导入它们。它只指向描述旧版本 gradle 插件的gradle plugin description。所以它对我不起作用。最后我想出了一些可能是导入Objective-C代码的方法:

fromPreset(presets.iosX64, 'ios') 
        compilations.main.outputKinds('FRAMEWORK')
        compilations.main 
            cinterops 
                firebase 
                    def pods = '$System.getProperty("user.home")/Projects/kmpp/iosApp/Pods/'
                    includeDirs '$podsFirebase/CoreOnly/Sources',
                            '$podsFirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers'
                
            
        
    

构建运行没有失败,但它不导入任何东西。我究竟做错了什么?是否可以导入这样的库?

更新:

here 我找到了一个使用cinterop 工具的示例,如下所示:

cd samples/gitchurn
../../dist/bin/cinterop -def src/main/c_interop/libgit2.def \
 -compilerOpts -I/usr/local/include -o libgit2

看起来cinterop 工具应该在我的项目中的/dist/bin/ 文件夹中,但没有这样的文件夹。我在哪里可以得到cinterop 工具?

【问题讨论】:

【参考方案1】:

我最终在 build.gradle 中找到了这个 cinterops 部分

    fromPreset(presets.iosX64, 'ios') 
        // This preset is for iPhone emulator
        // Switch here to presets.iosArm64 (or iosArm32) to build library for iPhone device
        compilations.main 
            outputKinds('FRAMEWORK')
            cinterops 
                firebase 
                    defFile "$projectDir/src/iosMain/cinterop/firebase.def"
                    includeDirs 
                        allHeaders "$projectDir/../iosApp/Pods/FirebaseCore/Firebase/Core/Public",
                                   "$projectDir/../iosApp/Pods/FirebaseDatabase/Firebase/Database/Public"
                    

                    compilerOpts "-F$projectDir/../iosApp/Pods/Firebase -F$projectDir/../iosApp/Pods/FirebaseCore -F$projectDir/../iosApp/Pods/FirebaseDatabase"
                    linkerOpts "-F$projectDir/../iosApp/Pods/Firebase -F$projectDir/../iosApp/Pods/FirebaseCore -F$projectDir/../iosApp/Pods/FirebaseDatabase"
                
            
        
    

结束这个.def文件:

language = Objective-C
headers = FirebaseCore.h FirebaseDatabase.h

这里发生了什么? Cocopods 框架位于 Xcode 项目的Pods 目录中。稍微浏览一下这个文件夹,你会找到你需要的。我不确定Public 文件夹中是否有一些标准但firebase 放置主头文件。它包含对它需要的其他头文件的引用......所以你在你的 .def 文件的 headers 部分中指定这些文件名。

接下来,您需要指定在何处查找这些文件以及它们引用的其他文件。您可以在includeDirsbuild.gradle 文件中的.def 文件中执行此操作。我更喜欢使用 build.gradle 文件,因为它可以使用变量。因此,您指定这些Public 文件夹的路径。 (这足以让 kotlin 看到库 API,但是为了能够运行应用程序,您需要编译和链接这个库......)

然后编译器和链接器需要知道库/框架在哪里。因此,您在 compilerOptslinkerOpts 中指定框架根文件夹的路径,如果它是一个框架,则在它们前面加上 -F,如果它是一个库,则在它们前面加上 -L

【讨论】:

直接从 kotlin native 使用 objectiv-c 静态方法导致我出现此错误 Undefined symbols for architecture x86_64。示例:FIRAnalytics.logEventWithName("value", null)FIRApp.configure()FIRDatabase.database() 从 iOS 实际的 kotlin 类运行上述示例将失败,并出现 Undefined symbols for architecture x86_64 你遇到过这个错误吗?如何从 kotlin 调用“cinteroped”objective-c 静态方法? 我遇到了与@JoãoZão 相同的错误。有人有解决办法吗? @JoãoZão 您是否将FirebaseAnalytics 添加到您的.def 文件、includeDirs 和编译器/链接器选项?在我的示例中,只有数据库可用... Firebase 由许多部分组成,您必须指定要使用的每一部分。 FIRDatabase.database() 会发生这种情况。我发现在 linkerOpts 中,我们需要通过 -F 选项将路径传递给框架(就像你正在做的那样),我们还必须通过框架文件本身通过-framework选项使用def文件示例:language = Objective-Cheaders = FirebaseCore.h FirebaseDatabase.hlinkerOpts = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/10.0.0/lib/darwin/libclang_rt.osx.a -F $fullpath-to-frameworks-dir -framework FirebaseDatabase抱歉格式错误。【参考方案2】:

看起来您将使用 cocoapods 库。目前 Gradle 插件不支持开箱即用的 cocapods。但可能是对您的库的依赖可以“手动”配置。您能否分享一个指向您的项目的链接?

【讨论】:

是的,那是 cocoapods。我的项目没有什么特别的,所以我没有把它推送到 github。它只是 IDEA 创建的默认多平台项目,我在其中添加了 firebase lib,如下所述:firebase.google.com/docs/ios/setup 如果 gradle 插件不支持开箱即用的 cocoapods,是否可以手动执行?例如通过创建 .def 文件并运行 cinterop 工具?顺便说一句,我也一直在尝试这样做,但没有奏效。我的终端无法识别 cinterop 命令。 我猜是的,可以手动完成,但我现在无法检查。我会在可能的情况下检查并回复您。

以上是关于Kotlin 与 Objective-C 框架的多平台/本机互操作性的主要内容,如果未能解决你的问题,请参考以下文章

Objective-C:Swift Package 函数中的多参数方法语法

Ktor 1.0 发布:Kotlin Web 框架;GoLand 2018.3 正式版发布!| 更新

学习Kotlin这一本就够

社区说 | Kotlin 移动端跨平台开发入门与实践

Kotlin 中的多变量 let

如何有效地迭代 NSSet (Objective-C) - Core Data 中的多对多关系表示?