iT邦幫忙

2021 iThome 鐵人賽

DAY 13
0

Keyword: expect/actual


有的時候,在不同平台上,功能的實作有平台上的限制,而這些限制並不是可以單單靠程式碼而去同共用的,例如藍芽裝置,在Android和iOS上規格和官方需求就完全不同.所以如果需要使用到藍芽裝置收集資訊,並當成資料層的物件,在物理上是不可能共用的.

好在KMM在限制我們使用shared層共享時,也考慮到了這點,因為平台獨特特性而需要有不同的實作時,經過特別的語法expect/actual,就能夠獨立實作.

建立一個expect規範

讓我們實際來嘗試看看吧.首先我們在SharedMain底下,建立一個檔案,叫做KMMLogger,裡面有兩個部分.一個是針對底層所建立的expect class,另外一個是給上層使用的class.可以注意到所有的上層交互的位置都是這個Class,也因此不會受到expect class的影響.

internal expect class Logger() {
    fun info(tag: String, message: String)
    fun debug(tag: String, message: String)
    fun warn(tag: String, message: String)
    fun error(tag: String, message: String)
}

object KMMLogger {
    private val logger = Logger()

    fun i(tag: String, message: String) {
        logger.info(tag, message)
    }

    fun d(tag: String, message: String) {
        logger.debug(tag, message)
    }

    fun w(tag: String, message: String) {
        logger.warn(tag, message)
    }

    fun e(tag: String, message: String) {
        logger.error(tag, message)
    }
}

然後,在iosMain同樣的路徑下,建立剛剛的expect的實作,這次使用的語法是actual來說明這是expect的實作.

注意,一定要放在完全相同路徑底下,如果你建立了一個log資料夾去存放剛剛建立的檔案,那在iosMain和AndroidMain實作的時候也需要這個package路徑.
你可以藉由package路徑有沒有完全相同來驗證這件事,別擔心,如果路徑不同在expect的區塊便會找不到實作,而標記為錯誤.

iosMain內的實作如下:

internal actual class Logger {
    actual fun info(tag: String, message: String) {
        print("Log level: info || Tag = $tag || Message = $message")
    }

    actual fun debug(tag: String, message: String) {
        print("Log level: debug || Tag = $tag || Message = $message")
    }

    actual fun warn(tag: String, message: String) {
        print("Log level: warning || Tag = $tag || Message = $message")
    }

    actual fun error(tag: String, message: String) {
        print("Log level: error || Tag = $tag || Message = $message")
    }
}

先暫時印出來Log即可

而androidMain如下

import android.util.Log

internal actual class Logger {
    actual fun info(tag: String, message: String) {
        Log.i(tag, message)
    }

    actual fun debug(tag: String, message: String) {
        Log.d(tag, message)
    }

    actual fun warn(tag: String, message: String) {
        Log.w(tag, message)
    }

    actual fun error(tag: String, message: String) {
        Log.e(tag, message)
    }
}

要使用的時候,就使用對上層的KMMLogger,進行優雅的呼叫,例如昨天的iOS Ktor部分

class CafeResponseItemViewModel: ObservableObject {
    @Published var cafeResponseItemList = [CafeResponseItem]()

    private let repository: DataRepository
		private let kmmLogger : KMMLogger //增加Logger的引用

    
    init(repository: DataRepository) {
        self.repository = repository
    }
    
    func fetch() {
        repository.fetchCafesFromNetwork(cityName:"taipei"){ result , error in
            if let result = result{
								self.kmmLogger.info(“Ktor Result”,result)//呼叫Logger
                self.cafeResponseItemList = result
            }
        }
    }
}

對於底層的實作,我們利用expect 去限制,而對於平台使用,就如同一般物件使用,藉由這個分離,使用的部分不用了解實作,所以就避開了各平台實作不一致所帶來各種問題.

有點像interface與implement當作物件之間的約束關係,而expect 與actual即是平台之間的約束關係.

明天我們,會來介紹這幾天使用到的Coroutine,Coroutine是Kotlin處理工作的絕活之一.


上一篇
Day 12: 前往未知秘境!在iOS上展示Ktor資料!
下一篇
Day 14:Coroutine,那是什麼?好吃嘛?
系列文
挑戰 Kotlin Multiplatform Mobile 跨平台開發,透過共同的Kotlin模組同時打造iOS與Android應用!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言