kotlin ios开发_适用于iOS开发人员的Kotlin Multiplatform

kotlin ios开发

重要要点

  • 使用Kotlin Multiplatform,您可以避免重复很多逻辑来开发在多个平台上运行的应用程序。
  • KMP并不是在所有平台上实现100%共享代码的最后一步,因为在许多情况下,由于仍然是特定于平台的,UI逻辑仍然必须本地编程。
  • Swift的语法和Kotlin的语法之间的相似之处极大地减少了编写KMP业务逻辑所涉及的学习曲线的大部分。
  • 您可以使用Android Studio创建可重用的KMP组件,然后将其作为框架导入到Xcode项目中。

适用于iOS开发人员的Kotlin Multiplatform

DRY(或不要重复自己)是编程的基本原则之一,但是要开发在多个平台上运行的应用程序,经常需要重复很多逻辑。 尽量减少重复只是很好的编程。 跨平台的更多共享代码意味着更少的重复,这意味着更好的代码。

考虑一下您当前的iOS项目:它是否可在其他平台(例如Android或网络)上使用? 如果是这样:您的iOS应用程序与其他平台上的同类应用程序共享多少逻辑? 如果没有,但是在其他平台上使您的应用程序可用是在路线图上,那么在下一个平台上进行的开发将迫使您重复自己的工作? 无论哪种方式,答案都可能是:很多。

输入Kotlin Multiplatform (KMP)。 Kotlin是一种静态类型的编程语言,与Swift极为相似,并且可以与Java 100%互操作。 在许多方面,它都是Android版Swift。 KMP是Kotlin的功能,可以在应用程序的各个平台之间共享代码,以便每个平台的本地编程UI都可以调用通用代码。 KMP不是完成所有平台上100%共享代码的最后一步,但这是朝着这个目标迈出的自然一步 。

KMP通过使用Kotlin对应用程序的各种平台通用的业务逻辑进行编程。 然后, 每个平台的本地编程 UI都调用该通用逻辑。 在许多情况下,仍然必须对UI逻辑进行本机编程,因为它过于特定于平台而无法共享。 在iOS中,这意味着将最初用KMP编写的.frameworkfile导入Xcode项目中,就像其他外部库一样。 您仍然需要Swift在iOS上使用KMP,因此KMP并不是Swift的终结 。

还可以迭代地引入KMP,因此您可以在不中断当前项目的情况下实施KMP。 它不需要替换现有的Swift代码。 下次您在应用程序的各个平台上实现功能时,请使用KMP编写业务逻辑,将其部署到每个平台上,并对UI进行原生编程。 对于iOS,这意味着Kotlin中的业务逻辑和Swift中的UI逻辑。

Swift的语法和Kotlin的语法之间的相似之处极大地减少了编写KMP业务逻辑所涉及的学习曲线的大部分。 在学习过程中剩下的就是IDE:Android Studio。

Kotlin多平台项目仍然是Kotlin的实验功能; 因此,API会随着每次更新而改变。

入门

本教程适用于几乎没有Android Studio或Kotlin经验的iOS开发人员。 如果您还没有Android Studio,请按照安装指南进行操作 。

克隆我们为您制作的入门项目 。 它包含一个样板KMP项目,其中包括一个空的KMP库。 入门项目还包括一个iOS应用程序和一个Android应用程序,它们都显示来自25个硬编码“ meh” URL列表的gif。

这些应用程序是为您提供的,但没有为您提供该库,因为本文仅专注于制作KMP库,而不是使用该库的应用程序。 但是请放心,如果您想深入研究Android开发,您将获得有价值的知识。

使用KMP,您将编写联网逻辑,方法是在其公共API中搜索短语“ whoa”,从Giphy获取25个URL。 这些URL将替换硬编码到每个平台中的URL。

打开Android Studio,然后选择打开现有的Android Studio项目 在打开的Finder窗口中,选择克隆的启动程序项目的顶级GifGetter /目录。

kotlin ios开发_适用于iOS开发人员的Kotlin Multiplatform_第1张图片

如果您看到此附加对话框,请按更新

Android Studio

