Day 12: 前往未知秘境!在iOS上展示Ktor资料!

Keyword: swift,swiftUI,ObservableObject
到Day12 使用swiftUI显示Ktor的资料 放在这边
KMMDay12


昨天用了Android显示资料,今天就来用iOS显示吧.今天大部分的时间都会在Xcode中进行.

开始撰写swift

首先...先在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}"
      ...
}

https://github.com/officeyuli/itHome2021/raw/main/day12/xcodeLocation.jpg

然後找到Android Studio 中找到Xcode专案的位置,如果没有更改的话应该会在iosApp package底下,一个副档名为.xcodeproj的档案

在上面右键⇒ Open in ⇒ Xcode 使用Xcode打开.

原本iOS所使用的swift语言,并不能支持Kotlin独特的suspend与coroutine机制,但好在经过KMM中间层的处理,让这段东西下放到Kotlin原生来处理.

由於网路请求是一个耗时工作,现在我们要来实作一个可供观察的资料来源,让网路请求的结果被通知,在Android中昨天我们已经使用了LiveData来处理,而在iOS中,则是可以使用ObservableObject来作为资料来源.

很巧的是,这两种解法殊途同归,都是利用了观察者模式.来降低资料层与画面层之间的耦合关系.之後可以研究这两段的程序码,资料层的部分都是不知道自己是被如何使用.只尽责地在资料变化时发布改变的消息出去,而接收到的画面层根据自己的需要来显示.这点不只是在Android或是iOS,在KMM中也是随处可见.

建立ObservableObject提供资料

(今天的以下部分都将在Xcode上撰写iOS专案,使用swift)

我们在iosApp资料夹上右键,打开选单,选择New File

https://github.com/officeyuli/itHome2021/raw/main/day12/new%20file.png

然後建立一个新的swift档案

https://github.com/officeyuli/itHome2021/raw/main/day12/createNewFile.jpg

命名用被观察的物件加上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
            }
        }
    }
}

将资料显示在iOS画面上

回到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()
            })
        }
    }

整体会像这样

https://github.com/officeyuli/itHome2021/raw/main/day12/NameList.jpg

最後再把通用文字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)
            }
        }
    }
}

跑起来看看

https://github.com/officeyuli/itHome2021/raw/main/day12/CafeList.jpg

Ktor的资料成功显示在iOS画面上了!

明天将会研究,如果有各平台不同的实作方法,例如最常用的Log在iOS与Android就不相同,这种情况应该如何去撰写


<<:  [Day 17] 资料产品生命周期管理-辅助决策

>>:  Day2 渗透测试流程与相关规范

【Day 27】- 再爬一次 Dcard ?(实战向 Dcard API 发出请求)

前情提要 昨天实战了分析了 CDC 官网,并找到了一个 API 能够查看确诊人数,并写个小程序向其发...

Day-24 : 开发时,使用到tailwindCSS,今天来讲安装

yarn add -D tailwindcss@latest postcss@latest auto...

[Day 30] 完赛心得 — 大家可以回家啦

完赛心得 转眼间就过了 30 天啦,第一次参赛有够菜没想到还能迎来这一天。 要坚持每天发文真的很考验...

Day 02:Nice to meet you!

前言 科技日新月异,AI、区块链、IoT、Web App 等等都是可以选择的道路。 大学就像一间自助...

如何快速上手第三方套件

在现在这种讲求快速开发的开发模式,我们通常不太会自己将所有功能都自己硬刻出来,而是会去使用第三方的套...