背景
KMM 使用中有些数据需要 IOS 原生提供,被 GPT 坑了一天,最后根据 medium 一篇较新文章 30 分钟解决,GPT 一直在 c_interop 文件路径和 Xcode header 配置里无用折腾,也就是携程旅行分享的那些方案的老一套。
迭代这么久实际没那么复杂了,但官网文档依旧没法看,这里以无 Shared 模块的 KMM 为例。
理论部分
Kotlin/Native 并不直接与 Swift 交互,而是与 Objective-C 交互,由于 Swift 可以将类暴露给 Objective-C,因此这成为了一个简洁明了的桥梁。
干活步骤
定义 swift 代码
最简单的类共享示例 iosApp/iosApp/SignatureUtils.swift :
import Foundation
@objc(SignatureUtils)
public class SignatureUtils: NSObject {
@objc public static let shared = SignatureUtils()
@objc public func getRemainingSignatureDays() -> Int {
//...
return -1
}
}
注意:不要在 @objc 中省略类名,使用 @objc(MyClassName) 来确保符号名称可预测、无混乱,避免增加莫名其妙失败因素。
因为是新加入的 swift 文件,Xcode 并不会识别,需要用 Xcode 打开 iosApp 项目后,在左侧资源目录对 xcodeproj 点击右键,随后找到文件添加即可。
如果不将其加入项目,编译时该类不会被编译,Kotlin/Native 也就找不到。

定义头文件以及 def
头文件 SignatureUtils.h,用于定义类的哪些方法、属性被 Kotlin/Native 使用:
#import <Foundation/Foundation.h>
@interface SignatureUtils : NSObject
+ (SignatureUtils *)shared;
- (NSInteger)getRemainingSignatureDays;
@end
def 文件 SignatureUtils.def:
language = Objective-C
headers = SignatureUtils.h
package = 项目包名
简单说 def 就是定义的意思,def 文件描述详情见官网:https://kotlinlang.org/docs/native-definition-file.html
参数解析:
language :默认是 C 但这里指定为 Objective-C 是必须,理论部分已经说明。
headers :链接哪些本机代码(静态库、框架等)可以有多项,空格隔开。
package :指通过桥接方式生成的 Kotlin 绑定类存放的位置,也就是当引用共享类时导入的包。
注意:保持文件名、类名的一致性 ,勿使用 GPT 去生成,100% 错误答案。建议手写,仅需改类名、方法名和返回结果类型,继承 NSObject 是必须的固定模板,其他为固定格式。
其他:这俩不必使用 Xcode 和 iosApp 关联,仅需知道位置,比如我这里将其放在 iosApp/iosApp/Signature 。
配置 Gradle
需要配置的是 composeApp/build.gradle.kts ,将目录指向至 def 与 .h 文件所处位置:
kotlin {
listOf(
iosArm64(),
iosSimulatorArm64()
iosX64(),
).forEach { iosTarget ->
iosTarget.binaries.framework {
//...
}
//此处 Signature 只需辨识度高即可
iosTarget.compilations.getByName("main") {
cinterops.create("Signature") {
definitionFile.set(file(rootDir.absolutePath + "/iosApp/iosApp/Signature/SignatureUtils.def"))
includeDirs.allHeaders(rootDir.absolutePath + "/iosApp/iosApp/Signature")
}
}
}
}使用方法和原生无差:
@OptIn(ExperimentalForeignApi::class)
fun getRemainingDays(): Int {
return SignatureUtils.shared()?.getRemainingSignatureDays()?.toInt() ?: -1
}
同步 Gradle 无报错即成功,有报错查看错误日志无用,请检查命名文件路径等细节。
完结
剩下的就是 KMM expect/actual 那套,原文中还有更多内容,但我不需要,便不赘述。
以上。
参考:Yes, Calling Swift from Kotlin Multiplatform is Easy : No Plugins, No Magic, Just CInterop.
本站广告由 Google AdSense 提供
0条评论