如果您以前没有研究过Android Studio,那么可能要花很多钱。这是对本教程中使用的Android Studio部分的介绍。 如果您想对IDE进行全面介绍,就在这里 。

让我们从基础开始:左侧的项目导航器 ,应显示一个文件结构。 如果没有看到文件结构,请选择左上角的“ 项目”选项卡,其中将显示项目导航器 项目导航器的顶部,您很可能会看到一个标记为Android的下拉菜单。 按下它,然后在下拉菜单中选择“ 项目”选项。

kotlin ios开发_适用于iOS开发人员的Kotlin Multiplatform_第2张图片
  1. Android Studio顶部的按钮行是工具栏 其中的工具具有许多功能,例如构建,运行,调试运行,应用新更改以及启动Android虚拟设备(AVD)管理器 在其下方是导航栏 ,显示了在项目的文件夹结构中可以找到当前打开文件的位置,在本例中为/GifGetter/GifLibrary/commonMain/kotlin/platform/common.kt
    • 以下是工具栏中的一些工具:
      • 锤子是“ 构建”按钮
      • 旁边的下拉列表是配置
      • 就像Xcode一样,它旁边是Run按钮
  2. Android的官方文档将其称为工具窗口之一 ,但通常称为项目结构文件结构,文件夹结构或其他类似术语。 这是Xcode 项目导航器的直接推论。
    • 请注意顶部的“ 项目”下拉菜单。 在本教程中应为Project 如果将其更改为Android或其他内容,请重新选择“ 项目”,否则在下面的结构中可能看不到所有文件夹和文件。
  3. 这是编辑器窗口 ,您可以在其中编写代码。 请注意顶部的文件: android.kt,common.kt,ios.ktGifLibrary ,这些都是当前打开的背景选项卡。 右键单击其中之一,以查看用于在右侧,左侧,顶部或底部打开更多编辑器窗口的选项。
  4. 这是工具窗口栏的一部分,该窗口环绕Android Studio的外部。 唯一相关的部分是黄色圆圈,特别是“ 终端”和“ 构建”选项卡。

项目结构

kotlin ios开发_适用于iOS开发人员的Kotlin Multiplatform_第3张图片

以下是KMP项目结构的大致概述:

  1. .gradle /.idea /文件夹:对于大多数Android Studio项目,不应将其提交给源代码管理的本地文件。 它们包括IDE,项目和依赖项的本地设置,因此与本教程无关。
  2. androidApp /文件夹:更大的KMP项目中Android应用程序项目的根目录,该目录具有所有Android方面的UI逻辑。
  3. build /文件夹:包含KMP构建的输出。 它还不应致力于源代码控制。
  4. iosApp /文件夹:较大的KMP项目中Xcode项目的根目录。
  5. GifLibrary / folder :KMP逻辑的所在地。 这将是Android项目的业务逻辑,并且KMP会将其生成到iOS项目的GifLibrary.framework中。 src /文件夹包含以下内容:
    • androidLibMain :Android平台特定的KMP逻辑
    • commonMain :不需要任何特定于平台的代码的KMP业务逻辑
    • iosMain :iOS平台特定的KMP逻辑,能够与Apple API进行交互(即:UIKit,GCD等)
    • main :包含一个AndroidManifest.xml文件,该文件将GifLibrary /定义为Android库。 在许多方面,它就像一个Xcode.plist。
  6. build.gradle :这可能是一个新概念,因为在iOS开发中没有直接对应的概念。 在某些方面,它是一个构建脚本或makefile,用于定义项目的依赖项,脚本和其他设置。 这样,它也有点像.xcodeproj文件
  7. 外部库 :项目的所有依赖项。 Android和KMP项目与iOS项目的不同之处在于,它们需要更多的外部依赖项。

项目顶层的其他文件(即: local.properties和所有.iml文件)由Android Studio生成,与我们将在KMP中执行的操作无关。 另一方面, settings.gradle是应包含在源代码管理中的配置文件。 build.gradle文件不同, settings.gradle不是构建脚本,而是gradle的配置文件。

既然您已经了解了Android Studio的基础知识,那么该开始学习KMP项目了!

