Русский
Русский
English
Статистика
Реклама

Варианты настройки iosMain sourceSetа в Kotlin Multiplatform Mobile

При использовании Kotlin Multiplatform Mobile сталкиваешься с непривычной особенностьюiOS код рассматривается компилятором в нескольких вариантах: iosArm64 и iosX64, а также iosArm32 (для поддержки девайсов вышедших до iPhone 5s). При разработке под iOS на Swift про эти особенности не думаешь, так как это скрыто в headerах системных библиотек условиями препроцессора.

Для разработчика чаще всего и не должно быть необходимости учитывать архитектуру процессора, на котором будет запущено приложение (особенно если архитектуры одинаковой битности, как iosArm64 и iosX64). И код под обе архитектуры полностью одинаковый, поэтому проект настраивают под использование одного источника исходного кодаiosMain. Есть несколько вариантов объединения ios кода в одном sourceSet, каждый со своими плюсами и минусами.

Commonizer в Kotlin1.4

Kotlin Multiplatform позволяет строить иерархию из KotlinSourceSetов. Например, сделать промежуточный sourceSet со всем ios кодом, как на схеме ниже.

iosMain в иерархии (sourcehttps://kotlinlang.org/docs/reference/mpp-share-on-platforms.html)iosMain в иерархии (sourcehttps://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=truekotlin.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.

Плюсы:

  1. промежуточный sourceSet полноценно поддерживается IDE

  2. отдельные gradle-задачи для компиляции обеих архитектур

Минусы:

  1. cInterop не видны для IDE в промежуточном sourceSet

  2. коммонизация работает только на 1 уровне иерархии (если за iosMain сделать appleMain для ios, macos - не будет работать обобщение)

  3. не все API доступно в промежуточном sourceSet

  4. внешние библиотеки должны иметь свой опубликованный промежуточный sourceSet (не важно как он зоветсяважно какие таргеты в нем объединены)

Один sourceSet для iOS

Следующий подход указан в документации Kotlin Multiplatform Mobile. В данном случае предлагается на этапе конфигурирования gradle выбирать какой таргет нам использоватьiosX64 или iosArm64. И выбор этот делается на основе переменной окружения SDKNAMEона подставляется Xcode автоматически. Поэтому с данным подходом мы сможем скомпилировать под девайс только из Xcode.

Настройка делается следующим образом:

import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetplugins {    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.

Плюсы:

  1. iosMain содержит весь код под обе платформы

  2. cInterop корректно работает

Минусы:

  1. Конфигурация в gradle зависит от переменных окружения

  2. В gradle доступна только одна задача компиляции iOS, а какая архитектура будет собираться решается переменной окружения

  3. Для компиляции под девайс нужно собирать из Xcode

Arm64 sourceSet depends onX64

Выставление зависимостей между 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.

Плюсы:

  1. код не дублирован, лежит в одном из sourceSet

  2. всё платформенное API доступно

  3. отдельные gradle-задачи для компиляции обеих архитектур

  4. cInterop корректно поддерживается

Минусы:

  1. до Kotlin 1.4 cInterop с такой конфигурацией не поддерживался (была ошибка о подключении некорректной архитектуры в линковку)

symlink Arm64 toX64

Последний вариант, используемый нами в 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.

Плюсы:

  1. код не дублирован, лежит в одном sourceSet, а symlink его отражает

  2. всё платформенное API доступно

  3. cinterop доступен и корректно работает на всех версиях Kotlin

Минусы:

  1. git изменения не видны при просмотре через symlink директорию

  2. IDE не замечает автоматически изменения symlink файлов (нужно делать reload directory или же просто работать всегда в одном сорссете)

  3. не работает на Windows (но для iOS и не нужно)

Источник: habr.com
К списку статей
Опубликовано: 08.01.2021 16:23:02
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Разработка под ios

Разработка мобильных приложений

Разработка под android

Kotlin

Kotlin multiplatform

Kotlin native

Mobile developement

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru