ios - 带有 Kotlin/Native 的原生 iOS 项目中的 Kotlin 文件

标签 ios kotlin native

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

虽然我认为这可以通过 Kotlin/Native 来实现,我发现使用 Kotlin/Native 的 iOS 示例(12)似乎也接管了 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 项目中调用。

为了让代码运行,我必须(从 gi​​t 克隆后)在运行“./setup.sh”之前执行“git submodule sync”。

要使用基于 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 的文本(最后,将显示生成的“字符串”变量)。可以直接将 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/

相关文章:

android - onOptionsItemSelected不能在 fragment 中工作

android - OnActivityResult 未在 Activity 的 Fragment 中被调用

ios - 如何根据时间调用2个号码

android - 有什么方法可以在 Android native 代码中获取全局变量 gDVM?

ios - Objective-C iPhone 定期检查位置

ios - TraitCollection.horizo​​ntalSizeClass 在 iOS 8.1 上报告紧凑,在 iOS 8.4 和 9.x 上报告常规

Kotlin 中的 JUnit 测试

java - 如何通过 JNI 将 Java 枚举暴露给 native 代码

ios - 如何在 swift 3 中将 View Controller 制作为启动画面?

ios - 如何在 Xcode 中验证 Localizable.strings 文件?