使用Kotlin/Native的原生iOS项目中的Kotlin文件

Cry*_*ser 6 native ios kotlin

我想包含一个Kotlin文件,该文件仅在现有iOS项目中执行数据处理和网络操作,同时保留原生iOS UI代码.

尽管我认为这可能是与可实现的科特林/母语,iOS的样品(1,2),我发现,使用科特林/母语似乎接管iOS的UI代码.

在Kotlin/Native中包含用于iOS数据传输的Kotlin文件,而无需触及UI代码,如果是这样,有什么步骤可以做到这一点?

Cry*_*ser 5

是的,在跨平台项目中,可以使用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项目调用该库.

为了运行代码,我在运行"./setup.sh"之前(在从git克隆之后)执行"git子模块同步".

为了使用基于Kotlin的数据模型传输数据,我希望有一个通用函数,可以将数据传递给Kotlin,修改该数据,并将结果返回到本机iOS代码.作为原理的证明,可以构建这样的函数,我将项目扩展到不仅从Kotlin接收字符串,而且将一个字符串发送到Kotlin,附加它,并将结果发回.

项目延期:

由于在这个看似简单的扩展中存在一些障碍,我为任何感兴趣的人布置了步骤.如果您继续,则应显示以下内容:

Kotlin < - > Swift

文字可能很愚蠢,但它会告诉你,会发生什么.函数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)
        ...
    }
Run Code Online (Sandbox Code Playgroud)

您可以看到Swift在包装函数中发送给Kotlin的文本(最后,将显示生成的'string'变量).可以直接将Swift String传递给包装器,但我想强调包装器会将输入和输出视为c-strings.实际上,本机iOS项目中的文件Kotlin Native-Bridging-Header.h现在变为:

extern const char* kotlin_wrapper(const char* swiftMessage);
Run Code Online (Sandbox Code Playgroud)

在它上面的文件Launcher.cpp.由于原始文件使用KString作为kotlin_main的结果值,我试了一段时间将const char*转换为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;
}
Run Code Online (Sandbox Code Playgroud)

您将看到包装器如何首先启动Kotlin运行时,然后调用函数kotlin_main,该函数位于文件kotlin.kt中:

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
}
Run Code Online (Sandbox Code Playgroud)

指针转换为Kotlin字符串,然后用于创建kotlinMessage(数据转换的示例).然后将结果消息转换回指针,并通过包装器返回到Swift UIViewController.

然后去哪儿?

原则上,可以使用此框架而无需再次触及C++层.只需定义pack和unpack函数,将任意数据类型打包成字符串,然后将字符串解压缩到另一端的相应数据类型.这种打包和解包函数必须每种语言只写一次,如果做得足够通用,可以重复用于不同的项目.在实践中,我可能会首先重写上面的代码来传递二进制数据,然后编写pack和unpack函数来将任意数据类型转换为二进制数据.