[//]: # (title: Choosing a configuration for your Kotlin Multiplatform project)

When you add Kotlin Multiplatform to an existing project or start a new one, there are different ways to structure your
code. Typically, you create one or more Kotlin Multiplatform shared modules and use them from your Android and iOS apps.

To choose the best approach for your specific case, consider the following questions:

* [How do you consume an iOS framework generated by a Kotlin Multiplatform module from the iOS app?](#connect-a-kotlin-multiplatform-module-to-an-ios-app)
  Do you integrate it directly, through CocoaPods, or by using the Swift package manager (SPM)?
* [Do you have one or several Kotlin Multiplatform shared modules?](#module-configurations)
  What should be an umbrella module for several shared modules?
* [Do you store all of the code in a monorepo or in different repositories?](#repository-configurations)
* [Do you consume a Kotlin Multiplatform module framework as a local or remote dependency?](#code-sharing-workflow)

Answering these questions will help you pick the best configuration for your project.

## Connect a Kotlin Multiplatform module to an iOS app

To use a Kotlin Multiplatform shared module from an iOS app, you first need to generate
an [iOS framework](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WhatAreFrameworks.html)
from this shared module. Then, you should add it as a dependency to the iOS project.

Generally, there are two options with different implementations:

* Local dependency. Kotlin build is directly interacting with an iOS build.
* Remote dependency. Kotlin build produces an iOS framework which you then connect to an iOS project using a package manager.

To review all available options for iOS integration, see [](multiplatform-ios-integration-overview.md).

## Module configurations

There are two module configuration options that you can use in Kotlin Multiplatform projects: single module or several
shared modules.

### Single shared module

The simplest module configuration contains only a single shared Kotlin Multiplatform module in the project:

![Single shared module](single-shared-module.svg){width=700}

The Android app can depend on the Kotlin Multiplatform shared module as a regular Kotlin module. However, iOS can't use
Kotlin directly, so the iOS app must depend on the iOS framework generated by the Kotlin Multiplatform module.

<table>
  <tr>
     <th>Pros</th>
     <th>Cons</th>
  </tr>
  <tr>
  <td>
    <list>
       <li>A simple design with just a single module reduces cognitive load. You don't need to think about where to put your functionality or how to split it logically into parts.</li>
       <li>Works great as a starting point.</li>
</list>
</td>
<td>
<list>
  <li>Compilation time increases as the shared module grows.</li>
  <li>This design doesn't allow having separate features or having dependencies only on the features the app needs.</li>
</list>
</td>
</tr>
</table>

### Several shared modules

As your shared module grows, it's a good idea to break it into feature modules.
This helps you avoid the scalability issues related to having just one module.

The Android app can depend on all feature modules directly, or only on some of them if necessary.

The iOS app can depend on one framework generated by the Kotlin Multiplatform module. When you use several
modules, you need to add an extra module depending on all of the modules you're using, called an _umbrella module_,
and then you need to configure a framework containing all of the modules, called an _umbrella framework_.

> The umbrella framework bundle contains all of the project's shared modules and is imported into the iOS app.
>
{style="tip"}

<table>
  <tr>
     <th>Pros</th>
     <th>Cons</th>
  </tr>
  <tr>
  <td>
    <list>
       <li>Separation of concerns for shared code.</li>
       <li>Better scalability.</li>
       </list>
</td>
<td>
<list>
  <li>More complicated setup, including the umbrella framework setup.</li>
 <li>More involved dependency management across modules.</li>
</list>
</td>
</tr>
</table>

To set up an umbrella module, you add a separate module that depends on all feature modules and generate a framework from this module:

![Umbrella framework](umbrella-framework.svg){width=700}

The Android app can either depend on the umbrella module for consistency or on separate feature modules. An umbrella
module often contains useful utility functions and dependency injection setup code.

You can export only some of the modules to the umbrella framework, typically when the framework artifact is consumed
as a remote dependency. The main reason for this is to keep  the size of the final artifact down
by making sure auto-generated code is excluded.

A known constraint of the umbrella framework approach is that the iOS app can't use only some of the feature modules –
it automatically consumes all of them. For possible improvements to this functionality,
describe your case in [KT-42247](https://youtrack.jetbrains.com/issue/KT-42247)
and [KT-42250](https://youtrack.jetbrains.com/issue/KT-42250).

> When you see that an iOS app depends on an umbrella module in the examples below, it means that it also depends on the
> umbrella framework generated from this module.
>
{style="tip"}

#### Why do you need an umbrella framework? {initial-collapse-state="collapsed" collapsible="true"}

While it's possible to include several frameworks generated from different Kotlin Multiplatform shared modules in your
iOS app, we do not recommend this approach. When a Kotlin Multiplatform module is compiled into a framework, the
resulting framework includes all of its dependencies. Whenever two or more modules use the same dependency and
are exposed to iOS as separate frameworks, the Kotlin/Native compiler duplicates the dependencies.

This duplication causes a number of issues. First, the iOS app size is unnecessarily inflated. Second, a
dependency's code structure is incompatible with the duplicated dependency's code structure. This creates a problem when
trying to integrate two modules with the same dependencies within the iOS application. For instance, any state passed by
different modules through the same dependency won't be connected. This can lead to unexpected behavior and bugs.
See the [TouchLab documentation](https://touchlab.co/multiple-kotlin-frameworks-in-application/) for more details on the
exact limitations.

Kotlin doesn't produce common framework dependencies because otherwise there would be duplication, and any Kotlin binary you add to your
app needs to be as small as possible. Including the whole Kotlin runtime and all of the code from all dependencies is
wasteful. The Kotlin compiler is able to trim the binary to exactly what it needs for a particular build. However, it
doesn't know what other builds might need, so trying to share dependencies is unfeasible. We're exploring various
options to minimize the effects of this issue.

The solution to this problem is to use an umbrella framework. It prevents the bloating of the iOS app with duplicated
dependencies, helps optimize the resulting artifact, and eliminates the frustrations caused by incompatibilities between
dependencies.

## Repository configurations

There are a number of repository configuration options that you can use in new and existing Kotlin Multiplatform
projects, using one repository or a combination of several repositories.

### Monorepo: everything in one repository

A common repository configuration is called a monorepo configuration. This approach is used in Kotlin Multiplatform
samples and tutorials. In this case, the repository contains both Android and iOS apps, as well as the shared module or
several modules, including the umbrella module:

![Monorepo configuration](monorepo-configuration-1.svg){width=700}

![Monorepo configuration](monorepo-configuration-2.svg){width=700}

Typically, the iOS app consumes the Kotlin Multiplatform shared module as a regular framework by using direct or CocoaPods
integration. See [Connecting Kotlin Multiplatform module to iOS app](#connect-a-kotlin-multiplatform-module-to-an-ios-app)
for more details and links to tutorials.

If the repository is under version control, the apps and the shared module have the same version.

<table>
  <tr>
     <th>Pros</th>
     <th>Cons</th>
  </tr>
  <tr>
  <td>
    <list>
       <li>Easy to set up with the help of wizards.</li>
       <li>iOS developers can easily work with Kotlin Multiplatform code since all of the code is located in the same repository.</li>
</list>
</td>
<td>
<list>
  <li>iOS developers need to set up and configure unfamiliar tools.</li>
<li>This approach often doesn't work for existing apps already stored in different repositories.</li>
</list>
</td>
</tr>
</table>

When the existing Android and iOS apps are already stored in different repositories, you can add the Kotlin
Multiplatform part to an Android repository or to a separate repository, instead of merging them.

### Two repositories: Android + shared | iOS

Another project configuration is having two repositories. In this case, the Kotlin Multiplatform repository contains
both the Android app and the shared module, including the umbrella module, while the Xcode project contains the iOS app:

![Two repository configuration](two-repositories.svg){width=700}

The Android and iOS apps can be versioned separately, and the shared module is versioned along with the Android app.

### Three repositories: Android | iOS | shared

One more option is having a separate repository for the Kotlin Multiplatform modules. In this case, the Android and iOS
apps are stored in separate repositories, and the project's shared code can contain multiple feature modules and the
umbrella module for iOS:

![Three repository configuration](three-repositories.svg){width=700}

Each project can be versioned separately. Kotlin Multiplatform modules must also be versioned and published for the
Android or JVM platforms. You can either publish feature modules independently or publish only the umbrella module and
make the Android app depend on it.

Publishing Android artifacts separately can present additional complexity for Android developers
compared to project scenarios where the Kotlin Multiplatform modules are part of the Android project.

When both Android and iOS teams consume the same versioned artifacts, they operate in version parity.
From a team perspective, this avoids the impression that the shared Kotlin Multiplatform code is "owned" by the
Android developers. For large projects that already publish versioned internal Kotlin and Swift packages for feature
development, publishing the shared Kotlin artifacts becomes a part of the existing workflow.

### Many repositories: Android | iOS | multiple libraries

When functionality should be shared between multiple apps on multiple platforms, you might prefer having many
repositories with Kotlin Multiplatform code. For example, you can store a logging library, which is common for the whole
product, in a separate repository with its own versioning.

In this case, you have multiple Kotlin Multiplatform library repositories. If several iOS apps use different subsets of
"library projects", each app can have an additional repository containing the umbrella module with the necessary
dependencies on the library projects:

![Many repository configuration](many-repositories.svg){width=700}

Here, each library must be versioned and published for the Android or JVM platforms, as well. The apps and each library
can be versioned separately.

## Code sharing workflow

The iOS app can consume a framework generated from the Kotlin Multiplatform shared modules as a _local_ or _remote_
dependency. You can use a local dependency by providing a local path to the framework in the iOS build. In this case,
you don't need to publish the framework. Alternatively, you can publish an artifact with the framework somewhere and
make the iOS app consume it as a remote dependency, like any other third-party dependency.

### Local: source distribution

Local distribution is where the iOS app consumes a Kotlin Multiplatform module framework without the
need for publishing. The iOS app can either integrate the framework directly or by using CocoaPods.

This workflow is typically used when both Android and iOS team members want to edit the shared Kotlin Multiplatform
code. The iOS developers need to install IntelliJ IDEA or Android Studio and have a basic knowledge of Kotlin and Gradle.

In the local distribution scheme, the iOS app build triggers the generation of the iOS framework. This means that iOS
developers can observe their changes to Kotlin Multiplatform code right away:

![Local source distribution](local-source-distribution.svg){width=700}

This scenario is typically used in two cases. First, it can be used in monorepo project configurations as the default
workflow, without the need to publish artifacts. Second, it can be used for local development, in addition to the remote
workflow. See [Setting up a local dependency for local development](#setting-up-a-local-dependency-for-local-development) for more
details.

This workflow is most effective when all of the team members are ready to edit code in the whole project. It includes both
Android and iOS parts after making changes to the common parts. Ideally, every team member can have IntelliJ IDEA/Android Studio and
Xcode installed to open and run both Android and iOS apps after making changes to the common code.

<table>
  <tr>
     <th>Pros</th>
     <th>Cons</th>
  </tr>
  <tr>
  <td>
    <list>
       <li>Both Android and iOS team members can easily edit Kotlin Multiplatform code, ensuring that creating and maintaining shared code is a shared responsibility. This helps prevent teams' isolation and encourages collaboration.</li>
       <li>This approach doesn't require separate versioning and publishing of the shared code.</li>
       <li>The development workflow is quicker, as iOS team members don't have to wait for artifacts to be created and published.</li>
   </list>
</td>
<td>
  <list>
    <li>Team members need to set up a full development environment on their machines.</li>
    <li>iOS developers have to learn how to use IntelliJ IDEA or Android Studio and Gradle.</li>
    <li>Managing changes becomes difficult as more code is shared and the team grows.</li>
  </list>
</td>
</tr>
</table>

### Remote: artifact distribution

Remote distribution means that the framework artifact is published using Swift Package Manager or as a CocoaPod and consumed
by the iOS app. The Android app may consume the binary dependency either locally or remotely.

Remote distribution is often used to gradually introduce the technology to existing projects. It doesn't significantly
change the workflow and build processes for iOS developers. Teams with two or more repositories mainly use remote
distribution to store project code.

As a start, you may want to use [KMMBridge](https://touchlab.co/trykmmbridge) – a set of build tools that greatly simplifies the
remote distribution workflow. Alternatively, you can always set up a similar workflow on your own:

![Remote artifact distribution](remote-artifact-distribution.svg){width=700}

<table>
  <tr>
     <th>Pros</th>
     <th>Cons</th>
  </tr>
  <tr>
  <td>Non-participating iOS team members don't have to code in Kotlin or learn how to use tools like IntelliJ IDEA/Android Studio or Gradle.
      This lowers the barrier of entry for the team significantly.</td>
<td>
  <list>
    <li>Slower workflow for iOS developers, as the process of editing and building the shared code involves publishing and versioning.</li>
   <li>Debugging shared Kotlin code is difficult on iOS.</li>
   <li>The likelihood of iOS team members contributing to shared code significantly decreases.</li>
   <li>The maintenance of the shared code rests entirely on participating team members.</li>
  </list>
</td>
</tr>
</table>

#### Setting up a local dependency for local development

Many teams choose remote distribution workflows when adopting Kotlin Multiplatform technology to keep the development
process the same for iOS developers. However, it's hard for them to change Kotlin Multiplatform code in this workflow.
We recommend setting up an additional "local development" workflow with a local dependency on a framework
generated from the Kotlin Multiplatform module.

When developers add new functionality, they switch to consuming the Kotlin Multiplatform module as a local dependency.
That allows making changes to common Kotlin code, immediately observing the behavior from iOS, and debugging Kotlin
code. When the functionality is ready, they can switch back to the remote dependency and publish their changes
accordingly. First, they publish changes to the shared modules, and only after that do they make changes to the apps.

For remote distribution workflows, use Swift Package Manager. For local distribution workflow, integrate
the framework directly.

<!-- This tutorial [TODO] describes how to switch workflows by choosing the corresponding scheme in Xcode:
[TODO screenshot] -->

If you use CocoaPods, you can alternatively use CocoaPods for local distribution workflow. You switch between them by
changing the environmental variable as described in the [TouchLab documentation](https://touchlab.co/kmmbridgecocoapodslocal).
