Kotlin Help

Understand mobile project structure

The purpose of the Kotlin Multiplatform Mobile technology is unifying the development of applications with common logic for Android and iOS platforms. To make this possible, it uses a mobile-specific structure of Kotlin Multiplatform projects. This page describes the structure of a basic cross-platform mobile project. Note that this structure isn’t the only possible way to organize your project; however, we recommend it as a starting point.

To view the complete structure of your mobile multiplatform project, switch the view from Android to Project.

Select the Project view

A basic Kotlin Mobile Multiplatform project consists of three components:

  • Shared module – a Kotlin module that contains common logic for both Android and iOS applications. Builds into an Android library and an iOS framework. Uses Gradle as the build system.

  • Android application – a Kotlin module that builds into the Android application. Uses Gradle as the build system.

  • iOS application – an Xcode project that builds into the iOS application.

Basic Multiplatform Mobile project structure

This is the structure of a Multiplatform Mobile project that you create with a Project Wizard in IntelliJ IDEA or Android Studio. Real-life projects can have more complex structure; we consider these three components essential.

Let’s take a closer look at the basic project and its components.

Root project

The root project is a Gradle project that holds the shared module and the Android application as its subprojects. They are linked together via the Gradle multi-project mechanism.

// settings.gradle.kts include(":shared") include(":androidApp")
// settings.gradle include ':shared' include ':androidApp'

The iOS application is produced from an Xcode project. It’s stored in a separate directory within the root project. Xcode uses its own build system; thus, the iOS application project isn’t connected with other parts of the Multiplatform Mobile project via Gradle. Instead, it uses the shared module as an external artifact – framework. For details on integration between the shared module and the iOS application, see iOS application.

This is a basic structure of a cross-platform mobile project:

Basic Multiplatform Mobile project directories

The root project does not hold source code. You can use it to store global configuration in its build.gradle(.kts) or gradle.properties, for example, add repositories or define global configuration variables.

For more complex projects, you can add more modules into the root project by creating them in the IDE and linking via include declarations in the Gradle settings.

Shared module

Shared module contains the core application logic used in both target platforms: classes, functions, and so on. This is a Kotlin Multiplatform module that compiles into an Android library and an iOS framework. It uses Gradle with the Kotlin Multiplatform plugin applied and has targets for Android and iOS.

