moko-kswift
moko-kswift
moko-kswift - этот плагин, позволяет автоматически генерировать Swift-friendly API из общего кода:
enum-ы, соответствующиеsealed-interface-ам из общего кода, чтобы использовать их вswitchбез веткиdefaultextensionsк платформенным классам (UILabelи тд) и интерфейсам
Детали подключения плагина вы можете узнать из его README и статьи.
Способы подключения
Cocapods
При подключении плагина moko-kswift он добавит следующие таски в gradle. Имя таски генерируется по принципу: kSwift + framework name + Podspec, находиться они будут в группе cocoapods.
:mpp-library:kSwiftMultiplatformLibraryPodspec- если подключен наш плагин dev.icerock.mobile.multiplatform.ios-framework:mpp-library-pods:kSwiftmpp_library_podsPodspec- если подключен плагин cocoapods от JetBrains
Обе эти таски генерируют .podspec файл. Его имя будет: framework_name + Swift.podspec, например mpp-library/MultiplatformLibrarySwift.podspec.
После этого нужно просто подключить его pod MultiplatformLibrarySwift в iosApp/Podfile и использовать import MultiplatformLibrarySwift в нужном файле.
Этот вариант более предпочтителен к использованию, потому что
- Меньше конфликтов с именами, сгенерированные классы и методы доступны только там, где мы его подключили
- Лучше воспроизводимость -
buildPhaseна сборку Kotlin-кода всегда происходит при компиляции пода. Реже будет происходить непонятная ошибка из-за не скомпилированного заранее Kotlin модуля.
Однако, сгенерированные файлы можно подключить и вручную, генерируются они по пути ../framework_name/build/cocoapods/framework/framework_nameSwift/..
Напрямую
Если фреймворк общего кода подключен к iOS-проекту напрямую, то сгенерированные файлы подключить при помощи cocoapods не получится, потому что pod MultiplatformLibrarySwift внутри себя имеет зависимость от основного фреймворка - MultiplatformLibrary.
Чтобы сгенерированные файлы всегда находились в одном месте, можно добавить таску в shared_module/build.gradle, которая переместит сгенерированные файлы в build/generated/swift. После этого нужно просто подключить их вручную к iOS проекту.
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink>().matching {
it.binary is org.jetbrains.kotlin.gradle.plugin.mpp.Framework
}.configureEach {
doLast {
val swiftDirectory = File(destinationDir, "${binary.baseName}Swift")
val xcodeSwiftDirectory = File(buildDir, "generated/swift")
swiftDirectory.copyRecursively(xcodeSwiftDirectory, overwrite = true)
}
}
При самой первой сборке iOS проекта, без предварительной сборки Kotlin, Xcode будет ругаться, что у него нет сгенерированных файлов. Поэтому нужно вручную предварительно компилировать Kotlin Framework и только потом собирать iOS в Xcode.
mvvm-livedata, mvvm-flow и moko-kswift в одном проекте
Проблема
Начиная с moko-mvvm-0.13.0 появилась поддержка декларативного UI - Jetpack Compose и SwiftUI. Она основана на mvvm-flow, без mvvm-livedata.
Однако, на момент написания, часть библиотек еще использовала модуль mvvm-livedata
moko-paging:0.7.1moko-fields:0.9.0- ...
Поэтому, пока все библиотеки не обновятся до поддержки и mvvm-flow и mvvm-livedata, нам иногда придется подключать оба этих модуля. В этом случае возникает проблема с генерацией extensions для iOS.
В mvvm-livedata и в mvvm-flow есть экстеншены с одинаковыми именами, для биндинга UI элемента к State.
Компилятор Kotlin/Native видит конфликты имен, чтобы их избежать он создаст эти экстеншены с _.
Однако, плагин moko-kswift ничего не знает про новые измененные названия extensions c _, он ожидает экстеншены с такими же именами, какие были в Kotlin, поэтому сгенерированный код окажется некорректным (будет использовать не верные имена функций).
Решение
В mpp-library/src/iosMain/... создаем файл со всеми экстеншенами из mvvm-flow или mvvm-livedata, которые понадобятся нам на платформах, например:
import dev.icerock.moko.mvvm.livedata.bindTextTwoWay
import dev.icerock.moko.mvvm.flow.bindTextTwoWay
// ...
fun UITextField.bindTextTwoWay(livedata: MutableLiveData<String>) = bindTextTwoWay(livedata)
fun UITextField.bindTextTwoWay(flow: CStateFlow<String>) = bindTextTwoWay(flow)
Оригинальные модули библиотек нужно будет добавить в исключения для moko-kswift, чтобы генерация происходила только на основе файла из iosMain.
kswift {
excludeLibrary("mvvm-livedata")
excludeLibrary("mvvm-flow")
}
На основе этого moko-kswift успешно сгенерирует файл, где будут все эти экстеншены, но с нормальными именами.