Spec-Zone.ru › Kotlin 2

Рекомендуемая структура проекта Kotlin Multiplatform

Обзоры базовых и расширенных концепций структуры проекта должны помочь вам разобраться в наборах исходного кода и управлении зависимостями. А как насчёт модулей, которые объединяют наборы исходного кода и используют эти зависимости?

В статье речь идёт именно о проектах KMP. Чтобы получить общее представление о принятии решений при модульной организации, ознакомьтесь с введением в модульную организацию Android.

Оптимальная структура модулей

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

Общий подход можно описать следующим образом:

  • Точки входа ваших приложений должны находиться в отдельных модулях, каждый из которых зависит от необходимых модулей с общим кодом.

  • Общий код обычно разделяют на бизнес-логику и пользовательский интерфейс, при этом цель состоит в том, чтобы избежать ненужных зависимостей:

    • Если все приложения, созданные в рамках проекта KMP, используют как общий код пользовательского интерфейса, так и общую бизнес-логику, для всего общего кода может быть достаточно одного модуля shared.

    • Если пользовательский интерфейс какого-либо приложения написан с использованием нативного кода (например, вы реализовали интерфейс iOS на чистом Swift), имеет смысл отделить код пользовательского интерфейса от бизнес-логики, чтобы избежать зависимостей от Compose Multiplatform там, где они не нужны. Таким образом, можно создать модули sharedLogic и sharedUI и добавлять их в качестве зависимостей к модулям точек входа по мере необходимости.

  • Если в проекте есть серверный код, которому нужно совместно использовать логику с клиентскими приложениями, рекомендуется организовать его следующим образом:

    • Папка app с модулями точек входа и модулями общего клиентского кода, организованными описанным выше способом.

    • Модуль server с кодом, специфичным для сервера.

    • Модуль core с кодом, общим для сервера и клиентов, например моделями и проверкой данных.

Если в проекте используется устаревшая структура, в которой точки входа приложений и общий код находятся в одном модуле, воспользуйтесь приведёнными ниже инструкциями, чтобы вынести точки входа в отдельные модули.

Если вы планируете использовать Android Gradle Plugin 9 или более новую версию, необходимо отделить точки входа приложения Android от общего кода. Подробнее см. в нашей статье о переходе на AGP 9.

Создание отдельных модулей для точек входа приложения

В качестве примера перехода к рекомендуемой структуре мы рассмотрим старый пример Compose Multiplatform, который можно найти в ветке old-project-structure репозитория с примерами.

Пример состоит из одного модуля Gradle (composeApp), который содержит весь общий код и точки входа KMP, а также папки iosApp с кодом и конфигурацией проекта iOS.

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

Модуль для точки входа приложения Android

Создание и настройка модуля приложения Android

Чтобы создать модуль приложения Android (androidApp):

  1. Создайте каталог androidApp в корне проекта.

  2. В этом каталоге создайте пустой файл build.gradle.kts и каталог src.

  3. Добавьте новый модуль в настройки проекта в файле settings.gradle.kts, добавив эту строку в конец файла:

    include(":androidApp")
    
  4. В главном меню выберите Сборка | Синхронизировать проект с файлами Gradle или нажмите кнопку обновления Gradle в редакторе.

Настройка сценария сборки приложения Android

