Kotlin multiplatform mobile on windows

Время на прочтение9 мин

Количество просмотров5.9K

Всем привет! Меня зовут Амет Хырхара, я Android разработчик в компании Joy Dev.

Первое, в чём может возникнуть затруднение у Android разработчика при переходе на КММ — это быстрая настройка окружения. Официальный сайт Kotlin не пестрит подробными инструкциями, и можно потратить несколько часов в поисках нужной информации. В данной статье мы пройдёмся по этапам настройки проекта.

Что такое КММ

КММ, или Kotlin Multiplatform Mobile, это технология кроссплатформенной разработки, которая даёт возможность объединить общую бизнес-логику приложения (к примеру, запросы на сервер, обработка данных, работа с БД) в отдельный модуль и при этом использовать нативный UI.

Настройка на Windows

Для того, чтобы создать КММ-проект, нам понадобится зайти в Android Studio, пройти по пути: File -> Settings -> Plugins, выбрать Marketplace и ввести в поисковую строку название инструмента. После этого установить плагин.

Когда плагин установлен, при создании проекта у вас появятся следующие опции. Выбираем первый вариант. 

При создании в параметре ios framework distribution выбираем Regular Framework.

На этом всё, можно начинать писать. Простота установки окупается невозможностью полноценно писать КММ проекты.

MacOS

Чтобы в полную силу работать с КММ: редактировать специфический для iOS-платформ код и в последующем проводить отладку – необходимо устройство от Apple.

Для начала нам необходимо установить Homebrew — это package manager, который позволяет устанавливать недостающие пакеты и библиотеки на macOS и Linux. Открываем официальный сайт и, согласно инструкции, копируем команду, далее вставляем её в терминал — аналог командной строки.

После установки вводим следующую команду: brew install kdoctor — инструмент командной строки, который проверяет вашу систему на наличие нужных файлов и их корректные версии для работы с КММ.

После установки вводим команду kdoctor и ждём результат. Ответ должен быть примерно такой:

Где [v] – означает все хорошо. [x] – нет.

Где [v] – означает все хорошо. [x] – нет.

То есть, инструмент проверяет:

1) Систему. Если она не подходит, то, увы, не судьба.

2) Наличие JDK.

3) Наличие Android Studio и плагина КММ (устанавливаем, как в Windows)

4) Наличие xCode.

5) Наличие Cocoapods и его совместимость с Ruby.

Проблемы, связанные с первыми четырьмя пунктами, описаны здесь. На моём опыте проблемы возникли с Cocoapods и его совместимостью с Ruby, так как это не освещено в официальной документации. Но, прежде чем начать, давайте разберём, что такое cocoapods, Ruby, а также rvm и rbenv.

Ruby – язык программирования, на котором написаны некоторые библиотеки, используемые в проекте.

Cocoapods – менеджер зависимостей (dependency manager), который, в частности, написан на Ruby. Там прописываются наши зависимости (примерно, как в gradle). Список зависимостей обычно указывается в Podfile.

rvm и rbenv –­ инструменты, которые позволяют нам управлять версиями Ruby, обновлять или делать откаты.

После того, как kdoctor проанализировал систему, он подскажет, какую версию Ruby нам нужно установить (подсказки начинаются с *).

Чтобы установить Ruby нужной версии, пишем в терминал brew install ruby@2.7

Для установки cocoapods используйте команды sudo gem install cocoapods и sudo gem install cocoapods-generate 

Если ваша версия Kotlin меньше, чем 1.7.0, то Cocoapods-generate не установятся на Ruby версии 3.0.0 и выше.

Одна из возможных ошибок, которая может возникнуть на данном этапе, это совместимость версий, потому что КММ стабильно работает на версиях ниже текущих, а также версия Ruby, установленная по умолчанию, может отличаться от нужной нам (проверить, какая версия Ruby используется у нас, можно с помощью команды ruby -v).

Установка искомой версии Ruby. Здесь у нас есть два пути: rbenv и rvm.

rbenv

1) Устанавливаем с помощью Homebrew brew install rbenv ruby-build

2) Используем rbenv install –list, чтобы увидеть список доступных версий, выбираем нужную

3) Устанавливаем версию rbenv install <версия>

4) Ставим её по умолчанию rbenv global <версия>

rvm

1) Установка \curl -sSL https://get.rvm.io | bash -s stable

2) Список версий rvm list known

3) Установка конкретной версии rvm install <версия>

4) Установка версии по умолчанию rvm use <версия> --default

После очередной работы kdoctor видим долгожданную надпись: Your system is ready for Kotlin Multiplatform Mobile Development! Можем создавать проект.

Использование Kotlin Multiplatform Wizard

Существует другой способ создания КММ-проекта: использовать Kotlin Multiplatform Wizard. Преимуществом будет выступать возможность автоматического подключения базовых библиотек, плагинов и выбор платформ, с которыми вы хотите работать (при обычном создании появляются только androidApp и iosApp). Затем скачать архив и открыть проект.

Обзор созданного проекта

После загрузки всех компонентов рекомендуем переключиться с вида Android на Project, чтобы видеть всю файловую систему проекта.

Модули и их предназначение

На этом этапе уже видно, что проект отличается от привычного. Вместо одного app модуля у нас создались три: androidApp, iosApp и shared. Если мы откроем shared/src, то увидим ещё три модуля – androidMain, commonMain, iosMain. 

Давайте разбираться.

androidApp – здесь мы прописываем наши UI элементы (Android View или Jetpack Compose) и логику, которые присущи исключительно Android и не имеют альтернативы на других платформах. Например, Activity или Broadcast Receiver – в iOS нет таких сущностей в подобном виде, поэтому они могут использоваться только в androidApp модуле. Также в директории мы видим build.gradle.kts, где будем указывать зависимости исключительно для Android (фрагменты, навигация и т.п.), и тут же подключается shared модуль, который мы рассмотрим далее: implementation(project(":shared")).

iosApp – аналогия androidApp для iOS. Для того, чтобы редактировать код в этом модуле, понадобится xCode. Чтобы открыть проект на xCode, запускаем среду разработки, кликаем Open a project or file, в директории вашего проекта заходим в iosApp и открываем файл iosApp.xcodeproj.

Если вы изначально создавали проект на Windows, а затем перешли на macOS, у вас скорее всего появится ошибка gradlew Permission denied. Для её решения в терминале Android Studio следует ввести следующую команду chmod +x gradlew

shared – здесь мы описываем бизнес-логику приложения: от запросов на сервер до вью моделей. Основная часть кода будет расположена в commomMain.

Два других модуля нужны для написания логики, которая присуща обеим платформам, но немного отличается реализацией. 

