Keyword: expect/actual
有的时候,在不同平台上,功能的实作有平台上的限制,而这些限制并不是可以单单靠程序码而去同共用的,例如蓝芽装置,在Android和iOS上规格和官方需求就完全不同.所以如果需要使用到蓝芽装置收集资讯,并当成资料层的物件,在物理上是不可能共用的.
好在KMM在限制我们使用shared层共享时,也考虑到了这点,因为平台独特特性而需要有不同的实作时,经过特别的语法expect/actual,就能够独立实作.
让我们实际来尝试看看吧.首先我们在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处理工作的绝活之一.
今天再多来看看两个Vue的指令,v-cloak与v-pre v-cloak 使用v-cloak的原因...
遍历 HTML tag 这时候你会需要使用Vue Server Test Utils暴露的另一个方法...
在上一篇介绍了使用 hooks 的基本规则,以及 useState 的使用方式。今天来针对 init...
jQuery 与 DOM 节点 .parent() / .parents() 往上层找到符合条件的元...
之前,我们都在讨论排程、号志的观念,在有效的排程之後,就能让任务很顺利的运作,达到一个有效的即时系统...