Kotlin Multiplatform Mobile Docs Help

Add dependencies to KMM modules

Every application requires a set of libraries in order to operate successfully. A KMM application can depend on multiplatform libraries that work on both iOS and Android, and it can depend on platform-specific iOS and Android libraries.

Here you can learn how to add:

Multiplatform libraries

You can add dependencies on libraries that have adopted Kotlin Multiplatform technology, such as kotlinx.coroutines and SQLDelight. The authors of these libraries usually provide guides for adding their dependencies to your project.

This page covers basic dependency use cases:

Learn more about configuring dependencies.

Check out this community-maintained list of Kotlin Multiplatform libraries.

Dependency on the Kotlin standard library

The Kotlin standard library is added automatically to all multiplatform projects, you don’t have to do anything manually.

Dependency on a library shared for all source sets

If you want to use a library from all source sets, you can add it only to the common source set. The Kotlin Multiplatform Mobile plugin will add the corresponding parts to any other source sets automatically.

kotlin { sourceSets { commonMain { dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2' } } androidMain { dependencies { //dependency to platform part of kotlinx.coroutines will be added automatically } } } }
kotlin { sourceSets["commonMain"].dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2") } sourceSets["androidMain"].dependencies { //dependency to platform part of kotlinx.coroutines will be added automatically } }

Dependency on a library used in specific source sets

If you want to use a multiplatform library just for specific source sets, you can add it exclusively to them. The specified library declarations will then be available only in those source sets.

kotlin { sourceSets { commonMain { dependencies { // kotlinx.coroutines will be available in all source sets implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2' } } androidMain { dependencies { } } iosMain { dependencies { // SQLDelight will be available only in the iOS source set, but not in Android or common implementation 'com.squareup.sqldelight:native-driver:1.4.1' } } } }
kotlin { sourceSets["commonMain"].dependencies { //kotlinx.coroutines will be available in all source sets implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2") } sourceSets["androidMain"].dependencies { } sourceSets["iosX64Main"].dependencies { //SQLDelight will be available only in the iOS source set, but not in Android or common implementation("com.squareup.sqldelight:native-driver:1.4.1) } }

Dependency on another multiplatform project

You can connect one multiplatform project to another as a dependency. To do this, simply add a project dependency to the source set that needs it. If you want to use a dependency in all source sets, add it to the common one. In this case, other source sets will get their versions automatically.

kotlin { sourceSets { commonMain { dependencies { implementation project(':some-other-multiplatform-module') } } androidMain { dependencies { //platform part of :some-other-multiplatform-module will be added automatically } } } }
kotlin { sourceSets["commonMain"].dependencies { implementation(project(":some-other-multiplatform-module")) } sourceSets["androidMain"].dependencies { //platform part of :some-other-multiplatform-module will be added automatically } }

iOS dependencies

Apple SDK dependencies (such as Foundation or Core Bluetooth) are available as a set of prebuilt libraries in Kotlin Multiplatform Mobile projects. They do not require any additional configuration.

You can also reuse other libraries and frameworks from the iOS ecosystem in your iOS source sets. Kotlin supports interoperability with Objective-C dependencies and Swift dependencies if their APIs are exported to Objective-C with the @objc attribute. Pure Swift dependencies are not yet supported.

Integration with the CocoaPods dependency manager is also supported with the same limitation – you cannot use pure Swift pods.

We recommend using CocoaPods to handle iOS dependencies in Kotlin Multiplatform Mobile (KMM) projects. Manage dependencies manually only if you want to tune the interop process specifically or if you have some other strong reason to do so.

With CocoaPods

  1. Perform initial CocoaPods integration setup

  2. Add a dependency on a Pod library from the CocoaPods repository that you want to use by including pod() in the build script of your project.

    kotlin { cocoapods { //.. pod('AFNetworking') { version = '~> 4.0.1' } } }
    kotlin { cocoapods { //.. pod("AFNetworking") { version = "~> 4.0.1" } } }
  3. Re-import the project.

To use the dependency in your Kotlin code, import the package cocoapods.<library-name>. In the example above, that would be:

import cocoapods.AFNetworking.*

Learn more about CocoaPods integration.

Without CocoaPods

If you don’t want to use CocoaPods, you can use the cinterop tool to create Kotlin bindings for Objective-C or Swift declarations. This will allow you to call them from Kotlin code. To do this:

  1. Download your dependency.

  2. Build it to get its binaries.

  3. Create a special .def file that describes this dependency to cinterop.

  4. Adjust your build script to generate bindings during the build.

The steps differ a bit for libraries and frameworks, but the idea remains the same.

Add a library without CocoaPods

  1. Download the library source code and place it somewhere where you can reference it from your project.

  2. Build a library (library authors usually provide a guide on how to do this) and get a path to the binaries.

  3. In your project, create a .def file, for example DateTools.def.

  4. Add a first string to this file: language = Objective-C. If you want to use a pure C dependency, omit the language property.

  5. Provide values for two mandatory properties:

    • headers describes which headers will be processed by cinterop.

    • package sets the name of the package these declarations should be put into.

    For example:

    headers = DateTools.h package = DateTools
  6. Add information about interoperability with this library to the build script:

    • Pass the path to the .def file. This path can be omitted if your .def file has the same name as cinterop and is placed in the src/nativeInterop/cinterop/ directory.

    • Tell cinterop where to look for header files using the includeDirs option.

    • Configure linking to library binaries.

    kotlin { iosX64 { compilations.main { cinterops { DateTools { // Path to .def file defFile("src/nativeInterop/cinterop/DateTools.def") // Directories for header search (an analogue of the -I<path> compiler option) includeDirs("include/this/directory", "path/to/another/directory") } anotherInterop { /* ... */ } } } binaries.all { // Linker options required to link to the library. linkerOpts "-L/path/to/library/binaries", "-lbinaryname" } } }
    kotlin { iosX64() { compilations.getByName("main") { val DateTools by cinterops.creating { // Path to .def file defFile("src/nativeInterop/cinterop/DateTools.def") // Directories for header search (an analogue of the -I<path> compiler option) includeDirs("include/this/directory", "path/to/another/directory") } val anotherInterop by cinterops.creating { /* ... */ } } binaries.all { // Linker options required to link to the library. linkerOpts("-L/path/to/library/binaries", "-lbinaryname") } } }
  7. Build the project.

Now you can use this dependency in your Kotlin code. To do that, import the package you’ve set up in the package property in the .def file. For the example above, this will be:

import DateTools.*

Add a framework without CocoaPods

  1. Download the framework source code and place it somewhere that you can reference it from your project.

  2. Build the framework (framework authors usually provide a guide on how to do this) and get a path to the binaries.

  3. In your project, create a .def file, for example MyFramework.def.

  4. Add the first string to this file: language = Objective-C. If you want to use a pure C dependency, omit the language property.

  5. Provide values for these two mandatory properties:

    • modules – the name of the framework that should be processed by the cinterop.

    • package – the name of the package these declarations should be put into. For example:

    modules = MyFramework package = MyFramework
  6. Add information about interoperability with the framework to the build script:

    • Pass the path to the .def file. This path can be omitted if your .def file has the same name as the cinterop and is placed in the src/nativeInterop/cinterop/ directory.

    • Pass the framework name to the compiler and linker using the -framework option. Pass the path to the framework sources and binaries to the compiler and linker using the -F option.

    kotlin { iosX64 { compilations.main { cinterops { DateTools { // Path to .def file defFile("src/nativeInterop/cinterop/MyFramework.def") compilerOpts("-framework", "MyFramework", "-F/path/to/framework/") } anotherInterop { /* ... */ } } } binaries.all { // Tell the linker where the framework is located. linkerOpts("-framework", "MyFramework", "-F/path/to/framework/") } } }
    kotlin { iosX64() { compilations.getByName("main") { val DateTools by cinterops.creating { // Path to .def file defFile("src/nativeInterop/cinterop/DateTools.def") compilerOpts("-framework", "MyFramework", "-F/path/to/framework/" } val anotherInterop by cinterops.creating { /* ... */ } } binaries.all { // Tell the linker where the framework is located. linkerOpts("-framework", "MyFramework", "-F/path/to/framework/") } } }
  7. Build the project.

Now you can use this dependency in your Kotlin code. To do this, import the package you’ve set up in the package property in the .def file. For the example above, this will be:

import MyFramework.*

Learn more about Objective-C and Swift interop and configuring cinterop from Gradle.

Workaround to enable IDE support for the shared iOS source set

Due to a known issue, you won't be able to use IDE features, such as code completion and highlighting, for the shared iOS source set in a multiplatform project with hierarchical structure support if your project depends on:

  • Multiplatform libraries that don't support the hierarchical structure.

  • Third-party iOS libraries, with the exception of platform libraries supported out of the box.

This issue applies only to the shared iOS source set. The IDE will correctly support the rest of the code.

To enable IDE support in these cases, you can work around the issue by adding the following code to build.gradle.(kts) in the shared directory of your project:

def iosTarget if (System.getenv("SDK_NAME")?.startsWith("iphoneos")) { iosTarget = kotlin.&iosArm64 } else { iosTarget = kotlin.&iosX64 }
val iosTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget = if (System.getenv("SDK_NAME")?.startsWith("iphoneos") == true) ::iosArm64 else ::iosX64 iosTarget("ios")

In this code sample, the configuration of iOS targets depends on the environment variable SDK_NAME, which is managed by Xcode. For each build, you'll have only one iOS target, named ios, that uses the iosMain source set. There will be no hierarchy of the iosMain, iosArm64, and iosX64 source sets.

Android dependencies

The workflow for adding Android-specific dependencies to a KMM module is the same as it is for pure Android projects: add a line to your Gradle build script declaring the dependency you need and import the project. You’ll then be able to use this dependency in your Kotlin code.

We recommend adding Android dependencies to KMM projects by adding them to a specific Android source set:

sourceSets { androidMain { dependencies { implementation 'com.example.android:app-magic:12.3' } } }
sourceSets["androidMain"].dependencies { implementation("com.example.android:app-magic:12.3") }

Moving what was a top-level dependency in an Android project to a specific source set in a KMM project might be difficult if the top-level dependency had a non-trivial configuration name. For example, to move а debugImplementation dependency from the top level of an Android project, you’ll need to add an implementation dependency to the source set named androidDebug. To minimize the effort you have to put in to deal with migration problems like this, you can add a dependencies block inside the android block:

android { ... dependencies { implementation 'com.example.android:app-magic:12.3' } }
android { ... dependencies { implementation("com.example.android:app-magic:12.3") } }

Dependencies declared here will be treated exactly the same as dependencies from the top-level block, but declaring them this way will also separate Android dependencies visually in your build script and make it less confusing.

Putting dependencies into a standalone dependencies block at the end of the script, in a way that is idiomatic to Android projects, is also supported. However, we strongly recommend against doing this because configuring a build script with Android dependencies in the top-level block and other target dependencies in each source set is likely to cause confusion.

Learn more about adding dependencies in Android documentation.

Last modified: 29 March 2021