Настройте сценарий сборки Gradle для нового модуля:

  1. Добавьте плагин Kotlin Android Gradle в каталог версий в файле gradle/libs.versions.toml:

    [plugins]
    kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
    
  2. Укажите плагины, необходимые модулю приложения Android, в файле androidApp/build.gradle.kts:

    plugins {
       alias(libs.plugins.kotlinAndroid)
       alias(libs.plugins.androidApplication)
       alias(libs.plugins.composeMultiplatform)
       alias(libs.plugins.composeCompiler)
    }
    
  3. Убедитесь, что все эти плагины указаны в файле build.gradle.kts в корневом каталоге:

    plugins {
        alias(libs.plugins.kotlinAndroid) apply false
        alias(libs.plugins.androidApplication) apply false
        alias(libs.plugins.composeMultiplatform) apply false
        alias(libs.plugins.composeCompiler) apply false
        // ...
    }
    
  4. Чтобы добавить необходимые зависимости, скопируйте существующие зависимости из блока androidMain.dependencies {} сценария сборки composeApp и добавьте зависимость от самого модуля composeApp. В этом примере результат должен выглядеть так:

    kotlin {
        dependencies { 
            implementation(projects.composeApp)
            implementation(libs.androidx.activity.compose)
            implementation(libs.compose.uiToolingPreview)
        }
    }
    
  5. Скопируйте весь блок android {} с конфигурацией, специфичной для Android, из файла composeApp/build.gradle.kts в файл androidApp/build.gradle.kts.

  6. Скопируйте параметры компилятора из блока androidTarget {} файла composeApp/build.gradle.kts в блок target {} файла androidApp/build.gradle.kts:

    kotlin {
        target {
            compilerOptions {
                jvmTarget.set(JvmTarget.JVM_11)
            }
        }
    }
    

    Если в сценарии сборки composeApp настроены другие плагины или свойства, не забудьте перенести их и в сценарий сборки androidApp.

  7. Измените конфигурацию модуля composeApp с приложения Android на библиотеку Android, поскольку фактически он становится библиотекой. В файле composeApp/build.gradle.kts:

    • Измените ссылку на плагин Gradle:

               alias(libs.plugins.androidApplication)
           
               alias(libs.plugins.androidLibrary)
           
    • Удалите строки со свойствами приложения из блока android.defaultConfig {}:

              defaultConfig {
                  applicationId = "com.jetbrains.demo"
                  minSdk = libs.versions.android.minSdk.get().toInt()
                  targetSdk = libs.versions.android.targetSdk.get().toInt()
                  versionCode = 1
                  versionName = "1.0"
              }
          
              defaultConfig {
                  minSdk = libs.versions.android.minSdk.get().toInt()
              }
          
  8. В главном меню выберите Сборка | Синхронизировать проект с файлами Gradle или нажмите кнопку обновления Gradle в редакторе.

Перемещение кода и запуск приложения Android

  1. Переместите каталог composeApp/src/androidMain в каталог androidApp/src/, но учитывайте, что код, который должен оставаться кроссплатформенным:

    • Код точки входа, например MainActivity.kt в нашем примере, должен находиться в модуле androidApp, чтобы приложение Android собиралось правильно.

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

  2. Переименуйте каталог androidApp/src/androidMain в main.

  3. Если конфигурация настроена правильно, импорты в файле androidApp/src/main/.../MainActivity.kt будут работать, и код скомпилируется.

  4. Если вы используете IntelliJ IDEA или Android Studio, IDE распознает новый модуль и автоматически создаст новую конфигурацию запуска androidApp. Если этого не произошло, вручную измените конфигурацию запуска Android composeApp:

    1. В раскрывающемся списке конфигураций запуска выберите Изменить конфигурации.

    2. Найдите конфигурацию composeApp в категории Android.

    3. В поле Общие | Модуль замените demo.composeApp на demo.androidApp.

  5. Запустите новую конфигурацию, чтобы убедиться, что приложение работает как ожидалось.

  6. Если все работает правильно, удалите блок kotlin.sourceSets.androidMain.dependencies {} из файла composeApp/build.gradle.kts.

Вы вынесли точку входа Android в отдельный модуль. Теперь обновите модуль с общим кодом, чтобы использовать новый плагин библиотеки Android-KMP.

Настольное приложение JVM

Создание и настройка модуля настольного приложения

Чтобы создать модуль настольного приложения (desktopApp):

  1. Создайте каталог desktopApp в корне проекта.

  2. В этом каталоге создайте пустой файл build.gradle.kts и каталог src.

  3. Добавьте новый модуль в настройки проекта в файле settings.gradle.kts, добавив эту строку:

    include(":desktopApp")
    