Например, в Android при использовании вью моделей мы наследуемся от класса ViewModel(), но в iOS этого делать не нужно. Поэтому в commomMain создаём директорию presentation, а в ней — абстрактный класс CommonViewModel. Добавляем ключевое слово expect (перед abstract), которое означает, что ожидается имплементация этого класса на каждой из платформ, в данном случае – androidMain и iosMain.

commonMain:

expect abstract class CommonViewModel()

В этих модулях мы тоже создаем папку presentation и абстрактный класс CommonViewModel, но expect заменяем на actual, означающее, что данный класс будет имплементирован. Перед конструктором также придётся прописать ключевое слово actual constructor. В iosMain мы оставим так, а в Android пронаследуемся от ViewModel()

iosMain:

expect abstract class CommonViewModel() actual constructor()

androidMain:

actual abstract class CommonViewModel actual constructor() : ViewModel()

Должно получиться как-то так:

Теперь при создании вью модели мы будем наследоваться от CommonViewModel и дёргать реализацию в соответствии с платформой, на которой мы её используем.

Использование expect-actual механизма легче понять, если воспринимать его как интерфейсы. Expect — это, по сути, интерфейс, который мы «переопределяем» (actual) в зависимости от ситуации.  

В shared модуле также есть свой build.gradle.kts, где мы будем указывать наши общие зависимости. Обращаем внимание на sourceSets – здесь мы видим перечень знакомых нам модулей и их копий с суффиксом Test.

Зависимости, размещённые в commonMain, будут относиться ко всем целевым платформам. В androidMain – к Android. В iosMain – к iosX64Main, iosArm64Main, iosSimulatorArm64Main (на это намекает метод dependsOn()). При подключении библиотек путаница не возникает, потому что на официальных порталах всегда детально указывается, как их следует подключать. Следующим этапом мы разберём удобную технологию управления зависимостями в КММ.

Управление зависимостями с помощью buildSrc

По мере разрастания проекта и увеличения числа подключаемых библиотек возникают трудности с мониторингом версий и дублированием. В таком случае хорошим решением будет поместить все имеющие зависимости в отдельный модуль. Таким модулем может выступить buildSrc – специальная библиотека, которая подключается к Gradle-проекту (само название также зарезервировано Gradle-ом) и при сборке проекта компилируется первой.

Чтобы создать buildSrc модуль, на уровне проекта добавляем папку с соответствующим названием. Внутри создаём build.gradle.kts файл, вставляем следующий код и жмём sync.

repositories {
    mavenCentral()
}

plugins {
    `kotlin-dsl`
}

После этого в buildSrc модуле создаём директорию src/main/kotlin ­– если синхронизация прошла успешно, она должна появиться в предложенных. Далее в папке kotlin создаем файл Dependencies.kt. Здесь мы и будем хранить наши зависимости.

В файле создаём три объекта: Plugins, Versions, Deps (внутри него для удобства можно отдельно создать объекты Android и Multiplatform) и вставляем следующий код:

В Plugins указываем базовые плагины:

object Plugins {
   const val androidApp = "com.android.application"
   const val android = "android"
   const val multiplatform = "multiplatform"
   const val androidLib = "com.android.library"
}

В Versions указываем версии SDK, подключаемых плагинов и библиотек:

object Versions {

   // SDK
   const val compileSdk = 32
   const val targetSdk = 32
   const val minSdk = 21

   // Plugins
   const val android_version = "7.3.1"
   const val kotlin_version = "1.7.10"
   const val compose_version = "1.2.1"
   const val compose_activity_version = "1.5.1"

   // Coroutines
   const val coroutines_version = "1.6.4"
}

В Deps прописываем зависимости для наших библиотек, используя ранее введённые версии:

Gradle-плагины для Kotlin и Android:

const val kotlin_gradle_plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin_version}"
const val android_gradle_plugin = "com.android.tools.build:gradle:${Versions.android_version}"

Зависимости для Android:

object Android {
    // Compose
    const val compose_ui = "androidx.compose.ui:ui:${Versions.compose_version}"
    const val compose_ui_tooling = "androidx.compose.ui:ui-tooling:${Versions.compose_version}"
    const val compose_ui_tooling_preview = "androidx.compose.ui:ui-tooling-preview:${Versions.compose_version}"
    const val compose_foundation = "androidx.compose.foundation:foundation:${Versions.compose_version}"
    const val compose_material = "androidx.compose.material:material:${Versions.compose_version}"
    const val compose_activity = "androidx.activity:activity-compose:${Versions.compose_activity_version}"
    // Coroutines
    const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutines_version}"
}

Зависимости для КММ:

object Multiplatform {
    // Coroutines
    const val coroutines_core = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutines_version}"
}

Примечание: в статье используются зависимости, которые подключаются при создании проекта, кроме корутин, которые специально взяты для демонстрации их включения в shared модуль.


Следующий шаг — преобразовать build.gradle.kts файлы нашего проекта. Открываем корневой файл и заменяем строки на наши переменные.

buildscript {
   repositories {
       gradlePluginPortal()
       google()
       mavenCentral()
   }
   dependencies {
       classpath(Deps.android_gradle_plugin)
       classpath(Deps.kotlin_gradle_plugin)
   }
}

В build.gradle.kts (:shared) обновляем плагины:

plugins {
    kotlin(Plugins.multiplatform)
    id(Plugins.androidLib)
}

Примечание: ключевое слово kotlin используется для автоматического добавления префикса org.jetbrains.kotlin в название плагина. То есть, выражения id(org.jetbrains.kotlin.multiplatform) и kotlin(multiplatform) тождественны.

Зависимости:

sourceSets {
        val commonMain by getting {
            dependencies {
                implementation(Deps.Multiplatform.coroutines_core)
            }
        }
        ...
}

Так как мы добавили библиотеку корутин в commonMain, мы можем её использовать как для Android, так и для iOS.

SDK: 

android {
   namespace = "com.example.newkmm"
   compileSdk = Versions.compileSdk
   defaultConfig {
       minSdk = Versions.minSdk
       targetSdk = Versions.targetSdk
   }
}

В build.gradle.kts (:android):

plugins {
   id(Plugins.androidApp)
   kotlin(Plugins.android)
}

android {
    namespace = "com.example.newkmm.android"
    compileSdk = Versions.compileSdk
    defaultConfig {
        applicationId = "com.example.newkmm.android"
        minSdk = Versions.minSdk
        targetSdk = Versions.targetSdk
        versionCode = 1
        versionName = "1.0"
    }
    ...
}
dependencies {
    implementation(project(":shared"))
    implementation(Deps.Android.compose_ui)
    implementation(Deps.Android.compose_ui_tooling)
    implementation(Deps.Android.compose_ui_tooling_preview)
    implementation(Deps.Android.compose_foundation)
    implementation(Deps.Android.compose_material)
    implementation(Deps.Android.compose_activity)
}