为KMP配置Gradle

*注意:如前所述,KMP仍处于试验阶段,因此这些gradle配置可能会发生很大变化。*

打开GifLibrary / build.gradle并查看kotlin{}块:

kotlin {
   targets {
       android()
       def onPhone = System.getenv('SDK_NAME')?.startsWith("iphoneos")
          if (onPhone) {
             iosArm64("ios")
          } else {
             iosX64("ios")
          }
   }
}

   sourceSets {
       commonMain {
           dependencies {
               implementation "io.ktor:ktor-client-json:$ktor_version"
           }
       }
   }
}

这是用于启动项目的相当标准的KMP gradle配置。 首先看targets {}中的块内kotlin {}块。 无需赘述,此块为gradle提供了针对Android和iOS平台的预配置设置。 iOS的预设需要额外的行,因为它们在设备和模拟器版本上有所不同。

接下来看sourceSets {}块。 目前, commonMain是唯一具有任何其他依赖关系的sourceSet ,但是仍然有另外两个需要其他依赖关系的sourceSets commonMain {}将以下内容添加到sourceSets {}

androidLibMain {
   dependencies {
       implementation "io.ktor:ktor-client-json-jvm:$ktor_version"
   }
}

iosMain {
   dependencies {
       implementation "io.ktor:ktor-client-ios:$ktor_version"
       implementation "io.ktor:ktor-client-json-native:$ktor_version"
   }
}

添加这些之后,您应该看到一条消息栏: 自上次项目同步以来,Gradle文件已更改。 为使IDE正常工作,可能需要进行项目同步 现在忽略此消息,因为对该gradle文件还有更多更改。

现在,所有三个必需的依赖项都包含在sourceSets ,但是在准备好此gradle之前,仍然需要做最后一个更改。 kotlin {}块下面,有一个名为copyFramework{}task ,可将KMP项目作为iOS框架复制到适当的文件夹中,以便iosApp /项目可以找到它。

为确保在项目构建时执行这些任务,请在copyFramework{}:之后插入以下行copyFramework{}:

tasks.build.dependsOn copyFramework

现在按立即同步以更新gradle文件。 如果需要下载新的依赖项,则可能需要一些时间。 您可以在以下位置检查gradle同步的进度   Android Studio底部的标签,以查看同步进度。

探索Giphy的API

转到Giphy的API并创建一个Giphy开发人员帐户。 或者,如果您已经拥有Giphy开发人员帐户,则只需登录。然后,按Create a App并在出现提示时输入“ GifGetter”以及您选择的任何说明。 复制新的Api密钥。

现在,返回Android Studio并打开GifLibrary / src / commonMain / kotlin / GiphyAPI并找到以下类:

class GiphyAPI {
   val apiKey: String = ""
}

将您的Giphy API密钥粘贴到此val语句的值中,该值当前为空字符串。 这就是在Kotlin中声明类和字符串常量的方式。 请注意,到目前为止,所有这些代码都可以是Swift,只有val关键字例外,它与Kotlin等效于let

平台特定的代码

KMP通过告诉通用代码对各种平台的期望值来解决iOS和Android平台之间的差异。 转到GifLibrary / src / commonMain / kotlin / platform / 选择文件→新建→Kotlin文件/类,然后创建一个新的Kotlin文件/类并将其命名为common 在下面的Kinddropdown菜单中,选择File

这些iOS和Android应用程序需要进行网络调用,因此它们需要运行异步代码。 iOS使用Grand Central Dispatch(GCD)进行此类并发操作,但是由于Android不使用GCD,因此无法在通用代码中使用。 因此,请告诉通用代码对其平台的expect 在这种情况下,需要期待以下dispatcher

import kotlinx.coroutines.CoroutineDispatcher

internal expect val dispatcher: CoroutineDispatcher

因为iosMan /androidLibMain /模块现在expect声明一个名为CoroutineDispatcher类型的名为dispatcher的常量,所以将出现错误。 从iOS端开始,转到GifLibrary / src / iosMain / platform /并创建一个名为iOS的新文件,并粘贴以下代码:

internal actual val dispatcher: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue())

internal class NsQueueDispatcher(private val dispatchQueue: dispatch_queue_t) : CoroutineDispatcher() {
   override fun dispatch(context: CoroutineContext, block: Runnable) {
       dispatch_async(dispatchQueue.freeze()) {
           block.run()
       }
   }
}

此逻辑表明,对于iOS平台,KMP将使用GCD并获取主队列以运行异步代码。 现在转到Android模块中的相应文件夹: GifLibrary / src / androidLibMain / platform / 创建另一个Kotlin文件,并将其命名为Android 幸运的是,Android平台使获取主调度程序变得容易:

internal actual val dispatcher: CoroutineDispatcher = Dispatchers.Main

编译错误应消失,因为现在调度程序已支持两个平台的commonMainexpects及其各自的实际实现。

准备好数据

我们需要可以表示从Giphy API接收到的JSON数据的类。 GifLibrary / src / commonMain / kotlin /中创建一个新的Kotlin文件,并将其命名为Data 将以下代码粘贴到新文件中:

import kotlinx.serialization.Serializable

@Serializable
data class GifResult(
       val `data`: List
)

@Serializable
data class Data(
       val images: Images
)

@Serializable
data class Images(
       val original: Original
)

@Serializable
data class Original(
       val url: String
)

在文件的顶部,必须import Serializable才能识别data class es前面的以下@Serializable语句。 将数据类视为Swift中的结构。 它们不是值类型对象,但是它们的行为类似。 Giphy API的响应JSON将映射到这些数据类。 现在, commonMain已经准备好处理大部分业务逻辑。

编写KMP业务逻辑

回到GiphyAPI文件并粘贴在下面的代码apiKey

private val client: HttpClient = HttpClient { // 1
   install(JsonFeature) { // 2
       serializer = KotlinxSerializer(Json.nonstrict).apply { // 3
           setMapper(GifResult::class, GifResult.serializer()) // 4
       }
   }
}

private fun HttpRequestBuilder.apiUrl(path: String) { // 5
   url { // 6
       takeFrom("https://api.giphy.com/") // 7
       encodedPath = path // 8
   }
}

这是此代码中发生的情况的分步细分:

  1. 声明一个类型为HttpClient的常量。 同样,此行与其在Swift中等效的唯一区别是关键字val ,而不是let
  2. JsonFeature安装到client对象中,使其能够序列化和反序列化JSON。
  3. 实例化并配置serializer ,这是HttpClient对象的属性,默认情况下为null Json.nonstrict格式指示响应JSON将包含与下一行设置的数据类无关的字段。
  4. 提供序列化程序的顶级数据类,然后该序列化程序将响应JSON序列化为GifResult对象。 JSON中的“数据”字段将填充相应的GifResult.data属性。 这些字段将继续向下以填充嵌套数据类的层次结构。
  5. 将函数apiUrl(path: String)HttpRequestBuilder类。
  6. 构造一个URL。
  7. 提供基本URL。
  8. 用指定的路径扩展URL。

现在已经有了样板化的网络逻辑,是时候对Giphy的端点进行网络调用以获取“ whoa”搜索结果了。 在您刚刚粘贴的网络代码下,添加以下内容:

suspend fun callGiphyAPI(): GifResult = client.get {
   apiUrl(path = "v1/gifs/trending?api_key=$apiKey&limit=25&rating=G")
}

但是,等等, suspend关键字是什么,为什么有必要呢? 嗯,这是通过协同程序在Kotlin中执行异步代码的方法之一。 现在,只知道这意味着该函数只能从协程或另一个挂起函数(例如HttpClient上的get{}函数)调用。

下一步是设置可用于iOS和Android应用程序的功能。 再一次,语法非常接近Swift:

