Keyword: swift,swiftUI,ObservableObject
到Day12 使用swiftUI显示Ktor的资料 放在这边
KMMDay12
昨天用了Android显示资料,今天就来用iOS显示吧.今天大部分的时间都会在Xcode中进行.
首先...先在gradle(shared)内的iOS区添加Ktor的依赖,这是给iOS所使用的Ktor,加入後记得sync Gradle啊
...//gradle中
val iosMain by getting{
dependencies{
implementation(Develop.Ktor.ios)
}
}
...//Dependencies中
object Ktor{
...
val ios = "io.ktor:ktor-client-ios:${Versions.ktor}"
...
}
然後找到Android Studio 中找到Xcode专案的位置,如果没有更改的话应该会在iosApp package底下,一个副档名为.xcodeproj的档案
在上面右键⇒ Open in ⇒ Xcode 使用Xcode打开.
原本iOS所使用的swift语言,并不能支持Kotlin独特的suspend与coroutine机制,但好在经过KMM中间层的处理,让这段东西下放到Kotlin原生来处理.
由於网路请求是一个耗时工作,现在我们要来实作一个可供观察的资料来源,让网路请求的结果被通知,在Android中昨天我们已经使用了LiveData来处理,而在iOS中,则是可以使用ObservableObject来作为资料来源.
很巧的是,这两种解法殊途同归,都是利用了观察者模式.来降低资料层与画面层之间的耦合关系.之後可以研究这两段的程序码,资料层的部分都是不知道自己是被如何使用.只尽责地在资料变化时发布改变的消息出去,而接收到的画面层根据自己的需要来显示.这点不只是在Android或是iOS,在KMM中也是随处可见.
(今天的以下部分都将在Xcode上撰写iOS专案,使用swift)
我们在iosApp资料夹上右键,打开选单,选择New File
然後建立一个新的swift档案
命名用被观察的物件加上ViewModel字尾,以这次的CafeResponseItem为例,新的档案名称为"CafeResponseItemViewModel"
建立一个CafeResponseItemViewModel的物件 ,继承ObservableObject,并且把KMM的shared import进来.
import Foundation
import shared
class CafeResponseItemViewModel: ObservableObject {
}
(其实Swift写起来跟Kotlin很类似呢,所以 Kotlin经验者在阅读swift的语法上应该没什麽问题)
把来自shared的DataRepository加入,注意swift和kotlin相比,多了一个let的关键字,而self接近Kotlin的this关键字
class CafeResponseItemViewModel: ObservableObject {
private let repository: DataRepository
init(repository: DataRepository) {
self.repository = repository
}
}
这样我们就能够在其中使用DataRepository了
然後把我们回传的CafeResponseItem Array标记为@Published ,告诉其他观察者这边会发送资讯,请记得接收.
最後加上一个拉取资料的方法,让CafeResponseItem的Array藉由Ktor去更新,整个class如下:
import Foundation
import shared
class CafeResponseItemViewModel: ObservableObject {
@Published var cafeResponseItemList = [CafeResponseItem]()
private let repository: DataRepository
init(repository: DataRepository) {
self.repository = repository
}
func fetch() {
repository.fetchCafesFromNetwork(cityName:"taipei"){ result , error in
if let result = result{
self.cafeResponseItemList = result
}
}
}
}
回到KMM专案一开始就建立好的ContentView.swift,这边是使用swiftUI的格式,也就是所谓的用宣告式UI,最近越来越流行了,在Flutter与Android compose都能看到类似的组件.
import SwiftUI
import shared
struct ContentView: View {
let greet = Greeting().greeting()
var body: some View {
Text(greet)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
PreviewProvider只是提供开发者的预览功能,真正的View是在ContentView.
先把范例预先建立好的greet与Text都删除,我们不需要了,然後放上我们刚刚建立好的CafeResponseItemViewModel
这边要标记@ObservedObject,与刚刚的@Published对应,说明这是接收订阅的一方.
struct ContentView: View {
@ObservedObject var cafeResponseItemViewModel = CafeResponseItemViewModel(repository:DataRepository())
var body: some View {
}
}
body的内容虽然可以直接把观察到的资料放进去,但是iOS有提供相当美观的NavigationView功能,可以提高使用者的体验,我们来试用看看.
这边先用一个简单的文字来看看Navigation效果,将body的View的内容加入NavigationView
var body: some View {
NavigationView {
Text("Hi")
.navigationBarTitle(Text("CafeList"), displayMode: .large)
}
}
然後在View的onAppear,由cafeResponseItemViewModel进行网路请求,这是在View出现时会执行其中的内容
var body: some View {
NavigationView {
Text("Hi")
.navigationBarTitle(Text("CafeList"), displayMode: .large)
.onAppear(perform: {
self.cafeResponseItemViewModel.fetch()
})
}
}
使用List ,将请求回传的List一一显示
var body: some View {
NavigationView {
List(cafeResponseItemViewModel.cafeResponseItemList, id: \.id) { cafe in
Text(cafe.name)
}
.navigationBarTitle(Text("CafeList"), displayMode: .large)
.onAppear(perform: {
self.cafeResponseItemViewModel.fetch()
})
}
}
整体会像这样
最後再把通用文字View替换成自定义的CafeItemView,整个ContentView.swift如下
import shared
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct ContentView: View {
@ObservedObject var cafeResponseItemViewModel = CafeResponseItemViewModel(repository:DataRepository())
var body: some View {
NavigationView {
List(cafeResponseItemViewModel.cafeResponseItemList, id: \.id) { cafe in
CafeItemView(cafeResponseItem: cafe)
}
.navigationBarTitle(Text("CafeList"), displayMode: .large)
.onAppear(perform: {
self.cafeResponseItemViewModel.fetch()
})
}
}
}
struct CafeItemView : View {
var cafeResponseItem: CafeResponseItem
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(cafeResponseItem.name).font(.headline)
Text(cafeResponseItem.id).font(.subheadline)
}
}
}
}
跑起来看看
Ktor的资料成功显示在iOS画面上了!
明天将会研究,如果有各平台不同的实作方法,例如最常用的Log在iOS与Android就不相同,这种情况应该如何去撰写
前情提要 昨天实战了分析了 CDC 官网,并找到了一个 API 能够查看确诊人数,并写个小程序向其发...
yarn add -D tailwindcss@latest postcss@latest auto...
完赛心得 转眼间就过了 30 天啦,第一次参赛有够菜没想到还能迎来这一天。 要坚持每天发文真的很考验...
前言 科技日新月异,AI、区块链、IoT、Web App 等等都是可以选择的道路。 大学就像一间自助...
在现在这种讲求快速开发的开发模式,我们通常不太会自己将所有功能都自己硬刻出来,而是会去使用第三方的套...