Синхронизируем проект. Во вкладке Build убеждаемся, что сначала выполняются таски, связанные с buildSrc, а затем таски основного проекта. 

КММ изначально собирается дольше, чем обычный Android проект, поэтому открываем в gradle.properties и добавляем следующие строки:

org.gradle.caching=true   // 1

org.gradle.parallel=true  // 2

org.gradle.daemon=true    // 3

1 – включаем кеширование.

2 – включаем параллельную сборку не взаимосвязанных задач, так как по умолчанию Gradle выполняет одну задачу за раз. Фича особенно полезна в КММ.

3 – Gradle Daemon по умолчанию включен, но желательно указывать его явно. Этот компонент занимается кешированием, мониторингом файловой системы для определения необходимых для билда файлов и держит JVM в «прогретом» состоянии.

После этого проект будет собираться значительно быстрее.

Итог

Подготовка системы к кроссплатформенной разработке, которая от пользователей Windows требует пары минут, может занять продолжительное время для владельцев macOS, поэтому мы постарались подробно описать, где и какие инструменты можно использовать для достижения цели, а также устранили возможную путаницу с несовместимостью версий.

Проект на старте имеет более сложную файловую систему и изначально состоит из большего числа модулей. В shared модуле мы описываем общую логику, которую имеет приложение и по случаю прибегаем к expect/actual механизму (который легко воспринимать в качестве интерфейса), когда сущности платформы частично отличаются реализацией.

Для централизованного управления зависимостями можно использовать Gradle библиотеку buildSrc, которая позволяет хранить все зависимости и версии в одном файле, разбив их на группы, что также улучшает читабельность кода. При сборке этот модуль компилируется первым, поэтому проблем на этом этапе не возникает. Существует также альтернатива в виде композитной сборки, использовать её следует по ситуации. Об этом есть статья.

Теперь, когда самое скучное позади, можем приступать к написанию логики самого проекта. 

Настройте среду

Прежде чем создать свое первое приложение, работающее как на iOS, так и на Android, вам необходимо настроить среду для разработки многоплатформенных мобильных приложений Kotlin .

Мы рекомендуем вам установить последние стабильные версии для обеспечения совместимости и лучшей производительности.

Tool

Comments

Android Studio

Вы будете использовать Android Studio для создания многоплатформенных приложений и запуска их на имитируемых или аппаратных устройствах.

Xcode

Большую часть времени Xcode будет работать в фоновом режиме. Вы будете использовать его для добавления кода Swift или Objective-C в ваше приложение iOS.

JDK

Чтобы проверить, установлен ли он, выполните следующую команду в терминале Android Studio или в командной строке:

java -version

Kotlin Многоплатформенный мобильный плагин

В Android Studio выберите «Настройки/Предпочтения» | «Плагины», найдите в Marketplace Kotlin Multiplatform Mobile и установите его.

Kotlin plugin

Плагин Kotlin поставляется с каждым релизом Android Studio. Однако его все равно необходимо обновить до последней версии, чтобы избежать проблем с совместимостью.

Чтобы обновить плагин, на экране приветствия Android Studio выберите Plugins | Installed. Нажмите Update рядом с Kotlin . Вы также можете проверить версию Kotlin в Tools | Kotlin | Configure Kotlin Plugin Updates.

Плагин Kotlin должен быть совместим с плагином Kotlin Multiplatform Mobile. См. compatibility table .

Проверьте свое окружение

Чтобы убедиться, что все работает так, как и ожидалось, установите и запустите инструмент KDoctor:

  1. В терминале Android Studio или в инструменте командной строки выполните следующую команду, чтобы установить инструмент с помощью Homebrew:

    brew install kdoctor
    

    Если у вас еще нет Homebrew, install it или см. KDoctor README для других способов его установки.

  2. После завершения установки вызовите KDoctor в консоли:

    kdoctor
    
  3. Если KDoctor обнаружит какие-либо проблемы при проверке вашей среды, просмотрите выходные данные на предмет проблем и возможных решений:

    • Исправьте все неудачные проверки ( [x] ). Описания проблем и возможные решения можно найти после символа * .

    • Проверьте предупреждения ( [!] ) и успешные сообщения ( [v] ). Они также могут содержать полезные заметки и советы.

Возможные проблемы и решения

Android Studio

Убедитесь, что у вас установлена ​​Android Studio. Вы можете получить ее из official website.

Java и JDK
  • Убедитесь, что у вас установлен JDK. Его можно получить из official website .

  • Android Studio использует связанный JDK для выполнения задач Gradle. Чтобы настроить Gradle JDK в Android Studio, выберите Settings/Preferences | Build, Execution, Deployment | Build Tools | Gradle.

  • Вы можете столкнуться с проблемами, связанными с JAVA_HOME . Эта переменная среды указывает местоположение двоичного файла Java, необходимого для Xcode и Gradle. Если это так, следуйте советам KDoctor, чтобы исправить проблемы.

Xcode
  • Убедитесь, что у вас установлен Xcode. Его можно получить из official website .

  • Запустите Xcode в отдельном окне, чтобы принять условия его лицензии и разрешить ему выполнить некоторые необходимые начальные задачи.

  • Error: can't grab Xcode schemes . Если вы столкнулись с такой ошибкой, в Xcode выберите Settings/Preferences | Locations. В поле Command Line Tools выберите ваш Xcode.

Kotlin plugins

Kotlin Многоплатформенный мобильный плагин

  • Убедитесь, что плагин Kotlin Mobile Multiplatform установлен и включен. На экране приветствия Android Studio выберите Plugins | Installed. Убедитесь, что плагин включен. Если его нет в списке Installed, найдите его в Marketplace и установите.

  • Если плагин устарел, нажмите Обновить рядом с названием плагина. То же самое можно сделать в разделе Настройки/Предпочтения | Инструменты | Плагины.

  • Проверьте совместимость плагина Kotlin Multiplatform Mobile с вашей версией Kotlin в таблице Release details .

Kotlin plugin

Убедитесь, что плагин Kotlin обновлен до последней версии. Для этого на экране приветствия Android Studio выберите Plugins | Installed. Нажмите Update рядом с Kotlin .

Вы также можете проверить версию Kotlin в разделе Инструменты | Kotlin | Настроить обновления плагина Kotlin .

Command line

Убедитесь, что у вас установлены все необходимые инструменты:

  • command not found: brew — install Homebrew .

  • command not found: java — install Java .

Next step

В следующей части урока вы создадите свое первое кроссплатформенное мобильное приложение.

Proceed to the next part

Get help

  • Kotlin Slack. Получите invite и присоединяйтесь к каналу #multiplatform .

  • Kotlin трекер проблем. Report a new issue .

