При использовании Kotlin Multiplatform Mobile сталкиваешься с непривычной особенностью — iOS код рассматривается компилятором в нескольких вариантах: iosArm64 и iosX64, а также iosArm32 (для поддержки девайсов вышедших до iPhone 5s). При разработке под iOS на Swift про эти особенности не думаешь, так как это скрыто в header’ах системных библиотек условиями препроцессора.  Для разработчика чаще всего и не должно быть необходимости учитывать архитектуру процессора, на котором будет запущено приложение (особенно если архитектуры одинаковой битности, как iosArm64 и iosX64). И код под обе архитектуры полностью одинаковый, поэтому проект настраивают под использование одного источника исходного кода — iosMain. Есть несколько вариантов объединения ios кода в одном sourceSet, каждый со своими плюсами и минусами. Kotlin Multiplatform позволяет строить иерархию из KotlinSourceSet’ов. Например, сделать промежуточный sourceSet со всем ios кодом, как на схеме ниже. С такой настройкой можно расположить весь код связанный с 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: А для включения commonizer добавляем в gradle.properties: В результате получаем одно место с исходным кодом iOS и работающую помощь от IDE. Но есть и ограничения — не всё iOS API доступно в iosMain. Например, протокол UITextFieldDelegateProtocol полностью пуст: Хотя при работе из iosX64Main/iosArm64Main мы видим полный интерфейс: А так-же при настройке 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. Настройка делается следующим образом: В итоге получаем iosMain полностью работающий и с IDE и с cInterop: Настроенный пример можно посмотреть на GitHub. Плюсы: iosMain содержит весь код под обе платформы cInterop корректно работает Минусы: Конфигурация в gradle зависит от переменных окружения В gradle доступна только одна задача компиляции iOS, а какая архитектура будет собираться решается переменной окружения Для компиляции под девайс нужно собирать из Xcode Выставление зависимостей между sourceSet можно использовать и не только для иерархии. Например указать зависимость iosArm64Main от iosX64Main. Для настройки требуется создание отдельных таргетов и указание зависимости: А весь код в таком случае располагается в директории iosX64Main: Настроенный пример можно посмотреть на GitHub. Плюсы: код не дублирован, лежит в одном из sourceSet всё платформенное API доступно отдельные gradle-задачи для компиляции обеих архитектур cInterop корректно поддерживается Минусы: до Kotlin 1.4 cInterop с такой конфигурацией не поддерживался (была ошибка о подключении некорректной архитектуры в линковку) Последний вариант, используемый нами в IceRock, позволяет не дублировать код, использовать все API и cInterop, а также не требует сложных настроек. Чтобы не дублировать код мы просто создаем symlink для одного из ios sourceSet: А в gradle настраиваем проект с двумя ios таргетами: В результате получаем желаемый результат: Настроенный пример можно посмотреть на GitHub. Плюсы: код не дублирован, лежит в одном sourceSet, а symlink его отражает всё платформенное API доступно cinterop доступен и корректно работает на всех версиях Kotlin Минусы: git изменения не видны при просмотре через symlink директорию IDE не замечает автоматически изменения symlink файлов (нужно делать reload directory или же просто работать всегда в одном сорссете) не работает на Windows (но для iOS и не нужно)  CTO