[Разработка под iOS, Разработка мобильных приложений, Разработка под Android, Kotlin] Варианты настройки iosMain sourceSet'а в Kotlin Multiplatform Mobile
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
При использовании Kotlin Multiplatform Mobile сталкиваешься с непривычной особенностью — iOS код рассматривается компилятором в нескольких вариантах: iosArm64 и iosX64, а также iosArm32 (для поддержки девайсов вышедших до iPhone 5s). При разработке под iOS на Swift про эти особенности не думаешь, так как это скрыто в header’ах системных библиотек условиями препроцессора. Для разработчика чаще всего и не должно быть необходимости учитывать архитектуру процессора, на котором будет запущено приложение (особенно если архитектуры одинаковой битности, как iosArm64 и iosX64). И код под обе архитектуры полностью одинаковый, поэтому проект настраивают под использование одного источника исходного кода — iosMain. Есть несколько вариантов объединения ios кода в одном sourceSet, каждый со своими плюсами и минусами.Commonizer в Kotlin 1.4Kotlin Multiplatform позволяет строить иерархию из KotlinSourceSet’ов. Например, сделать промежуточный sourceSet со всем ios кодом, как на схеме ниже.
iosMain в иерархии (source — https://kotlinlang.org/docs/reference/mpp-share-on-platforms.html)С такой настройкой можно расположить весь код связанный с ios в iosMain sourceSet. Он будет успешно компилироваться, но до Kotlin 1.4 IDE не могла корректно анализировать данный код, так как не известно под какую платформу нужно делать анализ — Arm64 или же X64. В результате мы получали ошибки в IDE (но для компилятора все было валидно):
С Kotlin 1.4 проблема поддержки IDE решена за счет нового инструмента — Commonizer. Он автоматически проводит поиск общего между iosArm64Main и iosX64Main и генерирует специальную iosMain klib, в которой содержатся все общие декларации, а IDE проводит анализ используя эту klib. Подробнее про commonizer вы можете узнать в выступлении разработчика Kotlin/Native.Извините, данный ресурс не поддреживается. :( Для настройки своего проекта под этот вариант нужно указать в build.gradle.kts:
plugins {
kotlin("multiplatform")
}
kotlin {
ios {
binaries {
framework {
baseName = "shared"
}
}
}
sourceSets {
val commonMain by getting
val iosMain by getting
}
}
А для включения commonizer добавляем в gradle.properties:
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.enableDependencyPropagation=false
В результате получаем одно место с исходным кодом iOS и работающую помощь от IDE.
Но есть и ограничения — не всё iOS API доступно в iosMain. Например, протокол UITextFieldDelegateProtocol полностью пуст:
public expect interface UITextFieldDelegateProtocol : platform.darwin.NSObjectProtocol {
}
Хотя при работе из iosX64Main/iosArm64Main мы видим полный интерфейс:
public interface UITextFieldDelegateProtocol : platform.darwin.NSObjectProtocol {
public open fun textField(textField: platform.UIKit.UITextField, shouldChangeCharactersInRange: kotlinx.cinterop.CValue<platform.Foundation.NSRange>, replacementString: kotlin.String): kotlin.Boolean
public open fun textFieldDidBeginEditing(textField: platform.UIKit.UITextField): kotlin.Unit
...
}
А так-же при настройке cinterop (например при подключении cocoapods в Kotlin) все декларации не доступны в iosMain при просмотре через IDE (хотя для компилятора все будет корректно работать).Настроенный пример можно посмотреть на GitHub.Плюсы:
- промежуточный sourceSet полноценно поддерживается IDE
- отдельные gradle-задачи для компиляции обеих архитектур
Минусы:
- cInterop не видны для IDE в промежуточном sourceSet
- коммонизация работает только на 1 уровне иерархии (если за iosMain сделать appleMain для ios, macos - не будет работать обобщение)
- не все API доступно в промежуточном sourceSet
- внешние библиотеки должны иметь свой опубликованный промежуточный sourceSet (не важно как он зовется — важно какие таргеты в нем объединены)
Один sourceSet для iOSСледующий подход указан в документации Kotlin Multiplatform Mobile. В данном случае предлагается на этапе конфигурирования gradle выбирать какой таргет нам использовать — iosX64 или iosArm64. И выбор этот делается на основе переменной окружения SDKNAME — она подставляется Xcode автоматически. Поэтому с данным подходом мы сможем скомпилировать под девайс только из Xcode.Настройка делается следующим образом:
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
kotlin("multiplatform")
}
kotlin {
val iosTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget =
if (System.getenv("SDK_NAME")?.startsWith("iphoneos") == true)
::iosArm64
else
::iosX64
iosTarget("ios") {
binaries {
framework {
baseName = "shared"
}
}
}
sourceSets {
val commonMain by getting
val iosMain by getting
}
}
В итоге получаем iosMain полностью работающий и с IDE и с cInterop:
Настроенный пример можно посмотреть на GitHub.Плюсы:
- iosMain содержит весь код под обе платформы
- cInterop корректно работает
Минусы:
- Конфигурация в gradle зависит от переменных окружения
- В gradle доступна только одна задача компиляции iOS, а какая архитектура будет собираться решается переменной окружения
- Для компиляции под девайс нужно собирать из Xcode
Arm64 sourceSet depends on X64Выставление зависимостей между sourceSet можно использовать и не только для иерархии. Например указать зависимость iosArm64Main от iosX64Main.Для настройки требуется создание отдельных таргетов и указание зависимости:
plugins {
kotlin("multiplatform")
}
kotlin {
val ios = listOf(iosX64(), iosArm64())
configure(ios) {
binaries {
framework {
baseName = "shared"
}
}
}
sourceSets {
val commonMain by getting
val iosX64Main by getting
val iosArm64Main by getting {
dependsOn(iosX64Main)
}
}
}
А весь код в таком случае располагается в директории iosX64Main:
Настроенный пример можно посмотреть на GitHub.Плюсы:
- код не дублирован, лежит в одном из sourceSet
- всё платформенное API доступно
- отдельные gradle-задачи для компиляции обеих архитектур
- cInterop корректно поддерживается
Минусы:
- до Kotlin 1.4 cInterop с такой конфигурацией не поддерживался (была ошибка о подключении некорректной архитектуры в линковку)
symlink Arm64 to X64Последний вариант, используемый нами в IceRock, позволяет не дублировать код, использовать все API и cInterop, а также не требует сложных настроек. Чтобы не дублировать код мы просто создаем symlink для одного из ios sourceSet:
ln -s iosX64Main iosArm64Main
А в gradle настраиваем проект с двумя ios таргетами:
plugins {
kotlin("multiplatform")
}
kotlin {
val ios = listOf(iosX64(), iosArm64())
configure(ios) {
binaries {
framework {
baseName = "shared"
}
}
}
sourceSets {
val commonMain by getting
}
}
В результате получаем желаемый результат:
Настроенный пример можно посмотреть на GitHub.Плюсы:
- код не дублирован, лежит в одном sourceSet, а symlink его отражает
- всё платформенное API доступно
- cinterop доступен и корректно работает на всех версиях Kotlin
Минусы:
- git изменения не видны при просмотре через symlink директорию
- IDE не замечает автоматически изменения symlink файлов (нужно делать reload directory или же просто работать всегда в одном сорссете)
- не работает на Windows (но для iOS и не нужно)
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка под iOS, Разработка под Android, C#, Xamarin] Экраны отсутствующего контента в мобильном приложении на примере Xamarin
- [Java, Разработка мобильных приложений, Разработка под Android] Android Bluetooth Low Energy (BLE) — готовим правильно, часть #1 (перевод)
- [Java, C++, Разработка под Android] Android interop with SWIG (a guide). From simple to weird. Part 1 — simple
- [Системное администрирование, Разработка под iOS, Разработка под MacOS] Безопасный downgrade macOS Big Sur (без 1008F)
- [Разработка под iOS, Разработка мобильных приложений, Swift, Аналитика мобильных приложений] Почему я не могу найти Яндекс.Такси через системный поиск на iPhone?
- [Разработка под iOS, Objective C, Swift] Memory Management: ARC vs MRC в iOS
- [Разработка под iOS, Законодательство в IT, Игры и игровые приставки, IT-компании] Apple удалила 46 тысяч приложений из китайского App Store
- [JavaScript, Разработка мобильных приложений, Разработка под Android] Cordova. Quick start
- [JavaScript, Разработка мобильных приложений, Разработка игр, ReactJS] Как я разработал мобильную игру на Android с использованием React.js и выложил её в Google Play Store
- [Разработка под Android, Gradle] Отключаем Jetifier и ускоряем сборку: опыт Тинькофф.Бизнес
Теги для поиска: #_razrabotka_pod_ios (Разработка под iOS), #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_razrabotka_pod_android (Разработка под Android), #_kotlin, #_kotlin_multiplatform, #_kotlin_native, #_kotlin, #_mobile_developement, #_razrabotka_pod_ios (
Разработка под iOS
), #_razrabotka_mobilnyh_prilozhenij (
Разработка мобильных приложений
), #_razrabotka_pod_android (
Разработка под Android
), #_kotlin
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 01:34
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
При использовании Kotlin Multiplatform Mobile сталкиваешься с непривычной особенностью — iOS код рассматривается компилятором в нескольких вариантах: iosArm64 и iosX64, а также iosArm32 (для поддержки девайсов вышедших до iPhone 5s). При разработке под iOS на Swift про эти особенности не думаешь, так как это скрыто в header’ах системных библиотек условиями препроцессора. Для разработчика чаще всего и не должно быть необходимости учитывать архитектуру процессора, на котором будет запущено приложение (особенно если архитектуры одинаковой битности, как iosArm64 и iosX64). И код под обе архитектуры полностью одинаковый, поэтому проект настраивают под использование одного источника исходного кода — iosMain. Есть несколько вариантов объединения ios кода в одном sourceSet, каждый со своими плюсами и минусами.Commonizer в Kotlin 1.4Kotlin Multiplatform позволяет строить иерархию из KotlinSourceSet’ов. Например, сделать промежуточный sourceSet со всем ios кодом, как на схеме ниже. iosMain в иерархии (source — https://kotlinlang.org/docs/reference/mpp-share-on-platforms.html)С такой настройкой можно расположить весь код связанный с ios в iosMain sourceSet. Он будет успешно компилироваться, но до Kotlin 1.4 IDE не могла корректно анализировать данный код, так как не известно под какую платформу нужно делать анализ — Arm64 или же X64. В результате мы получали ошибки в IDE (но для компилятора все было валидно): С Kotlin 1.4 проблема поддержки IDE решена за счет нового инструмента — Commonizer. Он автоматически проводит поиск общего между iosArm64Main и iosX64Main и генерирует специальную iosMain klib, в которой содержатся все общие декларации, а IDE проводит анализ используя эту klib. Подробнее про commonizer вы можете узнать в выступлении разработчика Kotlin/Native.Извините, данный ресурс не поддреживается. :( Для настройки своего проекта под этот вариант нужно указать в build.gradle.kts: plugins {
kotlin("multiplatform") } kotlin { ios { binaries { framework { baseName = "shared" } } } sourceSets { val commonMain by getting val iosMain by getting } } kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.enableDependencyPropagation=false Но есть и ограничения — не всё iOS API доступно в iosMain. Например, протокол UITextFieldDelegateProtocol полностью пуст: public expect interface UITextFieldDelegateProtocol : platform.darwin.NSObjectProtocol {
} public interface UITextFieldDelegateProtocol : platform.darwin.NSObjectProtocol {
public open fun textField(textField: platform.UIKit.UITextField, shouldChangeCharactersInRange: kotlinx.cinterop.CValue<platform.Foundation.NSRange>, replacementString: kotlin.String): kotlin.Boolean public open fun textFieldDidBeginEditing(textField: platform.UIKit.UITextField): kotlin.Unit ... }
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins { kotlin("multiplatform") } kotlin { val iosTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget = if (System.getenv("SDK_NAME")?.startsWith("iphoneos") == true) ::iosArm64 else ::iosX64 iosTarget("ios") { binaries { framework { baseName = "shared" } } } sourceSets { val commonMain by getting val iosMain by getting } } Настроенный пример можно посмотреть на GitHub.Плюсы:
plugins {
kotlin("multiplatform") } kotlin { val ios = listOf(iosX64(), iosArm64()) configure(ios) { binaries { framework { baseName = "shared" } } } sourceSets { val commonMain by getting val iosX64Main by getting val iosArm64Main by getting { dependsOn(iosX64Main) } } } Настроенный пример можно посмотреть на GitHub.Плюсы:
ln -s iosX64Main iosArm64Main
plugins {
kotlin("multiplatform") } kotlin { val ios = listOf(iosX64(), iosArm64()) configure(ios) { binaries { framework { baseName = "shared" } } } sourceSets { val commonMain by getting } } Настроенный пример можно посмотреть на GitHub.Плюсы:
=========== Источник: habr.com =========== Похожие новости:
Разработка под iOS ), #_razrabotka_mobilnyh_prilozhenij ( Разработка мобильных приложений ), #_razrabotka_pod_android ( Разработка под Android ), #_kotlin |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 01:34
Часовой пояс: UTC + 5