Последнее изменение: 11 апреля 2023 г.


Kotlin Multiplatform Mobile находится в стадии альфа-тестирования. Особенности языка и
инструментарий могут быть изменены в будущих версиях Kotlin.

Бета версия ожидается весной 2022 г. Посмотрите “Ключевые моменты видео Kotlin Multiplatform Mobile Beta Roadmap”,
чтобы узнать о планируемом функционале. Также вы можете изучить, как разные компании
уже используют Kotlin для разработки кроссплатформенного программного обеспечения.

Kotlin Multiplatform Mobile (KMM) — это средства разработки ПО, предназначенные для упрощения разработки
кроссплатформенных приложений для мобильных устройств. Вы можете использовать общий код в программах для iOS и Android и
писать код, специфичный для каждой из платформ, только там, где это необходимо. Например, при реализации нативного UI
или при работе с API, которые ограничены конкретной платформой.

Посмотрите вводное видео, в котором менеджер по продуктовому маркетингу
Kotlin Екатерина Петрова объясняет, что такое Kotlin Multiplatform Mobile и как использовать его в вашем проекте. Вместе
с ней вы настроите среду разработки и подготовитесь к созданию вашего первого кроссплатформенных мобильного приложения.

Также вы можете посмотреть другие видео о Kotlin Multiplatform Multiverse
на YouTube.

Поддерживаемые платформы

  • Приложения и библиотеки для Android,
  • Android NDK (ARM64 и ARM32),
  • Устройства Apple iOS (ARM64 и ARM32) и симуляторы,
  • Устройства Apple watchOS (ARM64 и ARM32) и симуляторы.

Технология Kotlin Multiplatform также поддерживает другие платформы,
включая JavaScript, Linux, Windows, и WebAssembly.

Начало с нуля

  • Настройте окружение для кроссплатформенной мобильной разработки,
  • Создайте в IDE ваше первое приложение, работающее и на Android, и на iOS,
  • Изучите примеры проектов,
  • Познакомьте свою команду с кроссплатформенной мобильной разработкой.

Настройте ваше Android приложение для работы на iOS

Если у вас уже есть мобильное Android приложение и вы хотите сделать его кроссплатформенным, то вот что вам может помочь
начать:

  • Настройте окружение для кроссплатформенной мобильной разработки,
  • Сделайте так, чтобы пример Android приложения, хорошо работал под iOS.

Получить помощь

  • Kotlin Slack: получите приглашение и присоединитесь к
    каналу #multiplatform;
  • StackOverflow: подпишитесь на метку «kotlin-multiplatform»;
  • Kotlin issue tracker: сообщите о новой проблеме.

В настоящее время мы переживаем бум появления новых технологий и подходов к написанию мобильных приложений. Одной из них является развивающийся SDK от компании JetBrains  для мультиплатформенной разработки Kotlin Multiplatfrom (KMP) .

Основная идея KMP, как и других кросс-платформенных SDK — оптимизация разработки путем написания кода один раз и последующего его использования на разных платформах.

Согласно концепции JetBrains, Kotlin Multiplatform не является фреймворком. Это именно SDK, который позволяет создавать модули с общим кодом, подключаемые к нативным приложениям.

Создаём своё первое приложение для Kotlin Multiplatform 1

Написанный на Kotlin модуль компилируется в JVM байткод для Android и LLVM байткод для iOS.

Этот модуль (Shared, Common) содержит переиспользуемую бизнес-логику. Платформенные модули iOS/Android, к которым подключен Shared/Common, либо используют написанную логику напрямую, либо имплементируют свою реализацию в зависимости от особенностей платформы.

Общая бизнес-логика может включать в себя:

  • сервисы для работы с сетью;
  • сервисы для работы с БД;
  •  модели данных.

Также в нее могут входить архитектурные компоненты приложения, напрямую не включающие UI, но с ним взаимодействующие:

  • ViewModel;
  • Presenter;
  • Интеракторы и т.п.

Концепцию Kotlin Multiplatform можно сравнить с реализацией Xamarin Native. Однако, в KMP нет модулей или функционала, реализующих UI. Эта логическая нагрузка ложится на подключенные нативные проекты.

Рассмотрим подход на практике и попробуем написать наше первое приложение Kotlin Multiplatform.

Для начала нам потребуется установить и настроить инструменты:

  1. Android Sdk
  2. Xcode с последним iOS SDK.
  3. Intelij IDEA CE или Android Studio. Обе IDE позволяют создавать и настраивать проекты для Kotlin Multiplatform. Но если в Intelij IDEA проект создается автоматически, то в Android Studio большую часть настроек надо сделать вручную. Если вам привычнее работать именно с Android Studio, то подробное руководство по созданию проекта можно посмотреть в документации на Kotlinlang.org

Мы рассмотрим создание проекта с помощью Intelij IDEA.

Выбираем меню File → New → Create Project:

Создаём своё первое приложение для Kotlin Multiplatform 2

В появившемся окне выбираем тип проекта Kotlin → Mobile Android/iOS|Gradle

Далее стандартно задаем путь к JDK, имя и расположение проекта

Создаём своё первое приложение для Kotlin Multiplatform 3

После нажатия кнопки Finish проект сгенерируется и будет почти готов к работе.

Рассмотрим, что у нас получилось:

Создаём своё первое приложение для Kotlin Multiplatform 4

Мультиплатформенные проекты Kotlin обычно делятся на несколько модулей:

  • модуль переиспользуемой бизнес-логики (Shared, commonMain и т.п);
  • модуль для IOS приложения (iOSMain, iOSTest);
  • модуль для Android приложения (androidMain, androidTest).

В них располагается наша бизнес-логика. Сам код базового примера мы разберем немного позже.

Создаём своё первое приложение для Kotlin Multiplatform 5

Код нативного Android приложения располагается в каталоге main, как если бы мы создавали проект по шаблону обычного Android.

iOS приложение создается автоматически и располагается в каталоге iOSApp:

Создаём своё первое приложение для Kotlin Multiplatform 6

Перед тем, как мы проверим работоспособность базового решения, необходимо сделать следующие финальные настройки:

В local.properties зададим путь к SDK Android:

Создаём своё первое приложение для Kotlin Multiplatform 7

Создадим конфигурацию для работы Android приложения:

Создаём своё первое приложение для Kotlin Multiplatform 8

Готово.

Теперь вызовем команду gradle wrapper для сборки нашего модуля общей логики:

Создаём своё первое приложение для Kotlin Multiplatform 9

После сборки модуль для бизнес-логики для Android приложения доступен в app/build/libs:

Создаём своё первое приложение для Kotlin Multiplatform 10

Путь к библиотеке прописывается стандартно, в блоке dependencies файла build.gradle:

Создаём своё первое приложение для Kotlin Multiplatform 11

Теперь наш проект сконфигурирован для запуска Android приложения:

Осталось сделать настройки для запуска приложения iOS.

В файле build.gradle(:app) необходимо изменить настройку архитектура проекта, чтобы наше приложение поддерживало как реальные устройства, так и эмуляторы.

Меняем:

Создаём своё первое приложение для Kotlin Multiplatform 12

На

Создаём своё первое приложение для Kotlin Multiplatform 13

После выполнения сборки создастся фреймворк в app/build/bin/ios:

Создаём своё первое приложение для Kotlin Multiplatform 14

Intelij IDEA автоматически создает в gradle файле код для генерации, подключения и встраивания фреймворка в IOS проект:

Создаём своё первое приложение для Kotlin Multiplatform 15

При ручной настройке проекта (например, через Android Studio) этот код потребуется указать самостоятельно.

После синхронизации gradle iOS проект готов к запуску и проверке с помощью XCode.

Проверяем, что у нас получилось. Открываем проект iOS через iosApp.xcodeproj:

Создаём своё первое приложение для Kotlin Multiplatform 16

Проект имеет стандартную структуру, за исключением раздела app, где мы получаем доступ к коду наших модулей на Kotlin.

Фреймворк действительно подключен автоматически во всех соответствующих разделах проекта:

Создаём своё первое приложение для Kotlin Multiplatform 17

Запускаем проект на эмуляторе:

Создаём своё первое приложение для Kotlin Multiplatform 18

Теперь разберем код самого приложения на базовом примере.

Используемую в проекте бизнес-логику можно разделить на:

  • переиспользуемую (общую);
  • платформенную реализацию.

Создаём своё первое приложение для Kotlin Multiplatform 19

Переиспользуемая логика располагается в проекте commonMain в каталоге kotlin и разделяется на package. Декларации функций, классов и объектов, обязательных к переопределению, помечаются модификатором expect:

Создаём своё первое приложение для Kotlin Multiplatform 20

Реализация expect-функционала задается в платформенных модулях и помечается модификатором actual:

Создаём своё первое приложение для Kotlin Multiplatform 21

Вызов логики производится в нативном проекте:

Создаём своё первое приложение для Kotlin Multiplatform 22

Все очень просто.

Теперь попробуем по тем же принципам сделать что-то посложнее и поинтереснее. Например, небольшое приложение для получения и отображение списка новостей для iOS и Android.

Приложение будет иметь следующую структуру:

Создаём своё первое приложение для Kotlin Multiplatform 23

В общей части (Common) расположим бизнес-логику:

  • сетевой сервис;
  • сервис для запросов новостей.

В модулях iOS/Android приложений оставим только UI компоненты для отображения списка и адаптеры. iOS часть будет написана на Swift, Android – на Kotlin. Здесь в плане работы не будет ничего нового.

Организуем архитектуру приложений по простому паттерну MVP. Презентер, обращающийся к бизнес-логике, также вынесем в Common часть. Также поступим и с протоколом для связи между презентером и экранами UI:

			interface INewsListView :IView {
    fun setupNews(list: List)
}
		

Начнем с бизнес-логики. Т.к весь функционал будет в модуле common, то мы будем использовать в качестве библиотек решения для Kotlin Multiplatform:

1. Ktor – библиотека для работы с сетью и сериализации.

В build.gradle (:app) пропишем следующие зависимости:

			commonMain {
    dependencies {
…
        implementation("io.ktor:ktor-client-core:1.3.2")
        implementation("io.ktor:ktor-client-json:1.3.2")
        implementation("io.ktor:ktor-client-serialization:1.3.2")
    }
}

 
androidMain {
    dependencies {
            …..
        implementation("io.ktor:ktor-client-android:1.3.2")
        implementation("io.ktor:ktor-client-json-jvm:1.3.2")
        implementation("io.ktor:ktor-client-serialization-jvm:1.3.2")
    }
}

iosMain {
    dependencies {
        //….
        implementation("io.ktor:ktor-client-ios:1.3.2")
        implementation("io.ktor:ktor-client-json-native:1.3.2")
        implementation("io.ktor:ktor-client-serialization-native:1.3.2")
    }
}
		

Также добавим поддержку плагина сериализации:

			plugins {
   ….
    id 'org.jetbrains.kotlin.plugin.serialization' version '1.3.72'
}

apply plugin: 'kotlinx-serialization'
		

2. Kotlin Coroutines – для организации многопоточной работы.

			commonMain {
    dependencies {
        …
        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.7")
        implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.14.0")
….
    }
}
 
androidMain {
    dependencies {
        …
        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7")
        implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0")
….
    }
}


iosMain {
    dependencies {
        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.5-native-mt")
        implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.14.0")
….
    }
}
		

При добавлении зависимости в iOS проект обратите внимание, что версия библиотеки должна быть обязательно native-mt и совместима с версией плагина Kotlin multiplatform.

При организации многопоточности с помощью Coroutines необходимо передавать контекст потока (CoroutineContext), в котором логика будет исполняться. Это платформозависимая логика, поэтому используем кастомизацию с помощью expect/actual.

В commonMain создадим Dispatchers.kt, где объявим переменные:

			expect val defaultDispatcher: CoroutineContext

expect val uiDispatcher: CoroutineContext
		

Реализация в androidMain создается легко. Для доступа к соответствующим потокам используем CoroutineDispatchers Main (UI поток) и Default (стандартный для Coroutine):

			actual val uiDispatcher: CoroutineContext
    get() = Dispatchers.Main

actual val defaultDispatcher: CoroutineContext
    get() = Dispatchers.Default
		

С iOS труднее. Та версия Kotlin Native LLVM компилятора, которая используется в Kotlin Multiplatform, не поддерживает background очереди. Это давно известная проблема, которая к сожалению, еще не исправлена

Поэтому попробуем обходной маневр как временное решение проблемы.

			actual val uiDispatcher: CoroutineContext
    get() = MainDispatcher

actual val defaultDispatcher: CoroutineContext
    get() = MainDispatcher


private object  MainDispatcher: CoroutineDispatcher() {

        override fun dispatch(context: CoroutineContext, block: Runnable) {
            dispatch_async(dispatch_get_main_queue()) {
                try {
                    block.run().freeze()
                } catch (err: Throwable) {
                    throw err
                }
            }
        }
}
		

Мы создаем свой CoroutineDispatcher, где прописываем выполнение логики в асинхронной очереди dispatch_async.

Также нам понадобится свой scope для работы сетевого клиента:

iOS

			actual fun ktorScope(block: suspend () -> Unit) {
    GlobalScope.launch(MainDispatcher) { block() }
}
		

