服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

终于来了!带你体验 Compose for iOS

日期: 来源:AndroidPub收集编辑:黄林晴
前言


目前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一统“江山”的日子还会远吗?


-- END --

推荐阅读

相关阅读

  • Kotlin 协程能完全取代 RxJava 吗?

  • 作者:RainyJiang https://juejin.cn/post/7175803413232844855背景自从 jetbrains 公司提出 Kotlin 协程用来解决异步线程问题,并且衍生出来了 Flow 作为响应式框架,引来了大量
  • 一文搞懂 Android 动态加载 so

  • 作者:Pika https://juejin.cn/post/7107958280097366030背景对于一个普通的android应用来说,so库的占比通常都是巨高不下的,因为我们无可避免的在开发中遇到各种各样需要用到na
  • 面试官:谈谈过滤器和拦截器的区别?

  • 来源:blog.csdn.net/qq_42924666/article/details/109563400一、拦截器和过滤器的区别二、拦截器和过滤器的代码实现三、总结1、什么是Filter及其作用介绍2、Filter API介绍3
  • 字节面试6连问:讲讲 ThreadLocal 与 Handler

  • 安卓进阶涨薪训练营,让一部分人先进大厂大家好,我是皇叔,最近开了一个安卓进阶涨薪训练营,可以帮助大家突破技术&职场瓶颈,从而度过难关,进入心仪的公司。详情见文章:没错!皇叔开了
  • 动态可监控线程池,你还没用起来吗?

  • 「使用线程池 ThreadPoolExecutor 过程中你是否有以下痛点呢?」1.代码中创建了一个 ThreadPoolExecutor,但是不知道那几个核心参数设置多少比较合适2.凭经验设置参数值,上线后
  • 第 3 次面腾讯,竟然栽倒在幂等性上

  • 链接:mydlq.club/article/94前段时间,一位读者说去腾讯面试,部门是 IEG, 考幂等性的问题,他回答得不太好、不全面,整个面试过程才20分钟,就被请出来了。这已经是他第 3 次栽倒在腾
  • 厉害了,Kotlin 协程能完全取代 RxJava?

  • 安卓进阶涨薪训练营,让一部分人先进大厂大家好,我是皇叔,最近开了一个安卓进阶涨薪训练营,可以帮助大家突破技术&职场瓶颈,从而度过难关,进入心仪的公司。详情见文章:没错!皇叔开了

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • 终于来了!带你体验 Compose for iOS

  • 前言目前Compose for iOS 已经有尚未开放的实验性API,乐观估计今年年底将会发布Compose for iOS。同时Kotlin也表示将在2023年发布KMM的稳定版本。届时Compose-jb + KMM 将实
  • 十五分钟讲完个人工智能,听完了能真懂的那种

  • 我从事人工智能相关的工作,有时候亲朋好友就问我,你那个人工智能到底是什么玩意?我一般不做解释,因为很难说清楚。后来,我碰到一个做演讲的朋友,他了解了我的困惑之后,他给我布置了
  • Kotlin 高阶函数与 Standard.kt 源码详解

  • 前言在Kotlin中,高阶函数是指将一个函数作为另一个函数的参数或者返回值。如果用f(x)、g(x)用来表示两个函数,那么高阶函数可以表示为f(g(x))。Kotlin为开发者提供了丰富的高
  • Kotlin 协程能完全取代 RxJava 吗?

  • 作者:RainyJiang https://juejin.cn/post/7175803413232844855背景自从 jetbrains 公司提出 Kotlin 协程用来解决异步线程问题,并且衍生出来了 Flow 作为响应式框架,引来了大量
  • 一文搞懂 Android 动态加载 so

  • 作者:Pika https://juejin.cn/post/7107958280097366030背景对于一个普通的android应用来说,so库的占比通常都是巨高不下的,因为我们无可避免的在开发中遇到各种各样需要用到na
  • 安卓14抢先看,新增“应用双开”

  • 2 月 8 日,谷歌在其安卓开发者社区上公布了首个 Android 14 开发者预览版(Android 14 DP1)。根据谷歌工程副总裁 Dave Burke 在开发者社区中发布的 Android 14 DP1 的说明来看,A