Настройка сценария сборки настольного приложения

Чтобы сценарий сборки настольного приложения работал:

  1. Добавьте плагин Kotlin JVM Gradle в каталог версий в файле gradle/libs.versions.toml:

    [plugins]
    kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
    
  2. Укажите плагины, необходимые модулю общего пользовательского интерфейса, в файле desktopApp/build.gradle.kts:

    plugins {
       alias(libs.plugins.kotlinJvm)
       alias(libs.plugins.composeMultiplatform)
       alias(libs.plugins.composeCompiler)
    }
    
  3. Убедитесь, что все эти плагины указаны в файле build.gradle.kts в корневом каталоге:

    plugins {
        alias(libs.plugins.kotlinJvm) apply false
        alias(libs.plugins.composeMultiplatform) apply false
        alias(libs.plugins.composeCompiler) apply false
        // ...
    }
    
  4. Чтобы добавить необходимые зависимости от других модулей, скопируйте существующие зависимости из блоков commonMain.dependencies {} и jvmMain.dependencies {} сценария сборки composeApp. В этом примере результат должен выглядеть так:

    kotlin {
        dependencies { 
            implementation(projects.sharedLogic)
            implementation(projects.sharedUI)
            implementation(compose.desktop.currentOs)
            implementation(libs.kotlinx.coroutinesSwing)
        }
    }
    
  5. Скопируйте блок compose.desktop {} с конфигурацией, специфичной для настольной платформы, из файла composeApp/build.gradle.kts в файл desktopApp/build.gradle.kts:

    compose.desktop {
        application {
            mainClass = "compose.project.demo.MainKt"
    
            nativeDistributions {
                targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
                packageName = "compose.project.demo"
                packageVersion = "1.0.0"
            }
        }
    }
    
  6. В главном меню выберите Сборка | Синхронизировать проект с файлами Gradle или нажмите кнопку обновления Gradle в редакторе.

Перемещение кода и запуск настольного приложения

После завершения настройки переместите код настольного приложения в новый каталог:

  1. Создайте новый каталог main в каталоге desktopApp/src.

  2. Переместите каталог composeApp/src/jvmMain/kotlin в каталог desktopApp/src/main/. Важно, чтобы координаты пакета соответствовали конфигурации compose.desktop {}.

  3. Если конфигурация настроена правильно, импорты в файле desktopApp/src/main/.../main.kt будут работать, и код скомпилируется.

  4. Чтобы запустить настольное приложение, измените конфигурацию запуска composeApp [jvm]:

    1. В раскрывающемся списке конфигураций запуска выберите Изменить конфигурации.

    2. Найдите конфигурацию composeApp [jvm] в категории Gradle.

    3. В поле Проект Gradle замените ComposeDemo:composeApp на ComposeDemo:desktopApp.

  5. Запустите обновленную конфигурацию, чтобы убедиться, что приложение работает как ожидалось.

  6. Если все работает правильно:

    • Удалите каталог composeApp/src/jvmMain.

    • Удалите код, связанный с настольной платформой, из файла composeApp/build.gradle.kts:

      • блок compose.desktop {},

      • блок jvmMain.dependencies {} внутри блока Kotlin sourceSets {},

      • объявление целевой платформы jvm() внутри блока kotlin {}.

Веб-приложение

Создание и настройка модуля веб-приложения

Чтобы создать модуль веб-приложения (webApp):

  1. Создайте каталог webApp в корне проекта.

  2. В этом каталоге создайте пустой файл build.gradle.kts и каталог src.

  3. Добавьте новый модуль в настройки проекта в файле settings.gradle.kts, добавив эту строку в конец файла:

    include(":webApp")
    

Настройка сценария сборки веб-приложения

