iT邦幫忙

2021 iThome 鐵人賽

DAY 24
0

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如何進行測試的


上一篇
Day 23: 不同的環境,不同的Driver,利用Driver 駕馭SQLDelight
下一篇
Day 25: 準備假的Coroutine,讓外面世界不會影響我!
系列文
挑戰 Kotlin Multiplatform Mobile 跨平台開發,透過共同的Kotlin模組同時打造iOS與Android應用!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言