Day 13:因应在地口味调整,根据各平台实作功能!

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处理工作的绝活之一.


<<:  [Day5] - Django 介绍

>>:  【D4】初步探索厨房器具:登入与帐号

Day14 v-cloak与v-pre

今天再多来看看两个Vue的指令,v-cloak与v-pre v-cloak 使用v-cloak的原因...

Day 27. SSR 常见问题(2)

遍历 HTML tag 这时候你会需要使用Vue Server Test Utils暴露的另一个方法...

Re: 新手让网页 act 起来: Day11 - React Hooks 之 useState (2)

在上一篇介绍了使用 hooks 的基本规则,以及 useState 的使用方式。今天来针对 init...

33岁转职者的前端笔记-DAY 24 jQuery DOM 节点

jQuery 与 DOM 节点 .parent() / .parents() 往上层找到符合条件的元...

Day 14 讯息伫列

之前,我们都在讨论排程、号志的观念,在有效的排程之後,就能让任务很顺利的运作,达到一个有效的即时系统...