kotlin ios开发
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 /目录。
如果您看到此附加对话框,请按更新 。
如果您以前没有研究过Android Studio,那么可能要花很多钱。这是对本教程中使用的Android Studio部分的介绍。 如果您想对IDE进行全面介绍,就在这里 。
让我们从基础开始:左侧的项目导航器 ,应显示一个文件结构。 如果没有看到文件结构,请选择左上角的“ 项目”选项卡,其中将显示项目导航器 。 在项目导航器的顶部,您很可能会看到一个标记为Android的下拉菜单。 按下它,然后在下拉菜单中选择“ 项目”选项。
以下是KMP项目结构的大致概述:
项目顶层的其他文件(即: local.properties和所有.iml文件)由Android Studio生成,与我们将在KMP中执行的操作无关。 另一方面, settings.gradle是应包含在源代码管理中的配置文件。 与build.gradle文件不同, settings.gradle不是构建脚本,而是gradle的配置文件。
既然您已经了解了Android Studio的基础知识,那么该开始学习KMP项目了!
*注意:如前所述,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开发人员帐户。 或者,如果您已经拥有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
已经准备好处理大部分业务逻辑。
回到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
}
}
这是此代码中发生的情况的分步细分:
HttpClient
的常量。 同样,此行与其在Swift中等效的唯一区别是关键字val
,而不是let
。 JsonFeature
安装到client
对象中,使其能够序列化和反序列化JSON。 serializer
,这是HttpClient
对象的属性,默认情况下为null
。 Json.nonstrict格式指示响应JSON将包含与下一行设置的数据类无关的字段。 GifResult
对象。 JSON中的“数据”字段将填充相应的GifResult.data
属性。 这些字段将继续向下以填充嵌套数据类的层次结构。 apiUrl(path: String)
到HttpRequestBuilder
类。 现在已经有了样板化的网络逻辑,是时候对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
}
}
}
getGifUrls(callback: (List) → Unit)
。 Android和iOS代码将调用此函数。 Unit
返回类型等于Kotlin的Swift void
。 GlobalScope
的上下文中运行以下代码,其中包括其自己的调度程序和作业取消逻辑。 dispatcher
,而不是默认值。 请记住, dispatcher
程序的实现特定于每个平台。 callGiphyApi()
函数返回的值。 GifResult
对象的数据列表... url
属性。 url
是一个字符串,因此它将映射到List
对象。 如果Kotlin it
关键字只有一个,它代表lambda的参数。 Android应用程序的最后一步是,将其现有的硬编码URL列表替换为从Giphy API获取的URL。 这不会像听起来那样困难。
打开文件androidApp / src / main / kotlin / MainActivity 。 Android的Activity
与ViewController
紧密相关,在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
,它本质上是UITableView
和UICollectionView
的Android版本,但是如果存在一些关键差异,您应该进一步研究你很感兴趣 这就是Android方面所有逻辑的全部内容!
如果它不是自动导入,并且您有未解决的参考错误,则可能需要在文件顶部的导入列表中添加以下内容:
import org.gifLibrary.GiphyAPI
按下Android Studio顶部工具栏中的“运行”按钮。 该图标与Xcode的“运行”按钮非常相似。 Android虚拟设备(AVD)管理器中将出现一个窗口,该窗口类似于Xcode的用于管理模拟器的窗口。 区别在于Xcode带有预下载的模拟器。
在新窗口中,如果您在列表中看到任何可用的Android设备,请选择该设备以运行该应用程序。 否则,请按创建新虚拟设备 。 在设备列表中选择电话类别和Pixel 2 XL ,然后按右下角的下一步 。
接下来,选择最新版本的Android(该版本的API级别最高)旁边的下载 。 然后将出现另一个窗口,以安装所选版本的Android。 如果您在最新版本的Android旁边没有看到“ 下载”按钮,则表示您很幸运! 选择该版本,然后再次按下一步 。 在下载Android操作系统时喝杯咖啡,然后在使用新的Android虚拟设备返回时,选择完成 。
全部在Android端完成! 尽情享受您的Android应用程序及其KMP网络逻辑的荣耀。 接下来,回到熟悉的领域。
打开Xcode项目,然后转到iosApp目标的Build Phases ,然后按+按钮,在此处用红色圆圈圈出:
从出现的列表中选择“ 新建运行脚本阶段”选项。 您会在主窗口的列表末尾看到一个新的运行脚本 。 将其在列表中向上拖动,使其位于“ 编译源”上方。 单击箭头旁边的箭头,然后粘贴以下脚本:
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_BUILD_TYPE”。 单击其旁边的箭头以显示“ 调试”和“ 发布”环境。 给调试 “调试”的价值和版本 “释放”的值。
添加另一个用户定义的设置,并将其命名为“ KOTLIN_DEVICE”。 然后单击“ 调试 ”右侧的+按钮。 这将在Debug下面创建一个字段,该字段将显示Any Architecture |。 任何SDK 。 单击它以查看潜在的体系结构选项的列表,然后选择Any iOS Simulator SDK 。 给该字段一个false值。 再次对Debug进行相同的操作,但是这次添加Any iOS SDK并将其值设置为true 。
对“ KOTLIN_DEVICE”下的Release重复这些步骤。 完成所有这些操作后,您的用户定义设置应如下所示:
接下来,在右上角的搜索栏中,搜索“框架搜索路径”:
将以下内容添加到Debug和Release的 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开发