fun getGifUrls(callback: (List) -> Unit) { // 1
   GlobalScope.apply { // 2
       launch(dispatcher) { // 3
           val result: GifResult = callGiphyAPI() // 4
           val urls = result.data.map { // 5
               it.images.original.url // 6
           }
           callback(urls) // 7
       }
   }
}
  1. 声明函数getGifUrls(callback: (List) → Unit) Android和iOS代码将调用此函数。 Unit返回类型等于Kotlin的Swift void
  2. GlobalScope的上下文中运行以下代码,其中包括其自己的调度程序和作业取消逻辑。
  3. 启动我们自己的dispatcher ,而不是默认值。 请记住, dispatcher程序的实现特定于每个平台。
  4. 声明一个值,该值等于callGiphyApi()函数返回的值。
  5. 映射GifResult对象的数据列表...
  6. ...向下延伸直到您获得每个url属性。 url是一个字符串,因此它将映射到List对象。 如果Kotlin it关键字只有一个,它代表lambda的参数。
  7. 将URL列表作为函数的参数提供给回调。

在Android上实施KMP

Android应用程序的最后一步是,将其现有的硬编码URL列表替换为从Giphy API获取的URL。 这不会像听起来那样困难。

打开文件androidApp / src / main / kotlin / MainActivity Android的ActivityViewController紧密相关,在MainActivity实现的第一个函数是onCreate(savedInstanceState: Bundle?) ,它在某种程度上onCreate(savedInstanceState: Bundle?) viewDidLoad()

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)

   val gifList: RecyclerView = findViewById(R.id.gif_list)
   val layoutManager = LinearLayoutManager(this)
   gifList.layoutManager = layoutManager

   adapter = GifAdapter(this)
   gifList.adapter = adapter
   adapter.setResults(urls)
}

删除使用fakeData设置结果的函数的最后一行,并将其替换为以下内容:

getGifUrls {
   adapter.setResults(it)
}

setResults(results: List)函数会将获取的gif URL设置为新的数据源,并重新加载RecyclerView ,它本质上是UITableViewUICollectionView的Android版本,但是如果存在一些关键差异,您应该进一步研究你很感兴趣 这就是Android方面所有逻辑的全部内容!

如果它不是自动导入,并且您有未解决的参考错误,则可能需要在文件顶部的导入列表中添加以下内容:

import org.gifLibrary.GiphyAPI

在Android模拟器上运行

按下Android Studio顶部工具栏中的“运行”按钮。 该图标与Xcode的“运行”按钮非常相似。 Android虚拟设备(AVD)管理器中将出现一个窗口,该窗口类似于Xcode的用于管理模拟器的窗口。 区别在于Xcode带有预下载的模拟器。

在新窗口中,如果您在列表中看到任何可用的Android设备,请选择该设备以运行该应用程序。 否则,请按创建新虚拟设备 在设备列表中选择电话类别和Pixel 2 XL ,然后按右下角的下一步

kotlin ios开发_适用于iOS开发人员的Kotlin Multiplatform_第4张图片

接下来,选择最新版本的Android(该版本的API级别最高)旁边的下载 然后将出现另一个窗口,以安装所选版本的Android。 如果您在最新版本的Android旁边没有看到“ 下载”按钮,则表示您很幸运! 选择该版本,然后再次按下一步 在下载Android操作系统时喝杯咖啡,然后在使用新的Android虚拟设备返回时,选择完成

全部在Android端完成! 尽情享受您的Android应用程序及其KMP网络逻辑的荣耀。 接下来,回到熟悉的领域。

Xcode设置

打开Xcode项目,然后转到iosApp目标的Build Phases ,然后按+按钮,在此处用红色圆圈圈出:

kotlin ios开发_适用于iOS开发人员的Kotlin Multiplatform_第5张图片

从出现的列表中选择“ 新建运行脚本阶段”选项。 您会在主窗口的列表末尾看到一个新的运行脚本 将其在列表中向上拖动,使其位于“ 编译源”上方。 单击箭头旁边的箭头,然后粘贴以下脚本:

cd "$SRCROOT/../"
./gradlew GifLibrary:copyFramework \
-Pconfiguration.build.dir="$CONFIGURATION_BUILD_DIR"          \
-Pkotlin.build.type="$KOTLIN_BUILD_TYPE"                      \
-Pdevice="$KOTLIN_DEVICE"

