Kotlin/Native在本地iOS项目中的Kotlin文件

6
我想在现有的iOS项目中包含一个仅执行数据处理和网络操作的Kotlin文件,同时保留本地iOS UI代码。
虽然我认为使用Kotlin/Native可能可以实现这一点,但我找到的使用Kotlin/Native的iOS示例(1, 2)似乎也接管了iOS UI代码。
在不触及UI代码的情况下,使用Kotlin/Native在iOS中包含用于数据传输的Kotlin文件是否可能,如果可能,步骤是什么?

1
你还应该查看https://github.com/JetBrains/kotlinconf-app以获取更多iOS示例。 - tskulbru
感谢提到这个例子。它清楚地展示了Kotlin/Native的另一个伟大案例。然而,就像我提到的例子一样,它基本上是使用Kotlin实现了包括UI在内的所有内容,而原生的iOS UIViewController子类和AppDelegate基本上是空的。我有成千上万行的原生iOS UI代码(许多UIViewController子类),我想保留它们,并想知道是否可以使用Kotlin/Native实现? - CryptUser
2个回答

5
是的,在跨平台项目中,可以使用Kotlin/Native在Kotlin和本地iOS UI代码之间传输数据。这样可以基于Kotlin拥有一个通用的数据模型代码库,同时仍然可以使用本地iOS UI代码。原始证明可以在 https://github.com/justMaku/Kotlin-Native-with-Swift 中找到。该项目展示了必要的步骤:
在Swift UIViewController中,它调用一个包装函数,该函数应该从Kotlin函数接收一个字符串。调用是通过一个C++层来介绍的,该层启动Kotlin运行时,将请求传递给Kotlin函数,从中接收字符串,然后将其返回给Swift UIViewController,然后显示它。
从技术层面上说,该项目包含一个编译Kotlin、C++和Kotlin/Native部分成静态库的脚本,然后可以从本地iOS项目中调用。
为使代码运行,我需要(在克隆git之后)执行“git submodule sync”,然后运行“./setup.sh”。
为了使用基于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)
        ...
    }

你可以在包装函数中看到Swift发送给Kotlin的文本(最终,结果变量'string'将被显示)。一个人可以直接将Swift字符串传递给包装器,但我想强调包装器将把输入和输出视为c-strings。实际上,在本机iOS项目中,文件Kotlin Native-Bridging-Header.h现在变成了:
extern const char* kotlin_wrapper(const char* swiftMessage);

它继续前往文件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;
}

你可以看到,wrapper首先启动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++ 层的情况下使用此框架。只需定义打包和解包函数,将任意数据类型打包成字符串并在另一侧解包字符串以获取相应的数据类型。这样的打包和解包函数每种语言只需要编写一次,并且如果足够通用,则可以在不同项目中重复使用。实际上,我可能会先重写以上代码以传递二进制数据,然后编写打包和解包函数以将任意数据类型转换为二进制数据并从中进行转换。

我能在iOS上使用Kotlin/Native的expect和actual类声明吗? - Nishita

2

如果您想要,可以使用Kotlin作为框架,因此Kotlin代码将保留在框架文件中,这样您就可以在Android和iOS上使用一些通用代码,而无需完全使用Kotlin编写iOS应用程序。

使用Gradle构建您的Kotlin代码以生成ObjC/Swift兼容的框架

在您的build.gradle文件中

buildscript {
    ext.kotlin_native_version = '0.5'
    repositories {
        mavenCentral()
        maven {
            url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies"
        }
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:$kotlin_native_version"
    }
}
group 'nz.salect'
version '0.1'
apply plugin: "konan"
konan.targets = ["iphone", "iphone_sim"]
konanArtifacts {
    framework('nativeLibs')
}

它将生成两个.framework文件,一个用于模拟器,另一个用于实际设备,请将该框架放入您的项目中,并像其他第三方框架一样将其链接到您的项目中。
Cmd: ./gradlew build

注意:每次更改Kotlin文件时,请构建并替换您的框架文件(您可以创建一个shell脚本并将其添加到构建阶段以自动执行此操作)。
干杯!

能否使用此方法来声明期望/实际类?如果可以,那么如何实现? - Nishita

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接