Чтобы сценарий сборки веб-приложения работал:

  1. Укажите плагины, необходимые модулю общего пользовательского интерфейса, в файле webApp/build.gradle.kts:

     ```kotlin
     plugins {
        alias(libs.plugins.kotlinMultiplatform)
        alias(libs.plugins.composeMultiplatform)
        alias(libs.plugins.composeCompiler)
     }
     ```
    
  2. Убедитесь, что все эти плагины указаны в файле build.gradle.kts в корневом каталоге:

    plugins {
        alias(libs.plugins.kotlinMultiplatform) apply false
        alias(libs.plugins.composeMultiplatform) apply false
        alias(libs.plugins.composeCompiler) apply false
        // ...
    }
    
  3. Скопируйте объявления целевых платформ JavaScript и Wasm из файла composeApp/build.gradle.kts в блок kotlin {} файла webApp/build.gradle.kts:

    kotlin {
        js {
            browser()
            binaries.executable()
        }
    
        @OptIn(ExperimentalWasmDsl::class)
        wasmJs {
            browser()
            binaries.executable()
        }
    }
    
  4. Добавьте необходимые зависимости от других модулей:

    kotlin {
        sourceSets {
            commonMain.dependencies { 
                implementation(projects.sharedLogic)
                // Provides the necessary entry point API
                implementation(compose.ui)
            }
        }
    }
    
  5. В главном меню выберите Сборка | Синхронизировать проект с файлами Gradle или нажмите кнопку обновления Gradle в редакторе.

Перемещение кода и запуск веб-приложения

После завершения настройки переместите код веб-приложения в новый каталог:

  1. Переместите весь каталог composeApp/src/webMain в каталог webApp/src. Если конфигурация настроена правильно, импорты в файле webApp/src/webMain/.../main.kt будут работать, и код скомпилируется.

  2. Обновите имя сценария в файле webApp/src/webMain/resources/index.html: замените composeApp.js на webApp.js.

  3. Чтобы запустить веб-приложение, измените конфигурацию запуска composeApp [wasmJs]:

    1. В раскрывающемся списке конфигураций запуска выберите Изменить конфигурации.

    2. Найдите конфигурацию composeApp [wasmJs] в категории Gradle.

    3. В поле Проект Gradle замените ComposeDemo:composeApp на ComposeDemo:webApp.

  4. Повторите эти действия для composeApp [js], чтобы также запускать версию JavaScript.

  5. Запустите конфигурации, чтобы убедиться, что приложение работает как ожидалось.

  6. Если все работает правильно:

    • Удалите каталог composeApp/src/webMain.

    • Удалите код, связанный с веб-платформой, из файла composeApp/build.gradle.kts:

      • блок webMain.dependencies {} внутри блока Kotlin sourceSets {},

      • объявления целевых платформ js {} и wasmJs {} внутри блока kotlin {}.

Настройка общего модуля

В примере приложения используются общий пользовательский интерфейс и общая бизнес-логика, поэтому для всего общего кода достаточно одного общего модуля: можно просто переоборудовать composeApp в модуль общего кода.

Единственное изменение конфигурации Gradle, не связанное с подключением модулей точек входа, — это новый плагин Android Library Gradle. Этот плагин создан специально для многоплатформенных проектов и необходим для использования AGP 9 и более поздних версий.

Необходимые изменения:

  1. Добавьте плагин библиотеки Android-KMP в каталог версий в файле gradle/libs.versions.toml:

    [plugins]
    androidMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }
    
  2. Добавьте плагины, необходимые модулю общего пользовательского интерфейса, в файл composeApp/build.gradle.kts:

    plugins {
       alias(libs.plugins.kotlinMultiplatform)
       alias(libs.plugins.androidMultiplatformLibrary)
       alias(libs.plugins.composeMultiplatform)
       alias(libs.plugins.composeCompiler)
    }
    
  3. Добавьте следующую строку в корневой файл build.gradle.kts, чтобы избежать конфликтов при применении плагина:

    alias(libs.plugins.androidMultiplatformLibrary) apply false
    
  4. В файле composeApp/build.gradle.kts замените блок kotlin.androidTarget {} блоком kotlin.androidLibrary {}:

    androidLibrary {
        namespace = "compose.project.demo.composedemo"
        compileSdk = libs.versions.android.compileSdk.get().toInt()
    
        compilerOptions {
            jvmTarget = JvmTarget.JVM_11
        }
    
        androidResources {
            enable = true
        }
    }
    
  5. Удалите корневой блок android {} из файла composeApp/build.gradle.kts.

  6. Удалите зависимости androidMain, поскольку весь код перемещен в модуль приложения: удалите блок kotlin.sourceSets.androidMain.dependencies {}.

  7. Убедитесь, что приложение Android работает как ожидалось.