该运行脚本读取这些定义的Xcode项目的Build Settings ,以在copyFramework gradle任务中使用,您可以查看是否回到GifGetter / GifLibrary / build.gradle 但是Xcode项目仍然没有这些设置。 要创建它们,请转到目标的“ Build Settings”选项卡,然后按+按钮,如下所示:

kotlin ios开发_适用于iOS开发人员的Kotlin Multiplatform_第6张图片

在出现的小下拉菜单中选择添加用户定义的设置 将新设置命名为“ KOTLIN_BUILD_TYPE”。 单击其旁边的箭头以显示“ 调试”和“ 发布”环境。 调试 “调试”的价值和版本 “释放”的值。

添加另一个用户定义的设置,并将其命名为“ KOTLIN_DEVICE”。 然后单击“ 调试 ”右侧的+按钮。 这将在Debug下面创建一个字段,该字段将显示Any Architecture |。 任何SDK 单击它以查看潜在的体系结构选项的列表,然后选择Any iOS Simulator SDK 给该字段一个false值。 再次对Debug进行相同的操作,但是这次添加Any iOS SDK并将其值设置为true

对“ KOTLIN_DEVICE”下的Release重复这些步骤。 完成所有这些操作后,您的用户定义设置应如下所示:

kotlin ios开发_适用于iOS开发人员的Kotlin Multiplatform_第7张图片

接下来,在右上角的搜索栏中,搜索“框架搜索路径”:

kotlin ios开发_适用于iOS开发人员的Kotlin Multiplatform_第8张图片

将以下内容添加到DebugRelease的 Framework搜索路径中:

"$(SRCROOT)/../GifLibrary/build"

该路径是KMP输出a的位置。 包含iOS应用程序所需的业务逻辑的框架文件。 如果您在Finder中转到GifGetter / GifLibrary / build / ,则会看到一个准备好供Xcode使用的GifLibrary.framework文件。

接下来,转到iOSApp目标的“ 常规”标签,滚动到底部,然后按“ 嵌入式二进制文件”下的+按钮。 在出现的窗口中,按底部的“添加其他”。 在出现的Finder窗口中,在GifGetter / GifLibrary / build /中选择GifLibrary.framework文件,然后按打开 这也应将框架添加到“ 链接二进制文件和框架”中。

打开GifRetriever.swift ,您将看到25个URL字符串的硬编码数组。 在此之下,您将看到requestGifs(_closure: @escaping StringsClosure)函数传回硬编码的URL。 在文件顶部的import Foundation语句下,添加import GifLibrary

现在,替换requestGifs(_closure: @escaping StringsClosure)函数的主体,使其看起来像这样:

func requestGifs(_ closure: @escaping StringsClosure) {
   GiphyAPI().getGifUrls { gifs -> KotlinUnit in
       closure(gifs)
       return KotlinUnit()
   }
}

下一步

如果愿意,请在GitHub上查看完成的项目以供参考。 请记住,KMP仍处于试验阶段,极易发生变化,但是它正在Swift发展和改进。 不仅如此, KMP已经成功用于构建App Store中当前可用的应用程序 。 对于希望扩展以在其他平台上开发其应用程序的所有iOS开发人员而言,KMP都是理所当然的下一代技术。

KMP不仅是iOS工程师成为双重威胁移动开发人员的最快途径,它还意味着创建现有的业务逻辑存储库,然后您也可以将其部署到其他平台。 如果您想将应用程序部署到Javascript环境中,则已经为它编写了业务逻辑。

如果您想更深入地了解KMP或Kotlin,请查看这些资源 ,其中包括指向Kotlin subreddit和Slack频道的链接。 Touchlab将于2019年6月4日美国东部标准时间下午2:00举办与本教程相同主题的网络研讨会 。 最后,退后一步,这里有一些关于Touchlab的KMP的其他背景文章,您可能会觉得有用:

  • 我们面向移动架构的方法
  • 跨平台术语过多

翻译自: https://www.infoq.com/articles/kotlin-multiplatform-ios-developers/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

kotlin ios开发

你可能感兴趣的:(kotlin ios开发_适用于iOS开发人员的Kotlin Multiplatform)