plugins { kotlin("multiplatform") version "1.7.0" // .. } kotlin { android() ios() }
plugins { id 'org.jetbrains.kotlin.multiplatform' version '1.7.0' //.. } kotlin { android() ios() }

Source sets

The shared module contains the code that is common for Android and iOS applications. However, to implement the same logic on Android and iOS, you sometimes need to write two platform-specific versions of it. To handle such cases, Kotlin offers the expect/actual mechanism. The source code of the shared module is organized in three source sets accordingly:

  • commonMain stores the code that works on both platforms, including the expect declarations

  • androidMain stores Android-specific parts, including actual implementations

  • iosMain stores iOS-specific parts, including actual implementations

Each source set has its own dependencies. Kotlin standard library is added automatically to all source sets, you don’t need to declare it in the build script.

kotlin { sourceSets { val commonMain by getting val androidMain by getting { dependencies { implementation("androidx.core:core-ktx:1.2.0") } } val iosMain by getting // ... } }
kotlin { sourceSets { commonMain { } androidMain { dependencies { implementation 'androidx.core:core-ktx:1.2.0' } } iosMain { } // ... } }

When you write your code, add the dependencies you need to the corresponding source sets. Read Multiplatform documentation on adding dependencies for more information.

Along with *Main source sets, there are three matching test source sets:

  • commonTest

  • androidTest

  • iosTest

Use them to store unit tests for common and platform-specific source sets accordingly. By default, they have dependencies on Kotlin test library, providing you with means for Kotlin unit testing: annotations, assertion functions and other. You can add dependencies on other test libraries you need.

kotlin { sourceSets { // ... val commonTest by getting { dependencies { implementation(kotlin("test")) } } val androidTest by getting val iosTest by getting } }
kotlin { sourceSets { //... commonTest { dependencies { implementation kotlin('test') } } androidTest { } iosTest { } } }

The main and test source sets described above are default. The Kotlin Multiplatform plugin generates them automatically upon target creation. In your project, you can add more source sets for specific purposes. For more information, see Multiplatform DSL reference.

Android library

The configuration of the Android library produced from the shared module is typical for Android projects. To learn about Android libraries creation, see Create an Android library in the Android developer documentation.

To produce the Android library, a separate Gradle plugin is used in addition to Kotlin Multiplatform:

plugins { // ... id("com.android.library") }
plugins { // ... id 'com.android.library' }

The configuration of Android library is stored in the android {} top-level block of the shared module’s build script:

android { compileSdk = 29 sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") defaultConfig { minSdk = 24 targetSdk = 29 } }
android { compileSdk 29 sourceSets.main.manifest.srcFile 'src/androidMain/AndroidManifest.xml' defaultConfig { minSdk 24 targetSdk 29 } }

It’s typical for any Android project. You can edit it to suit your needs. To learn more, see the Android developer documentation.

iOS framework

For using in iOS applications, the shared module compiles into a framework – a kind of hierarchical directory with shared resources used on the Apple platforms. This framework connects to the Xcode project that builds into an iOS application.

The framework is produced via the Kotlin/Native compiler. The framework configuration is stored in the ios {} block of the build script within kotlin {}. It defines the output type framework and the string identifier baseName that is used to form the name of the output artifact. Its default value matches the Gradle module name. For a real project, it’s likely that you’ll need a more complex configuration of the framework production. For details, see Multiplatform documentation.

kotlin { // ... ios { binaries { framework { baseName = "shared" } } } }
kotlin { // ... ios { binaries { framework { baseName = 'shared' } } } }

Additionally, there is a Gradle task embedAndSignAppleFrameworkForXcode, that exposes the framework to the Xcode project the iOS application is built from. It uses the iOS application's project configuration to define the build mode (debug or release) and provide the appropriate framework version to the specified location.

The task is built into the multiplatform plugin. It executes upon each build of the Xcode project to provide the latest version of the framework for the iOS application. For details, see iOS application.

Android application

The Android application part of a Multiplatform Mobile project is a typical Android application written in Kotlin. In a basic cross-platform mobile project, it uses two Gradle plugins:

  • Kotlin Android

  • Android Application

plugins { id("com.android.application") kotlin("android") }
plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' }

To access the shared module code, the Android application uses it as a project dependency.

dependencies { implementation(project(":shared")) //.. }
dependencies { implementation project(':shared') //.. }

Besides this dependency, the Android application uses the Kotlin standard library (which is added automatically) and some common Android dependencies:

dependencies { //.. implementation("androidx.core:core-ktx:1.2.0") implementation("androidx.appcompat:appcompat:1.1.0") implementation("androidx.constraintlayout:constraintlayout:1.1.3") }
dependencies { //.. implementation 'androidx.core:core-ktx:1.2.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' }

Add your project’s Android-specific dependencies to this block. The build configuration of the Android application is located in the android {} top-level block of the build script:

android { compileSdk = 29 defaultConfig { applicationId = "org.example.androidApp" minSdk = 24 targetSdk = 29 versionCode = 1 versionName = "1.0" } buildTypes { getByName("release") { isMinifyEnabled = false } } }
android { compileSdk 29 defaultConfig { applicationId 'org.example.androidApp' minSdk 24 targetSdk 29 versionCode 1 versionName '1.0' } buildTypes { 'release' { minifyEnabled false } } }

It’s typical for any Android project. You can edit it to suit your needs. To learn more, see the Android developer documentation.

iOS application

The iOS application is produced from an Xcode project generated automatically by the New Project wizard. It resides in a separate directory within the root project.

Basic Kotlin Multiplatform Xcode project

For each build of the iOS application, the project obtains the latest version of the framework. To do this, it uses a Run Script build phase that executes the embedAndSignAppleFrameworkForXcode Gradle task from the shared module. This task generates the .framework with the required configuration, depending on the Xcode environment settings, and puts the artifact into the DerivedData Xcode directory.

  • If you have a custom name for the Apple framework, use embedAndSign<Custom-name>AppleFrameworkForXcode as the name for this Gradle task.

  • If you have a custom build configuration that is different from the default Debug or Release, on the Build Settings tab, add the KOTLIN_FRAMEWORK_BUILD_TYPE setting under User-Defined and set it to Debug or Release.

Execution of embedAndSignAppleFrameworkForXcode in the Xcode project settings

To embed framework into the application and make the declarations from the shared module available in the source code of the iOS application, the following build settings should be configured properly:

  1. Other Linker flags under the Linking section:

    $(inherited) -framework shared
    Configuring Other linker flags in the Xcode project settings
  2. Framework Search Paths under the Search Paths section:

    Configuring Framework Search Paths in the Xcode project settings

In other aspects, the Xcode part of a cross-platform mobile project is a typical iOS application project. To learn more about creating iOS application, see the Xcode documentation.

Last modified: 01 July 2022