我想包含一个 Kotlin 文件,该文件仅在现有 iOS 项目中执行数据处理和网络操作,同时保留原生 iOS UI 代码。
虽然我认为这可以通过 Kotlin/Native 来实现,我发现使用 Kotlin/Native 的 iOS 示例(1、2)似乎也接管了 iOS UI 代码。
是否可以在不接触 UI 代码的情况下使用 Kotlin/Native 在 iOS 中包含用于数据传输的 Kotlin 文件,如果可以,这样做的步骤是什么?
最佳答案
是的,可以在跨平台项目中使用 Kotlin/Native 在 Kotlin 和原生 iOS UI 代码之间传输数据。这允许有一个基于 Kotlin 的数据模型的通用代码库,而例如继续使用 iOS 的原生 UI 代码。
原始证明:
项目https://github.com/justMaku/Kotlin-Native-with-Swift为我指明了正确的方向,因为它显示了这样做的基本步骤:
在 Swift UIViewController 中,它调用一个包装函数,该函数将接收来自 Kotlin 函数的字符串。调用通过 C++ 层进行调解,C++ 层本身启动 Kotlin 运行时,将请求传递给 Kotlin 函数,从它接收字符串,然后将其传递回 Swift UIViewController,然后显示它。
在技术层面,该项目包含一个脚本,将 Kotlin、C++ 和 Kotlin/Native 部分编译成一个静态库,然后可以从原生 iOS 项目中调用。
为了让代码运行,我必须(从 git 克隆后)在运行“./setup.sh”之前执行“git submodule sync”。
要使用基于 Kotlin 的数据模型传输数据,我想要一个通用函数,它可以将数据传递给 Kotlin,修改该数据,并将结果返回给原生 iOS 代码。作为可以构建这样一个函数的原理证明,我将项目扩展为不仅从 Kotlin 接收一个字符串,而且将一个字符串发送到 Kotlin,附加它,然后将结果发回。
项目扩展:
由于在这个看似简单的扩展中存在一些障碍,因此我为任何感兴趣的人列出了步骤。如果你跟着,你应该得到以下显示:
文字可能很愚蠢,但它会告诉你会发生什么。 函数viewDidAppear中ViewController.swift的变化是:
let swiftMessage: String = "Hello Kotlin, this is Swift!"
let cStr = swiftMessage.cString(using: String.Encoding.utf8)
if let retVal = kotlin_wrapper(cStr) {
let string = String(cString: retVal)
...
}
您会在包装函数中看到 Swift 发送给 Kotlin 的文本(最后,将显示生成的“字符串”变量)。可以直接将 Swift 字符串传递给包装器,但我想强调的是,包装器会将输入和输出视为 c 字符串。事实上,原生 iOS 项目中的 Kotlin Native-Bridging-Header.h 文件现在变为:
extern const char* kotlin_wrapper(const char* swiftMessage);
它转到文件 Launcher.cpp。由于original file使用 KString 作为 kotlin_main 的结果值,我尝试了一段时间 convert const char* to KString并将其传递给 kotlin_main。最后我发现,直接将 const char* 变量转移到 Kotlin 中,并使用 Kotlin/Native 提供给我们的函数进行转换,要简单得多。
我的 Launcher.cpp 然后变得比原来的更紧凑。这是完整的文件:
#include "Memory.h"
#include "Natives.h"
#include "Runtime.h"
#include "KString.h"
#include <stdlib.h>
#include <string>
extern "C" const char* kotlin_main(const char* swiftMessageChar);
extern "C" const char* kotlin_wrapper(const char* swiftMessageChar) {
RuntimeState* state = InitRuntime();
if (state == nullptr) {
return "Failed to initialize the kotlin runtime";
}
const char* exitMessage = kotlin_main(swiftMessageChar);
DeinitRuntime(state);
return exitMessage;
}
您会看到包装器如何首先启动 Kotlin 运行时,然后调用位于文件 kotlin.kt 中的函数 kotlin_main:
import konan.internal.ExportForCppRuntime
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.cstr
import kotlinx.cinterop.nativeHeap
import kotlinx.cinterop.toKString
@ExportForCppRuntime
fun kotlin_main(cPtr: CPointer<ByteVar>): CPointer<ByteVar> {
val swiftMessage = cPtr.toKString()
val kotlinMessage = "Hello Swift, I got your message: '$swiftMessage'."
val returnPtr = kotlinMessage.cstr.getPointer(nativeHeap)
return returnPtr
}
指针被转换为 Kotlin 字符串,然后用于创建 kotlinMessage(数据转换的示例)。然后将结果消息转换回指针,并通过包装器传递回 Swift UIViewController。
从这里到哪里去?
原则上,无需再次接触 C++ 层即可使用此框架。只需定义打包和解包函数,将任意数据类型打包成一个字符串,然后将字符串解包到另一端的相应数据类型。这样的打包和解包函数必须为每种语言编写一次,并且可以在不同的项目中重复使用,如果做得足够通用的话。在实践中,我可能会先重写上面的代码来传递二进制数据,然后编写 pack 和 unpack 函数来将任意数据类型转换为二进制数据。
关于ios - 带有 Kotlin/Native 的原生 iOS 项目中的 Kotlin 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47596110/