(Необязательно) Разделение общей логики и общего пользовательского интерфейса

Если некоторые целевые платформы проекта используют нативный пользовательский интерфейс, возможно, стоит разделить общий код на модули sharedLogic и sharedUI, чтобы модулям приложений с нативным пользовательским интерфейсом не требовалась зависимость от Compose Multiplatform для использования общего кода.

Ниже на примере того же приложения показан возможный подход.

Создание модуля общей логики

Прежде чем создавать модуль, определите, что относится к бизнес-логике, то есть какой код не зависит ни от пользовательского интерфейса, ни от платформы. В этом примере единственный подходящий вариант — функция currentTimeAt(), которая возвращает точное время для пары «местоположение и часовой пояс». Напротив, класс данных Country зависит от DrawableResource из Compose Multiplatform и не может быть отделен от кода пользовательского интерфейса.

Если в проекте уже есть модуль shared, например потому, что вы используете общий код не для всего пользовательского интерфейса, можно использовать этот модуль вместо sharedLogic. Возможно, стоит переименовать его, чтобы четче отделить общую логику от пользовательского интерфейса.

Вынесите соответствующий код в модуль sharedLogic:

  1. Создайте каталог sharedLogic в корне проекта.

  2. В этом каталоге создайте пустой файл build.gradle.kts и каталог src.

  3. Добавьте новый модуль в settings.gradle.kts, добавив эту строку в конец файла:

    include(":sharedLogic")
    
  4. Настройте сценарий сборки Gradle для нового модуля.

    1. Добавьте плагин библиотеки Android-KMP в каталог версий в файле gradle/libs.versions.toml:

      [plugins]
      androidMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }
      
    2. Укажите плагины, необходимые модулю общей логики, в файле sharedLogic/build.gradle.kts:

      plugins {
          alias(libs.plugins.kotlinMultiplatform)
          alias(libs.plugins.androidMultiplatformLibrary)
      }
      
    3. Убедитесь, что эти плагины указаны в файле build.gradle.kts в корневом каталоге:

      plugins {
        alias(libs.plugins.androidMultiplatformLibrary) apply false
        alias(libs.plugins.kotlinMultiplatform) apply false
        // ...
      }
      
    4. Укажите в файле sharedLogic/build.gradle.kts целевые платформы, которые должен поддерживать общий модуль в этом примере:

      kotlin {
          // There's no need for iOS framework configuration since sharedLogic
          // is not going to be exported as a framework, only 'sharedUI' is.
          iosArm64()
          iosSimulatorArm64()
      
          jvm()
      
          js {
              browser()
          }
      
          @OptIn(ExperimentalWasmDsl::class)
          wasmJs {
              browser()
          }
      }
      
    5. Для Android вместо блока androidTarget {} добавьте конфигурацию androidLibrary {} в блок kotlin {}:

      kotlin {
          // ...
          androidLibrary {
              namespace = "com.jetbrains.greeting.demo.sharedLogic"
              compileSdk = libs.versions.android.compileSdk.get().toInt()
              minSdk = libs.versions.android.minSdk.get().toInt()
      
              compilerOptions {
                  jvmTarget = JvmTarget.JVM_11
              }
          }
      }
      
    6. Добавьте необходимые зависимости для работы со временем в наборы исходного кода common и JavaScript так же, как они объявлены для composeApp:

      kotlin {
          sourceSets {
              commonMain.dependencies {
                  implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.8.0")
              }
              webMain.dependencies {
                  implementation(npm("@js-joda/timezone", "2.22.0"))
              }
          }
      }
      
    7. В главном меню выберите Сборка | Синхронизировать проект с файлами Gradle или нажмите кнопку обновления Gradle в редакторе.

  5. Переместите бизнес-логику, выделенную на первом шаге:

    1. Создайте каталог commonMain/kotlin внутри sharedLogic/src.

    2. Создайте файл CurrentTime.kt внутри commonMain/kotlin.

    3. Переместите функцию currentTimeAt из исходного файла App.kt в CurrentTime.kt.

  6. Сделайте функцию доступной компонуемой функции App() по новому адресу. Для этого объявите зависимость между composeApp и sharedLogic в файле composeApp/build.gradle.kts:

    commonMain.dependencies {
        implementation(projects.sharedLogic)
    }
    
  7. Еще раз выполните Сборка | Синхронизировать проект с файлами Gradle, чтобы применить изменения.

  8. Импортируйте функцию currentTimeAt() в файл composeApp/commonMain/.../App.kt, чтобы исправить код.

  9. Запустите приложение, чтобы убедиться, что новый модуль работает правильно.

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

