Keyword:SQLDelight,Native Driver
到24日,在iOS上呈現DB資料
KMMDay24
在昨天讓Android可以享受到KMM底下的SQLDelight內容後,今天我們來讓iOS也能使用SQLDelight.同樣的,在iOS上使用需要比Android多一層的手續,才能讓iOS也了解到Coroutine的使用方法.
我們現在來重寫iOS(shared)內的NativeViewModel,之前是給網路api的Ktor使用,現在要在裡面加入DB的使用,並且修改使用的方式.
//這是Kotlin 在iOSMain底下
class IOSNativeViewModel(
private val onDataState: (List<CAFE>) -> Unit//這個callback由iOS端執行
) : KoinComponent {
private val scope = IOSMainScope(Dispatchers.Main)
private val dataRepository: DataRepository by inject()
init {
observeCafeList()
}
private fun observeCafeList() {
scope.launch {
dataRepository.getCafeFromDb().collect { dataState ->
onDataState(dataState)//藉由callback讓iOS也能接受Flow的數據
}
}
}
public fun fetchCafeListFromNewWork(cityName: String = "taipei") {
scope.launch {
val result = async { dataRepository.fetchCafesFromNetwork(cityName) }
dataRepository.insertCafeToDB(result.await())
}
}
fun onDestroy() {
scope.onDestroy()
}
}
然後我們修改原本的iOS專案內的ObserableViewModel,讓這個物件的資料元從網路APi改為從DB上來的資料
這邊我們是使用callback的方式,讓flow在當數據改變時去執行特定的程式碼,這邊就很簡單把的數據存入Published內,然後Published和ObservableObject就會去更新swiftUI的畫面元件.記得加入onDestroy的函式,讓畫面在離開的時候銷毀coroutine的scope,避免CoroutineLeak的情況發生
//這邊是swift
import Foundation
import shared
class CafeEntityItemViewModel:ObservableObject{
private var viewModel: IOSNativeViewModel? = nil
@Published
var cafes:[CAFE]? = nil
func initData(){
viewModel = IOSNativeViewModel{ [weak self] data in
self?.cafes = data
}
}
func onDestroy(){
viewModel?.onDestroy()
viewModel = nil
}
}
最後在首頁的ContentView修改為新的DB物件CAFE而不是原本的CafeResponseItem,並且讓View再出現時開始拉取新網路資料,並且先顯示DB資料,這樣可以提高使用者體驗.
//這是SwiftUI
import SwiftUI
import shared
struct ContentView: View {
@ObservedObject var cafeEntityViewModel = CafeEntityItemViewModel()
var cafes: [CAFE]?
var body: some View{
NavigationView {
CafeListContent(cafelist: cafeEntityViewModel.cafes)
.navigationBarTitle(Text("CafeList"), displayMode: .large)
.onAppear(perform: {
self.cafeEntityViewModel.initData()
})
.onDisappear(perform: {
self.cafeEntityViewModel.onDestroy()
})
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct CafeListContent: View{
var cafelist: [CAFE]?
var body : some View {
ZStack{
if let cafes = cafelist {
List(cafes, id: \.id) { cafe in
CafeItemView(cafeItem: cafe)
}
}else{
Text("Empty")
}
}
}
}
struct CafeItemView : View {
var cafeItem: CAFE
var body: some View {
HStack {
VStack(alignment: .leading) {
Text(cafeItem.name!).font(.headline)
Text(cafeItem.id).font(.subheadline)
}
}
}
}
明天開始會寫KMM如何進行測試的