Android

			actual fun ktorScope(block: suspend () -> Unit) {
           GlobalScope.launch(Dispatchers.Main) { block() }
       }
		

Применим это при реализации сетевого клиента на Ktor:

			interface INetworkService {
    suspend fun getData(path: String, serializer: KSerializer,completed: (ContentResponse)->Unit)
}

class NetworkService : INetworkService{
    private val httpClient = HttpClient()

    override suspend fun getData(path: String, serializer: KSerializer,completed: (ContentResponse)->Unit){
        //Для ktor используем свой скоуп
        ktorScope {

           var contentResponse = ContentResponse()

           try {

               val json = httpClient.get {
                   url {
                       protocol = URLProtocol.HTTPS
                       host = NetworkConfig.shared.apiUrl
                       encodedPath = path
                       header("X-Api-Key", NetworkConfig.shared.apiKey)
                   }
               }
               print(json)
               val response = kotlinx.serialization.json.Json.nonstrict.parse(serializer, json)

               contentResponse.content = response
           } catch (ex: Exception) {
               val error = ErrorResponse()
               error.message = ex.message.toString()
               contentResponse.errorResponse = error
               print(ex.message.toString())
           }
            //Ответ отдаем в UI-поток
           withContext(uiDispatcher) {
               completed(contentResponse)
           }
       }
    }
}
		

Парсинг реализуем с помощью сериализатора типа KSerializer<T>. В нашем случае это NewsList.serializer(). Пропишем реализацию в сервисе новостей:

			@TheadLocal
class NewsService{
companion object {
    val shared = NewsApi()
}

   val networkService = NetworkService()

    suspend fun getNewsList(completed: (ContentResponse)->Unit){
        val path = "v2/top-headlines?language=en"
         networkService.getData(path, NewsList.serializer(),completed)
    }
}
		

Вызывать бизнес-логику будем в презентере. Для полноценной работы с coroutines нам надо будет создать scope:

			class PresenterCoroutineScope(
    context: CoroutineContext
) : CoroutineScope {

    private var onViewDetachJob = Job()
    override val coroutineContext: CoroutineContext = context + onViewDetachJob

    fun viewDetached() {
        onViewDetachJob.cancel()
    }
}
		

и добавить его в презентер. Вынесем в базовый класс:

			abstract class BasePresenter(private val coroutineContext: CoroutineContext) {

    protected var view: T? = null
    protected lateinit var scope: PresenterCoroutineScope

    fun attachView(view: T) {
        scope = PresenterCoroutineScope(coroutineContext)
        this.view = view
        onViewAttached(view)
    }


…
}
		

Теперь создадим презентер NewsListPresenter для нашего модуля. В инициализатор передадим defaultDispatcher:

			class NewsPresenter:BasePresenter(defaultDispatcher){
    var service: NewsApi = NewsApi.shared
    var data: ArrayList = arrayListOf()


    fun loadData() {
        //запускаем в скоупе
        scope.launch {
            service.getNewsList {
                val result = it
                if (result.errorResponse == null) {
                    data = arrayListOf()
                    data.addAll(result.content?.articles ?: arrayListOf())

                    view?.setupNews(data)
                }
            }
        }
    }
}
		

Обратите внимание! Из-за особенностей текущей работы Kotlin Native с многопоточностью в IOS работа с синглтонами может привести к крашу. Поэтому для корректной работы надо добавить аннотацию @ThreadLocal для используемого объекта:

			class NewsService{
    @ThreadLocal
    companion object {
        val shared = NewsService()
    }
…
}
		

Осталось подключить логику к нативным IOS и Android модулям и обработать ответ от Presenter:

			class NewsListVC: UIViewController {
    private lazy var presenter: NewsPresenter? = {
        let _presenter = NewsPresenter()
        _presenter.attachView(view: self)
        return _presenter
    }()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
            self.presenter?.loadData()
      }


extension NewsListVC : INewsListView {
    func setupNews(list: [NewsItem]) {
…
}
		

Android:

			class NewsActivity : AppCompatActivity(), INewsListView {
….
    private var _presenter: NewsPresenter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        _presenter = NewsPresenter()
        _presenter?.attachView(this)
…
}

override fun setupNews(list: List) {
….
}
		

Запускаем сборку common модуля gradle wrapper, чтобы сборки обновились. Проверяем работу приложений:

Android

Создаём своё первое приложение для Kotlin Multiplatform 24

iOS

Создаём своё первое приложение для Kotlin Multiplatform 25

Готово. Вы великолепны.

Оба наши приложения работают и работают одинаково.

Ссылка на ресурсы.

Информационные материалы, которые использовались:

При переходе на Kotlin Multiplatform Mobile (КММ), у Android разработчика могут возникнуть трудности с быстрой настройкой окружения, поскольку официальный сайт Kotlin не предоставляет подробных инструкций. В данной статье мы рассмотрим этапы настройки проекта. 

Что такое KMM

Кotlin Multiplatform Mobile (KMM) – это технология кроссплатформенной разработки, которая позволяет объединять общую бизнес-логику приложения (например, запросы на сервер, обработку данных и работу с БД) в отдельный модуль и при этом использовать нативный UI.

Настройка на Windows

Для того, чтобы создать КММ-проект, нам нужно зайти в Android Studio, далее пройти по пути: File -> Settings -> Plugins, выбрать Marketplace и ввести в поисковую строку название инструмента. После этого установить плагин.

KMM Для Windows

Когда плагин установлен, при создании проекта вам будут доступны следующие варианты. Выбираем первый.

плагин KMM для Windows

При создании в параметре ios framework distribution выбираем Regular Framework.

На этом этапе завершается процесс настройки, и вы можете приступить к написанию кода. Однако простота установки иногда приводит к затруднениям в реализации всего функционала в КММ-проектах.

MacOS

Для полноценной работы с КММ на macOS, включая редактирование специфичного кода для платформы iOS и последующую отладку, необходимо иметь устройство от Apple.
Для начала установки необходимо установить Homebrew – package manager, который позволяет устанавливать недостающие пакеты и библиотеки на macOS и Linux. Для этого можно перейти на официальный сайт и скопировать команду согласно инструкции, а затем вставить её в терминал – аналог командной строки.

KMM на MacOS

После установки необходимо ввести следующую команду: brew install kdoctor. Это инструмент командной строки, который проверяет наличие необходимых файлов и их корректные версии для работы с КММ.

После установки введите команду “kdoctor” и дождитесь результата. Ответ должен быть примерно такого вида:

KMM установка на MacOS

Таким образом, инструмент проверяет:

1) Систему. Если она не подходит, то, увы, не судьба.

2) Наличие JDK.

3) Наличие Android Studio и плагина КММ (устанавливаем, как в Windows).

4) Наличие xCode.

