您现在的位置是:首页 >技术交流 >Compose也能开发iOS了,快来体验~网站首页技术交流
Compose也能开发iOS了,快来体验~
前言
在之前,我们已经体验了Compose for Desktop 与 Compose for Web,目前Compose for iOS 已经有尚未开放的实验性API,乐观估计今年年底将会发布Compose for iOS。同时Kotlin也表示将在2023年发布KMM的稳定版本。
届时Compose-jb + KMM 将实现Kotlin全平台。
搭建项目
创建项目
因为目前Compose for iOS阶段还在试验阶段,所以我们无法使用Android Studio或者IDEA直接创建Compose支持iOS的项目,这里我们采用之前的方法,先使用Android Studio创建一个KMM项目,如果你不知道如何创建一个KMM项目,可以参照之前的这篇文章KMM的初次尝试~ ,项目目录结构如下所示。
创建好KMM项目后我们需要添加Compose跨平台的相关配置。
添加配置
首先在settings.gradle文件中声明compose插件,代码如下所示:
pluginManagement { repositories { google() gradlePluginPortal() mavenCentral() maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } plugins { val composeVersion = extra["compose.version"] as String id("org.jetbrains.compose").version(composeVersion) } }
这里compose.version的版本号是声明在gradle.properties中的,代码如下所示:
compose.version=1.3.0
然后我们在shared模块中的build文件中引用插件
plugins { kotlin("multiplatform") kotlin("native.cocoapods") id("com.android.library") id("org.jetbrains.compose") }
并为commonMain添加compose依赖,代码如下所示:
val commonMain by getting { dependencies { implementation(compose.ui) implementation(compose.foundation) implementation(compose.material) implementation(compose.runtime) } }
sync之后,你会发现一个错误警告:uikit还处于试验阶段并且有许多bug....
uikit就是compose-jb暴露的UIKit对象。为了能够使用,我们需要在gradle.properties文件中添加如下配置:
org.jetbrains.compose.experimental.uikit.enabled=true
添加好配置之后,我们先来运行下iOS项目,确保添加的配置是无误的。
果然,不运行不知道,一运行吓一跳
这个问题困扰了我两三天,实在是无从下手,毕竟现在相关的资料很少,经过N次的搜索,最终解决的方案很简单:Kotlin版本升级至1.8.0就可以了。
kotlin("android").version("1.8.0").apply(false)
再次运行项目,结果如下图所示。
不过这是KMM的iOS项目,接下来我们看如何使用Compose编写iOS页面。
开始iOS之旅
我们替换掉iOSApp.swift中的原有代码,替换后的代码如下所示:
import UIKit import shared @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) let mainViewController = Main_iosKt.MainViewController() window?.rootViewController = mainViewController window?.makeKeyAndVisible() return true } }
上面的代码看不懂没关系,我们只来看获取mainViewController的这一行
let mainViewController = Main_iosKt.MainViewController()
Main_iosKt.MainViewController是通过新建在shared模块iOSMain目录下的main.ios.kt文件获取的,代码如下所示:
fun MainViewController(): UIViewController = Application("Login") { //调用一个Compose方法 }
接下来所有的事情就都可以交给Compose了。
实现一个登录页面
因为页面这部分是公用的,所以我们在shared模块下的commonMain文件夹下新建Login.kt文件,编写一个简单的登录页面,代码如下所示:
@Composable internal fun login() { var userName by remember { mutableStateOf("") } var password by remember { mutableStateOf("") } Surface(modifier = Modifier.padding(30.dp)) { Column { TextField(userName, onValueChange = { userName = it }, placeholder = { Text("请输入用户名") }) TextField(password, onValueChange = { password = it }, placeholder = { Text("请输入密码") }) Button(onClick = { //登录 }) { Text("登录") } } } }
上述代码声明了一个用户名输入框、密码输入框和一个登录按钮,就是简单的Compose代码。
然后需要在main.ios.kt中调用这个login方法:
fun MainViewController(): UIViewController = Application("Login") { login() }
运行iOS程序,效果如下图所示:
嗯~,Compose 在iOS上UI几乎可以做到100%复用,还有不学习Compose的理由吗?
实现一个双端网络请求功能
在之前的第1弹和第2弹中,我们分别实现了在Desktop、和Web端的网络请求功能,现在我们对之前的功能在iOS上再次实现。
添加网络请求配置
首先在shared模块下的build文件中添加网络请求相关的配置,这里网络请求我们使用Ktor,具体的可参照之前的文章:KMM的初次尝试~
配置代码如下所示:
val commonMain by getting { dependencies { ... implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") implementation("io.ktor:ktor-client-core:$ktorVersion") implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion") implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") } }
val iosMain by getting { dependencies { implementation("io.ktor:ktor-client-darwin:$ktorVersion") } }
val androidMain by getting { dependencies { implementation("io.ktor:ktor-client-android:$ktorVersion") } }
添加接口
这里我们仍然使用「wandroid」中的每日一问接口 :https://wanandroid.com/wenda/list/1/json
DemoReqData与之前系列的实体类是一样的,这里就不重复展示了。
创建接口地址类,代码如下所示。
object Api { val dataApi = "https://wanandroid.com/wenda/list/1/json" }
创建HttpUtil类,用于创建HttpClient对象和获取数据的方法,代码如下所示。
class HttpUtil { private val httpClient = HttpClient { install(ContentNegotiation) { json(Json { prettyPrint = true isLenient = true ignoreUnknownKeys = true }) } } /** * 获取数据 */ suspend fun getData(): DemoReqData { val rockets: DemoReqData = httpClient.get(Api.dataApi).body() return rockets } }
这里的代码我们应该都是比较熟悉的,仅仅是换了一个网络请求框架而已。现在公共的业务逻辑已经处理好了,只需要页面端调用方法然后解析数据并展示即可。
编写UI层
由于Android、iOS、Desktop三端的UI都是完全复用的,所以我们将之前实现的UI搬过来即可。代码如下所示:
Column() { val scope = rememberCoroutineScope() var demoReqData by remember { mutableStateOf(DemoReqData()) } Button(onClick = { scope.launch { try { demoReqData = HttpUtil().getData() } catch (e: Exception) { } } }) { Text(text = "请求数据") } LazyColumn { repeat(demoReqData.data?.datas?.size ?: 0) { item { Message(demoReqData.data?.datas?.get(it)) } } } }
获取数据后,通过
Message方法
将数据展示出来,这里只将作者与标题内容显示出来,代码如下所示。
@Composable fun Message(data: DemoReqData.DataBean.DatasBean?) { Card( modifier = Modifier .background(Color.White) .padding(10.dp) .fillMaxWidth(), elevation = 10.dp ) { Column(modifier = Modifier.padding(10.dp)) { Text( text = "作者:${data?.author}" ) Text(text = "${data?.title}") } } }
分别运行iOS、Android程序,点击请求数据按钮,结果如下图:
这样我们就用一套代码,实现了在双端的网络请求功能。
一个尴尬的问题
我一直认为存在一个比较尴尬的问题,那就是像上面实现一个完整的双端网络请求功能需要用到KMM + Compose-jb,但是KMM与Compose-jb并不是一个东西,但是用的时候呢基本上都是一起用。Compose-jb很久之前已经发了稳定版本只是Compose-iOS目前还没有开放出来,而KMM当前还处于试验阶段,不过在2023年Kotlin的RoadMap中,Kotlin已经表示将会在23年中发布第一个稳定版本的KMM。而Compose for iOS何时发布,我想也是指日可待的事情。
所以,这个系列我觉得改名为:Kotlin跨平台系列更适合一些,要不然以后就会存在KMM跨平台第n弹,Compse跨平台第n弹....
因此,从第四弹开始,此系列将更名为:Kotin跨平台第N弹:~
写在最后
从自身体验来讲,我觉得KMM+Compose-jb 对Android开发者来说是非常友好的,不需要像Flutter那样还需要额外学习Dart语言。所以,你觉得距离Kotlin一统“江山”的日子还会远吗?