Создание модуля общего пользовательского интерфейса

Вынесите общий код, реализующий общие элементы пользовательского интерфейса, в модуль sharedUI:

  1. Создайте каталог sharedUI в корне проекта.

  2. В этом каталоге создайте пустой файл build.gradle.kts и каталог src.

  3. Добавьте новый модуль в settings.gradle.kts, добавив эту строку в конец файла:

    include(":sharedUI")
    
  4. Настройте сценарий сборки Gradle для нового модуля:

    1. Если вы еще не сделали этого для модуля sharedLogic, добавьте плагин библиотеки Android-KMP в каталог версий в файле gradle/libs.versions.toml:

      [plugins]
      androidMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }
      
    2. Укажите плагины, необходимые модулю общего пользовательского интерфейса, в файле sharedUI/build.gradle.kts:

      plugins {
         alias(libs.plugins.kotlinMultiplatform)
         alias(libs.plugins.androidMultiplatformLibrary)
         alias(libs.plugins.composeMultiplatform)
         alias(libs.plugins.composeCompiler)
      }
      
    3. Убедитесь, что все эти плагины указаны в файле build.gradle.kts в корневом каталоге:

      plugins {
          alias(libs.plugins.androidMultiplatformLibrary) apply false
          alias(libs.plugins.composeMultiplatform) apply false
          alias(libs.plugins.composeCompiler) apply false
          alias(libs.plugins.kotlinMultiplatform) apply false
          // ...
      }
      
    4. Укажите в блоке kotlin {} целевые платформы, которые должен поддерживать модуль общего пользовательского интерфейса в этом примере:

      kotlin {
          listOf(
              iosArm64(),
              iosSimulatorArm64()
          ).forEach { iosTarget ->
              iosTarget.binaries.framework {
                  // This is the name of the iOS framework you're going
                  // to import in your Swift code.
                  baseName = "sharedUI"
                  isStatic = true
              }
          }
      
          jvm()
      
          js {
              browser()
              binaries.executable()
          }
      
          @OptIn(ExperimentalWasmDsl::class)
          wasmJs {
              browser()
              binaries.executable()
          }
      }
      
    5. Для Android вместо блока androidTarget {} добавьте конфигурацию androidLibrary {} в блок kotlin {}:

      kotlin {
          // ...
          androidLibrary {
              namespace = "com.jetbrains.greeting.demo.sharedUI"
              compileSdk = libs.versions.android.compileSdk.get().toInt()
              minSdk = libs.versions.android.minSdk.get().toInt()
      
              compilerOptions {
                  jvmTarget = JvmTarget.JVM_11
              }
      
              // Enables Compose Multiplatform resources to be used in the Android app
              androidResources {
                  enable = true
              }
          }
      }
      
    6. Добавьте необходимые зависимости для общего пользовательского интерфейса так же, как они объявлены для composeApp:

      kotlin {
          sourceSets {
              commonMain.dependencies { 
                  implementation(projects.sharedLogic)
                  implementation(compose.runtime)
                  implementation(compose.foundation)
                  implementation(compose.material3)
                  implementation(compose.ui)
                  implementation(compose.components.resources)
                  implementation(compose.components.uiToolingPreview)
                  implementation(libs.androidx.lifecycle.viewmodelCompose)
                  implementation(libs.androidx.lifecycle.runtimeCompose)
                  implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.8.0")
              }
          }
      }
      
    7. В главном меню выберите Сборка | Синхронизировать проект с файлами Gradle или нажмите кнопку обновления Gradle в редакторе.

  5. Создайте новый каталог commonMain/kotlin внутри sharedUI/src.

  6. Переместите файлы ресурсов в модуль sharedUI: весь каталог composeApp/commonMain/composeResources нужно перенести в sharedUI/commonMain/composeResources.

  7. Создайте новый файл App.kt в sharedUI/src/commonMain/kotlin directory.

  8. Скопируйте все содержимое исходного файла composeApp/src/commonMain/.../App.kt в новый файл App.kt.

  9. Временно закомментируйте весь код в старом файле App.kt. Это позволит проверить работу модуля общего пользовательского интерфейса, прежде чем полностью удалить старый код.

  10. Новый файл App.kt должен работать как ожидалось, за исключением импортов ресурсов, которые теперь находятся в другом пакете. Повторно импортируйте объект Res и все drawable-ресурсы, указав правильный путь, например:

        import demo.composeapp.generated.resources.mx
    
        import demo.sharedui.generated.resources.mx
    
  11. Чтобы новая компонуемая функция App() стала доступна точкам входа модулей приложения, которые от нее зависят, добавьте зависимость в соответствующие файлы build.gradle.kts:

    kotlin {
        sourceSets {
            commonMain.dependencies {
                implementation(projects.sharedUI)
                // ...
            }
        }
    }
    
  12. Запустите приложения, чтобы убедиться, что новый модуль предоставляет точкам входа приложений общий код пользовательского интерфейса.

  13. Удалите файл composeApp/src/commonMain/.../App.kt.

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