5) Наличие Cocoapods и его совместимость с Ruby.

Здесь описаны проблемы, связанные с первыми четырьмя пунктами. Я столкнулся с проблемами совместимости Cocoapods и Ruby, которые не были освещены в официальной документации. Перед тем, как приступить к настройке, давайте рассмотрим, что такое Cocoapods, Ruby, а также инструменты rvm и rbenv.

Ruby – это язык программирования, который используется для написания некоторых библиотек, используемых в проекте.

Cocoapods – это менеджер зависимостей (dependency manager), написанный на Ruby. Он позволяет указать зависимости проекта (подобно gradle), которые обычно перечисляются в Podfile.

rvm и rbenv – инструменты, которые дают нам возможность управлять версиями Ruby, обновлять или делать откаты.

После того, как kdoctor проанализировал систему, он подскажет, какую версию Ruby нам нужно установить (подсказки начинаются с *).

Для установки нужной версии Ruby введите в терминал команду brew install ruby@2.7.

Чтобы установить cocoapods, используйте команды sudo gem install cocoapods и sudo gem install cocoapods-generate.

Обратите внимание, что если ваша версия Kotlin меньше 1.7.0, то cocoapods-generate не установится на версиях Ruby 3.0.0 и выше.

На данном этапе возможны ошибки, связанные с несовместимостью версий. Kotlin Multiplatform Mobile стабильно работает на более ранних версиях, а версия Ruby, установленная по умолчанию, может не соответствовать требуемой (чтобы узнать текущую версию Ruby, введите команду ruby -v).

Установка искомой версии Ruby. Здесь у нас есть два пути: rbenv и rvm.

rbenv

1) Устанавливаем с помощью Homebrew brew install rbenv ruby-build

2) Используем rbenv install –list, чтобы увидеть список доступных версий, выбираем нужную

3) Устанавливаем версию rbenv install <версия>

4) Ставим её по умолчанию rbenv global <версия>

rvm

1) Установка \curl -sSL https://get.rvm.io | bash -s stable

2) Список версий rvm list known

3) Установка конкретной версии rvm install <версия>

4) Установка версии по умолчанию rvm use <версия> –default

Когда kdoctor успешно завершит работу, вы увидите желанную надпись: “Your system is ready for Kotlin Multiplatform Mobile Development!”. Теперь вы можете приступать к созданию проекта.

Использование Kotlin Multiplatform Wizard

Вы можете воспользоваться Kotlin Multiplatform Wizard для создания КММ-проекта. Этот инструмент позволяет автоматически подключить базовые библиотеки, плагины и выбрать платформы для работы (при обычном создании проекта доступны только androidApp и iosApp). После создания проекта с помощью инструмента, вы можете скачать архив и открыть его.

Обзор созданного проекта

Рекомендую переключиться с режима просмотра Android на режим просмотра Project после загрузки всех компонентов, чтобы иметь доступ ко всей файловой системе проекта.

На данном этапе проект отличается от стандартного. Вместо одного модуля app было создано три модуля: androidApp, iosApp и shared. Если мы откроем shared/src, то увидим ещё три модуля: androidMain, commonMain, iosMain.

Давайте разберёмся, что представляет каждый модуль.

Модуль androidApp используется для разработки пользовательского интерфейса (UI) и логики, которые характерны только для Android и не могут быть использованы на других платформах. Например, элементы пользовательского интерфейса Android View или Jetpack Compose, а также Activity или Broadcast Receiver – такие сущности отсутствуют в iOS в подобном виде. В файле build.gradle.kts мы указываем зависимости, специфичные только для Android, такие как фрагменты, навигация и т.д. Также мы подключаем shared модуль, который будет рассмотрен далее: implementation(project(“:shared”)).

Модуль iosApp является аналогом androidApp для iOS. Для редактирования кода в этом модуле потребуется xCode. Чтобы открыть проект в xCode, необходимо запустить среду разработки, выбрать “Open a project or file”, затем в директории проекта перейти в iosApp и открыть файл iosApp.xcodeproj.

Если вы создали проект изначально на Windows, а затем перешли на macOS, вы можете столкнуться с ошибкой “gradlew Permission denied”. Чтобы её исправить, в терминале Android Studio нужно выполнить следующую команду: chmod +x gradlew.

В модуле shared мы описываем бизнес-логику приложения, начиная от запросов к серверу до вью-моделей. Основная часть кода будет находиться в commonMain.

создание проекта на KMM

платформ, но имеет некоторые отличия в реализации.

Например, при использовании вью-моделей в Android мы наследуемся от класса ViewModel(), но это не требуется в iOS. Поэтому мы создаём директорию presentation в commonMain и в ней – абстрактный класс CommonViewModel. Мы добавляем ключевое слово “expect” перед “abstract”, что означает, что каждая из платформ (в данном случае androidMain и iosMain) должна имплементировать этот класс.

commonMain:

expect abstract class CommonViewModel()

В этих модулях мы тоже создаём папку presentation и абстрактный класс CommonViewModel, но expect заменяем на actual, означающее, что данный класс будет имплементирован. Перед конструктором также придётся прописать ключевое слово actual constructor. В iosMain мы оставим так, а в Android пронаследуемся от ViewModel(). 

iosMain:

expect abstract class CommonViewModel() actual constructor()

androidMain:

actual abstract class CommonViewModel actual constructor() : ViewModel()

Должно получиться так:

Kotlin Multiplatform Mobile проект

Теперь, когда мы создаём вью-модель, мы будем наследоваться от CommonViewModel и использовать соответствующую реализацию для платформы, на которой используем эту модель.

Механизм expect-actual можно воспринимать как интерфейсы. Expect – это интерфейс, который мы “реализуем” (actual) в зависимости от контекста.

В shared модуле также есть свой build.gradle.kts, где мы указываем общие зависимости. Обратите внимание на sourceSets – здесь мы видим знакомые нам модули и их копии с суффиксом Test.

создание проекта на KMM

Зависимости, которые мы размещаем в commonMain, будут относиться ко всем платформам. Если мы размещаем зависимости в androidMain, то они будут относиться только к Android, а если в iosMain, то к iosX64Main, iosArm64Main и iosSimulatorArm64Main (как указано в методе dependsOn()). При подключении библиотек нет путаницы, потому что официальные порталы всегда подробно описывают, как их следует подключать. В следующем этапе мы рассмотрим удобную технологию управления зависимостями в КММ.

По мере роста проекта и увеличения числа подключаемых библиотек может возникнуть сложность в контроле версий и избежании дублирования. Решением этой задачи может стать создание отдельного модуля для всех зависимостей. В качестве такого модуля может выступать buildSrc – специальная библиотека, которая подключается к Gradle-проекту (само название также зарезервировано Gradle-ом) и при сборке проекта компилируется первой.

