使用Kotlin / Native的原生iOS项目中的Kotlin文件 [英] Kotlin File in Native iOS Project with Kotlin/Native

查看:96
本文介绍了使用Kotlin / Native的原生iOS项目中的Kotlin文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



虽然我认为这可以通过



文字可能很愚蠢,但它告诉你,会发生什么。
函数viewDidAppear中ViewController.swift的变化是:

 让swiftMessage:String =Hello Kotlin,this斯威夫特! 
let cStr = swiftMessage.cString(using:String.Encoding.utf8)
if let retVal = kotlin_wrapper(cStr){
let string = String(cString:retVal)
。 ..
}

你会看到Swift在包装函数中发送给Kotlin的文本(最后,将显示生成的字符串变量。可以直接将Swift String传递给包装器,但我想强调包装器会将输入和输出视为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然后变得比原来更紧凑。这是完整的文件:

  #includeMemory.h
#includeNatives.h
#includeRuntime.h
#includeKString.h
#include< stdlib.h>
#include< string>

externCconst char * kotlin_main(const char * swiftMessageChar);

externCconst char * kotlin_wrapper(const char * swiftMessageChar){
RuntimeState * state = InitRuntime();
if(state == nullptr){
return无法初始化kotlin运行时;
}
const char * exitMessage = kotlin_main(swiftMessageChar);
DeinitRuntime(州);
return exitMessage;
}

您可以看到包装器如何首先启动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,我收到你的消息:'$ swiftMessage'。
val returnPtr = kotlinMessage.cstr.getPointer(nativeHeap)
return returnPtr
}

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



从这里开始?



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


I would like to include a Kotlin file that only performs data processing and network operations in an existing iOS project, while keeping native iOS UI code.

While I thought that this may be achievable with Kotlin/Native, the iOS samples (1,2) that I found that use Kotlin/Native seem to take over the iOS UI code as well.

Is including a Kotlin file for data transfer in iOS possible with Kotlin/Native without touching the UI code, and if so, what are the steps to do so?

解决方案

Yes, it is possible in a cross-platform project to transfer data between Kotlin and native iOS UI Code by using Kotlin/Native. This allows to have a common code base for the data model based on Kotlin, while e.g. continuing to use native UI code for iOS.

The original proof:

The project https://github.com/justMaku/Kotlin-Native-with-Swift pointed me in the right direction, since it shows the essential steps to do so:

In a Swift UIViewController, it calls a wrapper function that shall receive a string from a Kotlin function. The call is mediated through a C++ layer, which itself starts the Kotlin runtime, passes the request to a Kotlin function, receives the string from it, and passes it back to the Swift UIViewController, which then displays it.

On the technical level, the project contains a script that compiles the Kotlin, C++, and Kotlin/Native part into a static library, which then can be called from the native iOS project.

To get the code to run, I had (after cloning from git) to perform a "git submodule sync" before running "./setup.sh".

To transfer data with a data model based on Kotlin, I would like to have a generic function, that can pass data to Kotlin, modify that data, and return the result back to the native iOS code. As a proof of principle, that such a function can be build, I extended the project to not only receive a string from Kotlin, but send one to Kotlin, append it, and send the result back.

Extension of the project:

Since there were some roadblocks in this seemingly simple extension, I lay out the steps for anybody interested. If you follow along, you should get the following displayed:

The text may be stupid, but it tells you, what happens. The changes in ViewController.swift in the function viewDidAppear are:

    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)
        ...
    }

You see the text that Swift sends to Kotlin in the wrapper function (in the end, the resulting 'string' variable will be displayed). One could directly pass the Swift String to the wrapper, but I wanted to highlight that the wrapper will consider the input and output as c-strings. Indeed, the file Kotlin Native-Bridging-Header.h inside the native iOS project now becomes:

extern const char* kotlin_wrapper(const char* swiftMessage);

On it goes to the file Launcher.cpp. Since the original file used a KString as result value of kotlin_main, I tried for some time to convert const char* to KString and pass that to kotlin_main. In the end I found, that it is much simpler to directly transfer the const char* variables to Kotlin, and do the transformation there with the functions that are given to us by Kotlin/Native.

My Launcher.cpp then became more compact than the original. Here is the complete file:

#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;
}

You see how the wrapper first starts the Kotlin runtime and then calls the function kotlin_main, which resides in the file 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
}

The pointer is converted to a Kotlin String, and then used in the creation of the kotlinMessage (the example of a data transformation). The result message is then transformed back to a pointer, and passed through the wrapper back to the Swift UIViewController.

Where to go from here?

In principle, one could use this framework without touching the C++ layer again. Just define pack and unpack functions, that pack arbitrary data types into a string and unpack the string to the respective data type on the other side. Such pack and unpack functions have to be written only once per language, and can be reused for different projects, if done sufficiently generic. In practice, I probably would first rewrite the above code to pass binary data, and then write the pack and unpack functions to transform arbitrary data types to and from binary data.

这篇关于使用Kotlin / Native的原生iOS项目中的Kotlin文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