proofread (#2062)
This commit is contained in:
committed by
Nikolay Igotti
parent
ab1cf56ecd
commit
7c21913990
+53
-53
@@ -1,40 +1,40 @@
|
|||||||
# Kotlin/Native in multiplatform projects
|
# Kotlin/Native in multiplatform projects
|
||||||
|
|
||||||
While Kotlin/Native could be used as the only Kotlin compiler in the project, it is pretty common to combine
|
While Kotlin/Native can be used as the only Kotlin compiler in a project, it is pretty common to combine
|
||||||
Kotlin/Native with other Kotlin backends, such as Kotlin/JVM (for JVM or Android targets) or Kotlin/JS
|
Kotlin/Native with other Kotlin backends, such as Kotlin/JVM (for JVM or Android targets) or Kotlin/JS
|
||||||
(for web and Node.js applications). This document describes recommended approaches and the best practices for such scenarios.
|
(for web and Node.js applications). This document describes the recommended approaches and best practices for such scenarios.
|
||||||
|
|
||||||
Kotlin as a language provides a notion of expect/actual declarations, and Gradle in its turn
|
Kotlin as a language provides a notion of expect/actual declarations, and Gradle in turn
|
||||||
augments it with the notion of multiplatform projects (aka MPP). Those two, combined together, provide a flexible
|
augments it with the notion of multiplatform projects (aka MPP). These two, combined together, provide a flexible
|
||||||
standartized [mechanism of multiplatform development](https://kotlinlang.org/docs/reference/multiplatform.html)
|
standardized [mechanism of multiplatform development](https://kotlinlang.org/docs/reference/multiplatform.html)
|
||||||
across various Kotlin flavours.
|
across various Kotlin flavors.
|
||||||
|
|
||||||
Code, common amongst multiple platforms can be placed in common modules, while platform-specific code could be placed
|
Code, common amongst multiple platforms can be placed in common modules, while platform-specific code can be placed
|
||||||
into platform-specific modules, and expect/actual declarations can bind them together in developer-friendly way.
|
into platform-specific modules, and expect/actual declarations can bind them together in a developer-friendly way.
|
||||||
|
|
||||||
Below one can find a step-by-step tutorial of creating a Kotlin multiplatform application for Android and iOS.
|
Below you will find a step-by-step tutorial for creating a Kotlin multiplatform application for Android and iOS.
|
||||||
|
|
||||||
## Creating multiplatform Android/iOS application with Kotlin
|
## Creating multiplatform Android/iOS application with Kotlin
|
||||||
|
|
||||||
To create an MPP application one has to start with clear understanding which parts of an application is common for a different
|
To create an MPP application you have to start with clear understanding of which parts of an application are common for different
|
||||||
targets, and which ones are specific, and organize module structure accordingly. For shared Kotlin code the common
|
targets, and which are specific, and then organize the module structure accordingly. For shared Kotlin code the common
|
||||||
ground consist of the Kotlin's standard library, which does include basic data structures and computational primitives,
|
ground consists of Kotlin's standard library, which does include basic data structures and computational primitives,
|
||||||
along with expect classes with platform-specific implementation. Most frequently, such code consists of GUI,
|
along with some expected classes with platform-specific implementation. Most frequently, such code consists of a GUI,
|
||||||
input-output, cryptography and other APIs, available on the particular platform.
|
input-output, cryptography, and other APIs, available on the particular platform.
|
||||||
|
|
||||||
In this tutorial, the multiplatform application will include three parts:
|
In this tutorial, the multiplatform application will include three parts:
|
||||||
|
|
||||||
* An **Android application** represented by a separate Android Studio project written in Kotlin.
|
* An **Android application** represented by a separate Android Studio project written in Kotlin.
|
||||||
* An **iOS application** represented by a separate Xcode project, written in Swift.
|
* An **iOS application** represented by a separate Xcode project, written in Swift.
|
||||||
* A **multiplatform library** containing a business logic of the application and used by both Android and iOS applications.
|
* A **multiplatform library** containing the business logic of the application and used by both Android and iOS applications.
|
||||||
This library can contain both platform-dependent and platform-independent code and is compiled into a `jar`-library
|
This library can contain both platform-dependent and platform-independent code and is compiled into a `jar`-library
|
||||||
for Android and in a `Framework` for iOS by Gradle.
|
for Android and in a `Framework` for iOS by Gradle.
|
||||||
|
|
||||||
So, the multiplatform library will include three subprojects:
|
So, the multiplatform library will include three subprojects:
|
||||||
|
|
||||||
* `common` - contains a common logic for both applications;
|
* `common` - contains common logic for both applications
|
||||||
* `ios` - contains an iOS-specific code;
|
* `ios` - contains iOS-specific code
|
||||||
* `android` - contains an Android-specific code.
|
* `android` - contains Android-specific code
|
||||||
|
|
||||||
### 1. Preparing a workspace
|
### 1. Preparing a workspace
|
||||||
|
|
||||||
@@ -49,12 +49,12 @@ generate different greetings on different platforms. Create the following direct
|
|||||||
├── android/
|
├── android/
|
||||||
└── ios/
|
└── ios/
|
||||||
|
|
||||||
As said above, [Gradle](https://gradle.org/) is the main build system for Kotlin thus our project will use it.
|
As said above, [Gradle](https://gradle.org/) is the main build system for Kotlin so our project will use it.
|
||||||
|
|
||||||
To install Gradle refer to [this instruction](https://gradle.org/install/). Despite the fact that you can use the local
|
To install Gradle refer to [these instructions](https://gradle.org/install/). Despite being able to use the local
|
||||||
Gradle installation for building the project, it's recommended to use the
|
Gradle installation for building a project, it's recommended to use the
|
||||||
[Gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html) instead. To create the wrapper, install
|
[Gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html) instead. To create the wrapper, install
|
||||||
Gradle as described above and execute `gradle wrapper` in the root directory of the project. After that you can
|
Gradle as described above, and execute `gradle wrapper` in the root directory of the project. After that you can
|
||||||
use `./gradlew` to run the build instead of using your local Gradle installation.
|
use `./gradlew` to run the build instead of using your local Gradle installation.
|
||||||
|
|
||||||
Once the wrapper is created we need to describe the project structure in Gradle terms. To do this, create
|
Once the wrapper is created we need to describe the project structure in Gradle terms. To do this, create
|
||||||
@@ -69,20 +69,20 @@ Here we declare all subprojects for our `greeting` multiplatform library. All ot
|
|||||||
in the project also must be declared here.
|
in the project also must be declared here.
|
||||||
|
|
||||||
Note that both iOS and Android applications are not included in the root Gradle build. They are represented by
|
Note that both iOS and Android applications are not included in the root Gradle build. They are represented by
|
||||||
independent builds which are managed by specific IDEs (Android Studio and Xcode). Such an approach makes work with
|
independent builds which are managed by specific IDEs (Android Studio and Xcode). Such an approach makes working with
|
||||||
these builds easier from these IDEs.
|
these builds easier from the IDEs.
|
||||||
|
|
||||||
As for IDE for other parts of the project, [IntelliJ IDEA](https://www.jetbrains.com/idea/) is recommended to be used.
|
As for an IDE for other parts of the project, [IntelliJ IDEA](https://www.jetbrains.com/idea/) is recommended.
|
||||||
|
|
||||||
> Note: Kotlin/Native is not supported by IntelliJ IDEA so the only IDE to develop Kotlin/Native subprojects is
|
> Note: Kotlin/Native is not supported by IntelliJ IDEA so the only IDE to develop Kotlin/Native sub-projects is
|
||||||
[CLion](https://www.jetbrains.com/clion/). But at the moment CLion has no Gradle integration. As a workaround you can
|
[CLion](https://www.jetbrains.com/clion/). But at the moment CLion has no Gradle integration. As a workaround you can
|
||||||
create a CLion Cmake project from a Kotlin/Native Gradle one. Just run `./gradlew generateCMake` for this project. It
|
create a CLion Cmake project from a Kotlin/Native Gradle one. Just run `./gradlew generateCMake` for this project. It
|
||||||
will generate all the necessary files which are required. See
|
will generate all the necessary files which are required. See
|
||||||
[this](https://blog.jetbrains.com/kotlin/2017/11/kotlinnative-ide-support-preview/) blog post to learn more about
|
[this](https://blog.jetbrains.com/kotlin/2017/11/kotlinnative-ide-support-preview/) blog post to learn more about
|
||||||
Kotlin/Native support in CLion.
|
Kotlin/Native support in CLion.
|
||||||
|
|
||||||
As the final step create empty `build.gradle` files in the root directory of the project and in all subprojects which are
|
For the final step create the empty `build.gradle` files in the root directory of the project and in all subprojects which are
|
||||||
included in `settings.gradle`. After all these actions the project structure will be the following (files
|
included in `settings.gradle`. After performing all these actions the project structure will be the following (files
|
||||||
generated by the Gradle wrapper are not shown):
|
generated by the Gradle wrapper are not shown):
|
||||||
|
|
||||||
application/
|
application/
|
||||||
@@ -99,12 +99,12 @@ generated by the Gradle wrapper are not shown):
|
|||||||
├── build.gradle
|
├── build.gradle
|
||||||
└── settings.gradle
|
└── settings.gradle
|
||||||
|
|
||||||
Now we have a basic structure of the project and can proceed to implementing of the multiplatform library.
|
Now we have the basic structure of the project and can proceed to implement the multiplatform library.
|
||||||
|
|
||||||
### 2. Multiplatform library
|
### 2. Multiplatform library
|
||||||
|
|
||||||
We need to add buildscript dependencies to be able to use Kotlin plugins for Gradle in our build. Open
|
We need to add buildscript dependencies to be able to use the Kotlin plugins for Gradle in our build. Open
|
||||||
`build.gradle` in the `greeting` directory and put into it the following snippet:
|
the `build.gradle` in the `greeting` directory and put the following snippet into it:
|
||||||
|
|
||||||
// Set up a buildscript dependency on the Kotlin plugin.
|
// Set up a buildscript dependency on the Kotlin plugin.
|
||||||
buildscript {
|
buildscript {
|
||||||
@@ -135,7 +135,7 @@ Now all subprojects of the library can use Kotlin plugins.
|
|||||||
|
|
||||||
#### 2.1 Common subproject
|
#### 2.1 Common subproject
|
||||||
|
|
||||||
The `common` subproject contains a platform-independent code. To build it, add the following snippet in `common/build.gradle`:
|
The `common` subproject contains platform-independent code. To build it, add the following snippet in `common/build.gradle`:
|
||||||
|
|
||||||
apply plugin: 'kotlin-platform-common'
|
apply plugin: 'kotlin-platform-common'
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ The `common` subproject contains a platform-independent code. To build it, add t
|
|||||||
version = 1.0
|
version = 1.0
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Set up compilation dependency on common Kotlin stdlib
|
// Set up a compilation dependency on common Kotlin stdlib
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,14 +164,14 @@ functionality into it:
|
|||||||
fun greeting(): String = "Hello, ${Platform().platform}"
|
fun greeting(): String = "Hello, ${Platform().platform}"
|
||||||
}
|
}
|
||||||
|
|
||||||
Here we create a simple class using `expect`/`actual` paradigm. See details about platform-specific declarations
|
Here we create a simple class using the `expect`/`actual` paradigm. Find details about platform-specific declarations
|
||||||
[here](https://kotlinlang.org/docs/reference/multiplatform.html#platform-specific-declarations).
|
[here](https://kotlinlang.org/docs/reference/multiplatform.html#platform-specific-declarations).
|
||||||
|
|
||||||
#### 2.2 Android subproject
|
#### 2.2 Android subproject
|
||||||
|
|
||||||
The `android` subproject contains platform-dependent implementations of `expect`-declarations we've created in the
|
The `android` subproject contains platform-dependent implementations of the `expect`-declarations we've created in the
|
||||||
`common` project. We compile it into a Java library which an Android Studio project can depend on. The content
|
`common` project. We compile it into a Java library which an Android Studio project can depend on. The content
|
||||||
of `android/build.gradle` will be the following:
|
of the `android/build.gradle` will be the following:
|
||||||
|
|
||||||
apply plugin: 'kotlin-platform-jvm'
|
apply plugin: 'kotlin-platform-jvm'
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ of `android/build.gradle` will be the following:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
As said above this subproject should include actual implementations of the common project's `expect`-declarations.
|
As mentioned above this subproject should include actual implementations of the common project's `expect`-declarations.
|
||||||
Let's write an Android-specific method:
|
Let's write an Android-specific method:
|
||||||
|
|
||||||
// greeting/android/src/main/kotlin/android.kt
|
// greeting/android/src/main/kotlin/android.kt
|
||||||
@@ -202,8 +202,8 @@ Let's write an Android-specific method:
|
|||||||
|
|
||||||
#### 2.3 iOS subproject
|
#### 2.3 iOS subproject
|
||||||
|
|
||||||
This project is compiled into an Objective-C framework using Kotlin/Native compiler. To do this, declare a framework in
|
This project is compiled into an Objective-C framework using the Kotlin/Native compiler. To do this, declare a framework in
|
||||||
`ios/build.gradle` and add an `expectedBy` dependency in the same manner as in the Android project:
|
`ios/build.gradle` and add an `expectedBy` dependency in the same manner as was done in the Android project:
|
||||||
|
|
||||||
apply plugin: 'konan'
|
apply plugin: 'konan'
|
||||||
|
|
||||||
@@ -234,8 +234,8 @@ As well as `android`, this project contains platform-dependent implementations o
|
|||||||
|
|
||||||
### 3. Android application
|
### 3. Android application
|
||||||
|
|
||||||
Now we can create an Android application which will use the library we implemented on the previous step. Open Android
|
Now we can create an Android application which will use the library we implemented in the previous step. Open Android
|
||||||
Studio and create a new project in the `androidApp` directory. Android Studio will generate all necessary files and
|
Studio and create a new project in the `androidApp` directory. Android Studio will generate all the necessary files and
|
||||||
directories.
|
directories.
|
||||||
|
|
||||||
Kotlin/Native requires Gradle 4.7 or higher so you need to make sure that the AS project uses the correct
|
Kotlin/Native requires Gradle 4.7 or higher so you need to make sure that the AS project uses the correct
|
||||||
@@ -243,7 +243,7 @@ Gradle version. To do this, open `androidApp/gradle/gradle-wrapper.properties` a
|
|||||||
property. Upgrade the wrapper if necessary
|
property. Upgrade the wrapper if necessary
|
||||||
(see [Gradle documentation](https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:upgrading_wrapper)).
|
(see [Gradle documentation](https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:upgrading_wrapper)).
|
||||||
|
|
||||||
Now we only need to add a dependency on our library. There are 2 actions we need to do:
|
Now we only need to add a dependency on our library. There are 2 actions we need to take:
|
||||||
|
|
||||||
1. Add dependency on the library. To do this just open `androidApp/app/build.gradle` and add the following snippet in
|
1. Add dependency on the library. To do this just open `androidApp/app/build.gradle` and add the following snippet in
|
||||||
the `dependencies` script block:
|
the `dependencies` script block:
|
||||||
@@ -285,7 +285,7 @@ following line in `androidApp/settings.gradle`:
|
|||||||
>implementation project(':greeting:android')
|
>implementation project(':greeting:android')
|
||||||
>```
|
>```
|
||||||
|
|
||||||
After these steps we can access our library as any other Kotlin code:
|
After these steps we can access our library as we would with any other Kotlin code:
|
||||||
|
|
||||||
import org.greeting.*
|
import org.greeting.*
|
||||||
|
|
||||||
@@ -297,25 +297,25 @@ After these steps we can access our library as any other Kotlin code:
|
|||||||
|
|
||||||
### 4. iOS application
|
### 4. iOS application
|
||||||
|
|
||||||
As said above the multiplatform library can also be used in iOS applications. The general approach here is the same as
|
As already mentioned above the multiplatform library can also be used in iOS applications. The general approach here is the same as
|
||||||
in case of an Android application: we create a separate Xcode project and add the library as a framework. But we need
|
in the case of the Android application: we create a separate Xcode project and add the library as a framework. But we need
|
||||||
to make some additional steps here.
|
to make some additional steps here.
|
||||||
|
|
||||||
Unlike Android Studio Xcode doesn't use Gradle, so we cannot just add the library as a dependency. Instead we need to
|
Unlike Android Studio Xcode doesn't use Gradle, so we cannot just add the library as a dependency. Instead we need to
|
||||||
create a new framework in the Xcode project and then replace its default build phases with a custom one which delegates
|
create a new framework in the Xcode project and then replace its default build phases with a custom one which delegates
|
||||||
building the framework to Gradle.
|
building the framework to Gradle.
|
||||||
|
|
||||||
To do this, make the following steps:
|
To do this, perform the following steps:
|
||||||
|
|
||||||
1. Create a new Xcode project in the root directory of our project (the `application` directory in the
|
1. Create a new Xcode project in the root directory of your project (the `application` directory in the
|
||||||
[section 1](#1-preparing-a-workspace)). Name it `iosApp` so Xcode will create the project in the directory we created
|
[section 1](#1-preparing-a-workspace)). Name it `iosApp` so Xcode will create the project in the directory we created
|
||||||
in the section 1.
|
in the section 1.
|
||||||
2. Add a new framework in the project. Go `File` -> `New` -> `Target` -> `Cocoa Touch Framework`. Specify the same
|
2. Add a new framework in the project. Go to `File` -> `New` -> `Target` -> `Cocoa Touch Framework`. Specify the same
|
||||||
framework name as in `greeting/ios/build.gradle`: `Greeting`.
|
framework name as is in `greeting/ios/build.gradle`: `Greeting`.
|
||||||
3. Choose the new framework in the `Project Navigator` and open the `Build Settings` tab. Here we need to add a new build
|
3. Choose the new framework in the `Project Navigator` and open the `Build Settings` tab. Here we need to add a new build
|
||||||
setting specifying what Gradle task will be executed to build the framework for one or another platform. Fortunately,
|
setting specifying what Gradle task will be executed to build the framework for one or another platform. Fortunately,
|
||||||
Xcode allows us to set different values for the same build setting depending on the platform. Create a new build
|
Xcode allows us to set different values for the same build setting depending on the platform. Create a new build
|
||||||
setting in the `User-defined` section and name it `KONAN_TASK`. Then specify the following values of it for different
|
setting in the `User-defined` section and name it `KONAN_TASK`. Then specify the following values for it for different
|
||||||
platforms (for both `Debug` and `Release` modes):
|
platforms (for both `Debug` and `Release` modes):
|
||||||
|
|
||||||
|Platform |Value |
|
|Platform |Value |
|
||||||
@@ -329,7 +329,7 @@ platforms (for both `Debug` and `Release` modes):
|
|||||||
4. Add one more build setting for the framework to manage optimizations performed by the Kotlin/Native compiler. Name
|
4. Add one more build setting for the framework to manage optimizations performed by the Kotlin/Native compiler. Name
|
||||||
it `KONAN_ENABLE_OPTIMIZATIONS ` and set its value to `YES` for the `Release` mode and to `NO` for the `Debug` mode.
|
it `KONAN_ENABLE_OPTIMIZATIONS ` and set its value to `YES` for the `Release` mode and to `NO` for the `Debug` mode.
|
||||||
5. Ensure that the framework is still selected in the `Project Navigator` and open the `Build phases` tab. Remove all
|
5. Ensure that the framework is still selected in the `Project Navigator` and open the `Build phases` tab. Remove all
|
||||||
default phases except `Target Dependencies`.
|
the default phases except `Target Dependencies`.
|
||||||
6. Add a new `Run Script` build phase and put the following code into the script field:
|
6. Add a new `Run Script` build phase and put the following code into the script field:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -354,7 +354,7 @@ default phases except `Target Dependencies`.
|
|||||||
Kotlin sources (`greeting/ios/src` in this sample). Choose the framework created as a target to add these sources to.
|
Kotlin sources (`greeting/ios/src` in this sample). Choose the framework created as a target to add these sources to.
|
||||||
Do this for the common code of the library too.
|
Do this for the common code of the library too.
|
||||||
|
|
||||||
Now the framework is added and all Kotlin API are available from Swift code (note that you need to build the
|
Now the framework is added and all the Kotlin API are available from Swift code (note that you need to build the
|
||||||
framework in order to get code completion). Let's print our greeting:
|
framework in order to get code completion). Let's print our greeting:
|
||||||
|
|
||||||
import Greeting
|
import Greeting
|
||||||
@@ -367,6 +367,6 @@ framework in order to get code completion). Let's print our greeting:
|
|||||||
|
|
||||||
### Sample
|
### Sample
|
||||||
|
|
||||||
A sample implementation which follows these documenation can be found [here](https://github.com/JetBrains/kotlin-mpp-example).
|
A sample implementation which follows this documentation can be found [here](https://github.com/JetBrains/kotlin-mpp-example).
|
||||||
You may also look at the [calculator sample](https://github.com/JetBrains/kotlin-native/tree/master/samples/calculator). It has a simpler structure (particularly both Android app
|
You may also look at the [calculator sample](https://github.com/JetBrains/kotlin-native/tree/master/samples/calculator). It has a simpler structure (particularly both Android app
|
||||||
and Kotlin/Native library are combined in a single Gradle build) but also uses the multiplatform support provided by Kotlin.
|
and Kotlin/Native library are combined in a single Gradle build) but also uses the multiplatform support provided by Kotlin.
|
||||||
|
|||||||
Reference in New Issue
Block a user