Для создания модуля buildSrc необходимо на уровне проекта создать папку с соответствующим названием и внутри неё создать файл build.gradle.kts с определенным кодом, после чего выполнить синхронизацию проекта, нажав sync.

repositories {
    mavenCentral()
}

plugins {
    `kotlin-dsl`
}

Затем в модуле buildSrc необходимо создать директорию src/main/kotlin, которая появится в списке доступных, если синхронизация прошла успешно. После этого внутри директории kotlin создайте файл с названием Dependencies.kt. Этот файл будет содержать все зависимости проекта.

В файле создаём три объекта: Plugins, Versions, Deps (внутри него для удобства можно отдельно создать объекты Android и Multiplatform) и вставляем следующий код:

В Plugins указываем базовые плагины:

object Plugins {
   const val androidApp = "com.android.application"
   const val android = "android"
   const val multiplatform = "multiplatform"
   const val androidLib = "com.android.library"
}

В Versions указываем версии SDK, подключаемых плагинов и библиотек:

object Versions {

   // SDK
   const val compileSdk = 32
   const val targetSdk = 32
   const val minSdk = 21

   // Plugins
   const val android_version = "7.3.1"
   const val kotlin_version = "1.7.10"
   const val compose_version = "1.2.1"
   const val compose_activity_version = "1.5.1"

   // Coroutines
   const val coroutines_version = "1.6.4"
}

В Deps прописываем зависимости для наших библиотек, используя ранее введённые версии:

Gradle-плагины для Kotlin и Android:

const val kotlin_gradle_plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin_version}"
const val android_gradle_plugin = "com.android.tools.build:gradle:${Versions.android_version}"

Зависимости для Android:

object Android {
    // Compose
    const val compose_ui = "androidx.compose.ui:ui:${Versions.compose_version}"
    const val compose_ui_tooling = "androidx.compose.ui:ui-tooling:${Versions.compose_version}"
    const val compose_ui_tooling_preview = "androidx.compose.ui:ui-tooling-preview:${Versions.compose_version}"
    const val compose_foundation = "androidx.compose.foundation:foundation:${Versions.compose_version}"
    const val compose_material = "androidx.compose.material:material:${Versions.compose_version}"
    const val compose_activity = "androidx.activity:activity-compose:${Versions.compose_activity_version}"
    // Coroutines
    const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutines_version}"
}

Зависимости для КММ:

object Multiplatform {
    // Coroutines
    const val coroutines_core = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutines_version}"
}

Примечание: в статье используются зависимости, которые подключаются при создании проекта, кроме корутин, которые специально взяты для демонстрации их включения в shared модуль.

Следующий шаг – преобразовать build.gradle.kts файлы нашего проекта. Открываем корневой файл и заменяем строки на наши переменные.

buildscript {
   repositories {
       gradlePluginPortal()
       google()
       mavenCentral()
   }
   dependencies {
       classpath(Deps.android_gradle_plugin)
       classpath(Deps.kotlin_gradle_plugin)
   }
}
В build.gradle.kts (:shared) обновляем плагины:

plugins {
    kotlin(Plugins.multiplatform)
    id(Plugins.androidLib)
}

Примечание: ключевое слово kotlin используется для автоматического добавления префикса org.jetbrains.kotlin в название плагина. То есть, выражения id(org.jetbrains.kotlin.multiplatform) и kotlin(multiplatform) тождественны.

Зависимости:

sourceSets {
        val commonMain by getting {
            dependencies {
                implementation(Deps.Multiplatform.coroutines_core)
            }
        }
        ...
}

Так как мы добавили библиотеку корутин в commonMain, мы можем её использовать как для Android, так и для iOS.

SDK: 

android {
   namespace = "com.example.newkmm"
   compileSdk = Versions.compileSdk
   defaultConfig {
       minSdk = Versions.minSdk
       targetSdk = Versions.targetSdk
   }
}

В build.gradle.kts (:android):

plugins {
   id(Plugins.androidApp)
   kotlin(Plugins.android)
}

android {
    namespace = "com.example.newkmm.android"
    compileSdk = Versions.compileSdk
    defaultConfig {
        applicationId = "com.example.newkmm.android"
        minSdk = Versions.minSdk
        targetSdk = Versions.targetSdk
        versionCode = 1
        versionName = "1.0"
    }
    ...
}
dependencies {
    implementation(project(":shared"))
    implementation(Deps.Android.compose_ui)
    implementation(Deps.Android.compose_ui_tooling)
    implementation(Deps.Android.compose_ui_tooling_preview)
    implementation(Deps.Android.compose_foundation)
    implementation(Deps.Android.compose_material)
    implementation(Deps.Android.compose_activity)
}

Синхронизируем проект. Во вкладке Build убеждаемся, что сначала выполняются таски, связанные с buildSrc, а затем таски основного проекта. 

KMM проект

КММ изначально собирается дольше, чем обычный Android проект, поэтому открываем в gradle.properties и добавляем следующие строки:

org.gradle.caching=true   // 1

org.gradle.parallel=true  // 2

org.gradle.daemon=true    // 3

  1. Включаем кеширование.
  2.  Включаем параллельную сборку не взаимосвязанных задач, так как по умолчанию Gradle выполняет одну задачу за раз. Фича особенно полезна в КММ.
  3.  Gradle Daemon по умолчанию включён, но желательно указывать его явно. Этот компонент занимается кешированием, мониторингом файловой системы для определения необходимых для билда файлов и держит JVM в «прогретом» состоянии.

В процессе подготовки системы к кроссплатформенной разработке необходимо учесть, что для владельцев macOS это может занять значительно больше времени, чем для пользователей Windows. В данном тексте описываются инструменты, которые можно использовать для достижения цели, и устраняется возможная путаница с несовместимостью версий.

Проект на старте имеет более сложную файловую структуру и состоит из большего числа модулей. В shared модуле описывается общая логика, используя механизм expect/actual в случае отличия реализации сущностей платформы.

Для централизованного управления зависимостями можно использовать Gradle библиотеку buildSrc, где все зависимости и версии хранятся в одном файле, разбитом на группы для повышения читабельности кода. При сборке проекта этот модуль компилируется первым, что упрощает процесс.

Теперь, когда подготовительный этап завершён, можно переходить к написанию логики самого проекта.

Подписывайся на нас на VC, хабре, ВК, там мы публикуем еще больше полезного материала!
А если хочешь крутой проект, напиши нам, и мы вместе придумаем решение.

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Можно ли airdrop установить на windows
  • Как войти в безопасный режим windows 10 на ноутбуке lenovo при включении
  • Android sdk windows installer
  • Компьютер не уходит в спящий режим windows 10 по таймеру
  • Как убрать надпись windows embedded