Обновление интеграции с iOS

Поскольку точка входа приложения iOS не собирается как отдельный модуль Gradle, исходный код можно встроить в любой модуль. В этом примере его можно оставить внутри shared:

  1. Переместите каталог composeApp/src/iosMain в каталог shared/src.

  2. Настройте проект Xcode для использования фреймворка, созданного модулем shared:

    1. Выберите пункт меню Файл | Открыть проект в Xcode.

    2. Нажмите проект iosApp в окне инструментов Навигатор проекта, затем выберите вкладку Этапы сборки.

    3. Найдите этап Компиляция фреймворка Kotlin.

    4. Найдите строку, начинающуюся с ./gradlew, и замените composeApp на sharedUi:

      ./gradlew :shared:embedAndSignAppleFrameworkForXcode
      
    5. Обратите внимание, что импорт в файле ContentView.swift должен остаться без изменений, поскольку он соответствует параметру baseName из конфигурации Gradle для целевой платформы iOS, а не фактическому имени модуля. Если вы измените имя фреймворка в файле shared/build.gradle.kts, необходимо соответствующим образом изменить директиву импорта.

  3. Запустите приложение из Xcode или с помощью конфигурации запуска iosApp в IntelliJ IDEA.

21 июля 2026 г.
Дополнительные сведения о структуре мультиплатформенного проектаВыбор конфигурации для проекта Kotlin Multiplatform

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/multiplatform/multiplatform-project-recommended-structure.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API