Day 21: iOS也懂Koin喔?其实是KMM当工具人啦

Keyword: Koin,AppDelegate
今天完成的内容,在iOS上使用Koin 放在这边
KMMDay21


有个好消息,原来KMM编译过程中有用到一个物件,在新版本的Xcode後也换成另外版本了,因此KMM的编译过程中找不到.不过在Kotlin的新版本中已经改用另外一版本了,只是目前这个版本的Kotlin还没到Stable版本,所以需要使用early-access版本的.(官方表示在1.5.31版本修正了这个问题)

如何更换Kotlin成为early-access版本呢?就在上方的Tools ⇒ Kotlin ⇒Configure Kotlin Plugin Updates

https://github.com/officeyuli/itHome2021/raw/main/day21/kotlin%20tool2.png

在里面可以选择使用的Kotlin版本,这边我们换成1.5版本的Early Access Preview 1.5.x 版本的Kotlin

https://github.com/officeyuli/itHome2021/raw/main/day21/kotlin%20version.png

然後让他下载新版本的Kotlin就可以啦

此外官方讨论区也有人提到必须要升级Gradle版本到7.2版之後,我也有遇到同样的情况.那调整Gradle版本的位置就在工具列中,

https://github.com/officeyuli/itHome2021/raw/main/day21/tool%20bar.jpg

其中的

https://github.com/officeyuli/itHome2021/raw/main/day21/1632667723624.jpg

点开後把下面的gradle版本调整成7.2 之後同步即可.

使用multiplatformSettings

虽然标题这麽说,但是Koin是给Kotlin专用的,实际上是不能运行在swift或是Object-C版本的.好在我们前几天已经把ViewModel的部分下放到了iosMain之中,也就是说,现在iOS专案内除了负责显示的swiftUI,其他部分都是由KMM的Kotlin部分代劳,而这部分,就能使用Koin注入所需要的物件.

所以:虽然Koin不能运行在显示层,但是其他部分,商业逻辑与资料层都能够进行依赖注入.画面层则是直接呼叫这些Koin所管理的物件,也能进行一定程度的解耦.

首先,我们会用到一个为KMM特制的第三方库进行资料的储存库,会根据平台不同而使用不同的实作方式,来源是:

GitHub - russhwolf/multiplatform-settings: A Kotlin Multiplatform library for saving simple key-value data

同样的,我们在BuildSrc的依赖管理区域,加入这个第三方库.来管理他的版本

object Version{
	...
val multiplatformSettings = "0.7.7"
}
object Develop{
		val multiplatformSettings = "com.russhwolf:multiplatform-settings:${Versions.multiplatformSettings}"
    val multiplatformSettingsTest = "com.russhwolf:multiplatform-settings-test:${Versions.multiplatformSettings}"
}

然後在gradle(shared)中加入使用,记得同步Gradle让其加入专案之中.

sourceSets["commonMain"].dependencies {
	implementation(Develop.multiplatformSettings)
}
sourceSets["commonTest"].dependencies {
	implementation(Develop.multiplatformSettingsTest)
}

建立一个iOS使用的Koin物件

在shared内的iosMain建立起一个档案KoinForIOS.kt,这个档案会给iOS的App呼叫,里面写一个Koin启动时所必要的Function,前几天Android的版本可以直接呼叫我们放在commonMain内的Koin.kt,但是iOS需要过经过一层处理

//这边是Kotlin
fun initKoinIos(
		userDefaults: NSUserDefaults,//iOS独有的使用者资讯档案,
		doOnStartup: () ->Unit): KoinApplication //初始化结束时会呼叫
		 = initKoin(//藉由这行,呼叫Koin.kt内的Koin初始化流程
		    module{//同样建立iOS使用的Module
		        single<Settings>{AppleSettings(userDefaults)}
		        single { doOnStartup }
		    }
)

这样,只要iOS呼叫了这个Function,就能初始化Koin.那这个物件会由KMM包装成iOS也能够看懂的物件以供使用.

撰写iOS 呼叫方法

(之後都是在Xcode内撰写swift)
那我们接下来写呼叫initKoinIos的swift方法.

建立一个swift档案在iOS专案中,我的叫做iOSKoinFunctions,在里面写下Koin初始化的流程

//这是swift
import Foundation
import shared

func startKoin(){
    let _userDefaults = UserDefaults(suiteName: "KMMItExample")!
    let _doONStartup = {NSLog("Hello from iOS/Swift")}
    let koinApplication = KoinForIOSKt.doInitKoinIos(userDefaults: _userDefaults,doOnStartup: _doONStartup)
    _koin = koinApplication.koin
}
private var _koin: Koin_coreKoin? = nil

var koin: Koin_coreKoin {
    return _koin!
}

这边我们UserDefaults放入了专案的名称,这边可以存放各种资料,如果有其他想放的使用者资讯也能放在这边.

然後让传入一个doOnStartUp让iOS在完成时执行作为Log用途,这边就简单呼叫NSLog印出来吧.

最後就是重点,koinApplication 就是初始化的流程.可以注意到KMM把刚刚的KoinForIOS.kt变成了KoinForIOSKT物件(这个其实是Kotlin里面在档案里撰写方法时的特性),然後initKoinIos变成了doInitKoinIos,经过这层转换,就能够在swift之中使用Kotlin的方法.当然swift还是认swift语法,只是这个swift语法是从Kotlin转换过来的.

建立AppDelegate

接下来我们要在iOS App启动的时候一并启动Koin,而KMM预设范例中的swiftUI目前没有办法做到开始时顺带启动Koin,所以我们要写一个AppDelegate,告诉iOS系统,在App启动时要帮我做这些事.

首先,跟之前建立新的swift档案一样,我们建立一个名为AppDelegate的swift档案,然後照iOS的开发流程,让这个档案继承UIResponder, UIApplicationDelegate,就会变成iOS的入口.

//这边是swift
import Foundation
import UIKit
import SwiftUI

@UIApplicationMain //这个注解代表这是App入口
class  AppDelegate: UIResponder, UIApplicationDelegate{
   
}

建立了之後就能够把原本的iosAPP这个档案砍掉了,我们有了新的入口.

然後我们这边有一个application初始化的流程,我们在其中加入初始化Koin的流程

@UIApplicationMain 
class  AppDelegate: UIResponder, UIApplicationDelegate{
    func application(_ application: UIApplication, didFinishLaunchingWithOptions
            launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        startKoin()//刚刚写的startKoin()方法
        return true
    }
}

最後指定一开始时要显示的画面,完整如下

import Foundation
import UIKit
import SwiftUI

@UIApplicationMain
class  AppDelegate: UIResponder, UIApplicationDelegate{
    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions
            launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        startKoin()
        let viewController = UIHostingController(rootView: ContentView())
        self.window = UIWindow(frame: UIScreen.main.bounds)
        self.window?.rootViewController = viewController
        self.window?.makeKeyAndVisible()
        return true
    }
}

按下执行,发现....什麽都没变,跟原本一样.但是其实商业逻辑与资料层已经成为了Koin管理的物件
明天,会进入使用SqlDelight DB来储存资料.


<<:  第二十一天:Gradle Kotlin DSL

>>:  [Day 11]在你顺利的时候来一拳才是标配(前端篇)

[Day7] IoT Maker之Coding知识科普 - (Variable)

1.前言 今天本来要进入函示Part2的,但後来想想这次系列文章面对的是全龄层(普遍级),前面好像也...

受信任的计算机系统评估标准(Trusted Computer System Evaluation Criteria : TCSEC)

可信恢复是“在系统故障後确保恢复而不受影响的能力”。( NIST Glossary )通用标准中指定...

【C#】计算程序的执行时间

我们来看到C#要如何计算程序码的执行时间呢 ~ 有两种方法分别是 Stopwatch DateTim...

D29 - 「来互相伤害啊!」:天时地利

互殴之前当然要先有场地才行,让我们建立 Phaser 场景吧! 建立场景 首先建立 src\comp...