Skip to content
This repository was archived by the owner on May 17, 2023. It is now read-only.

Commit 9f232c6

Browse files
committed
move iOS and Android tutorial
1 parent c15292d commit 9f232c6

19 files changed

+483
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Introduction
2+
3+
In this tutorial, we will create an application for both iOS and Android, making use of Kotlin's code sharing capabilities.
4+
For Android, we'll be using Kotlin/JVM, while for iOS we'll use Kotlin/Native.
5+
6+
We're going to learn how to:
7+
- Create an [Android app](#creating-an-android-project) using Android Studio
8+
- Create a shared [Kotlin library](#creating-the-shared-module)
9+
- Use the Kotlin library [from the Android app](#using-sharedcode-from-android)
10+
- Start the [Android application](#running-the-android-application)
11+
- Create an [iOS app](#creating-ios-application) using Xcode
12+
- Use the shared Kotlin library [from the iOS app](#setting-up-framework-dependency-in-xcode)
13+
- Call [Kotlin from Swift](#calling-kotlin-code-from-swift)
14+
- Start the [iOS application](#running-the-ios-application)
15+
16+
The application we're going to create will display the text
17+
`Kotlin Rocks on Android` on Android devices and `Kotlin Rocks on iOS <version>` on iOS devices.
18+
Our goal is to demonstrate the ability to share Kotlin code between the platforms, the project setup, and the benefits that
19+
this provides. While we'll be demonstrating this with a simple application, what is shown here can be applied to real-world applications, no matter their size or complexity.
20+
21+
The common code is `"Kotlin Rocks on ${platformName()}"`, where `platformName()` is
22+
a function that is declared using the `expect` keyword. The `actual` implementation will be specific to the platform.
23+
24+
![Emulator App](./assets/iPhone-emulator-kotlin-rocks.png) ![Emulator App](./assets/android-emulator-kotlin-rocks-android.png)
25+
26+
27+
# Setting Up the Local Environment
28+
29+
* We will be using [Android Studio](https://developer.android.com/studio/) for the Android part of the tutorial.
30+
It is also possible to use [IntelliJ IDEA](https://jetbrains.com/idea) Community or Ultimate edition.
31+
32+
* The Kotlin plugin 1.3.50 or higher should be installed in the IDE. This can be verified via
33+
*Language & Frameworks | Kotlin Updates* section in the *Settings* (or *Preferences*) window.
34+
35+
* A macOS host operating system is required to compile for iOS and macOS devices. We need to have
36+
[Xcode](https://developer.apple.com/xcode/) and the tools installed and configured. Check out
37+
the [Apple Developer Site](https://developer.apple.com/xcode/) for more details.
38+
39+
*Note: We'll be using IntelliJ IDEA 2019.2, Android Studio 3.4,
40+
Kotlin 1.3.50, Xcode 10.3, macOS 10.14, Gradle 5.5.1*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Creating an Android Project
2+
3+
We need Android Studio for the tutorial. We can download and install it from the
4+
[https://developer.android.com/studio/](https://developer.android.com/studio/). Let's open
5+
the IDE and check that we see the newest Kotlin version, namely
6+
1.3.50
7+
or newer under the Kotlin section _Languages & Frameworks_ | _Kotlin_
8+
in the _Settings_ (or _Preferences_) dialog of Android Studio.
9+
10+
Our first step is to create a new Android project via the *Start a new Android project* item on the Android Studio home screen.
11+
We then proceed to select the *Empty Activity* option and click *Next*. It's important to pick the _Kotlin_
12+
language in the wizard. Let's use the `com.jetbrains.handson.mpp.mobile` package
13+
for the tutorial. Now we can press the *Finish* button and create our new Android project.
14+
15+
At this point, we should be able to compile and run the Android application. Let's check that it works!
16+
17+
The `step-002` branch of the
18+
[github.com/kotlin-hands-on/mpp-ios-android](https://github.com/kotlin-hands-on/mpp-ios-android/tree/step-002)
19+
repository contains a possible solution for the tasks that we have done above. We can also download the
20+
[archive](https://github.com/kotlin-hands-on/mpp-ios-android/archive/step-002.zip) from GitHub directly or
21+
check out the repository and select the branch.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# Creating the Shared Module
2+
3+
The goal of this tutorial is to demonstrate the reusability of Kotlin code between Android and iOS. Let's start
4+
by manually creating a `SharedCode` sub-project in our Gradle project. The source code from the `SharedCode`
5+
project will be shared between platforms.
6+
We will create several new files in our project to implement this.
7+
8+
## Updating Gradle Scripts
9+
10+
The `SharedCode` sub-project should generate several artifacts for us:
11+
- A JAR file for the Android project, from the `androidMain` source set
12+
- The Apple framework
13+
- for iOS device and App Store (`arm64` target)
14+
- for iOS simulator (`x86_64` target)
15+
16+
Let's update the Gradle scripts now to implement this and configure our IDE.
17+
First, we add the new project to the `settings.gradle` file, simply by adding the following line to the end of the file:
18+
19+
```groovy
20+
include ':SharedCode'
21+
```
22+
23+
Next,
24+
we need to create a `SharedCode/build.gradle.kts` file with the following content:
25+
26+
```kotlin
27+
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
28+
29+
plugins {
30+
kotlin("multiplatform")
31+
}
32+
33+
kotlin {
34+
//select iOS target platform depending on the Xcode environment variables
35+
val iOSTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget =
36+
if (System.getenv("SDK_NAME")?.startsWith("iphoneos") == true)
37+
::iosArm64
38+
else
39+
::iosX64
40+
41+
iOSTarget("ios") {
42+
binaries {
43+
framework {
44+
baseName = "SharedCode"
45+
}
46+
}
47+
}
48+
49+
jvm("android")
50+
51+
sourceSets["commonMain"].dependencies {
52+
implementation("org.jetbrains.kotlin:kotlin-stdlib-common")
53+
}
54+
55+
sourceSets["androidMain"].dependencies {
56+
implementation("org.jetbrains.kotlin:kotlin-stdlib")
57+
}
58+
}
59+
60+
val packForXcode by tasks.creating(Sync::class) {
61+
val targetDir = File(buildDir, "xcode-frameworks")
62+
63+
/// selecting the right configuration for the iOS
64+
/// framework depending on the environment
65+
/// variables set by Xcode build
66+
val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
67+
val framework = kotlin.targets
68+
.getByName<KotlinNativeTarget>("ios")
69+
.binaries.getFramework(mode)
70+
inputs.property("mode", mode)
71+
dependsOn(framework.linkTask)
72+
73+
from({ framework.outputDirectory })
74+
into(targetDir)
75+
76+
/// generate a helpful ./gradlew wrapper with embedded Java path
77+
doLast {
78+
val gradlew = File(targetDir, "gradlew")
79+
gradlew.writeText("#!/bin/bash\n"
80+
+ "export 'JAVA_HOME=${System.getProperty("java.home")}'\n"
81+
+ "cd '${rootProject.rootDir}'\n"
82+
+ "./gradlew \$@\n")
83+
gradlew.setExecutable(true)
84+
}
85+
}
86+
87+
tasks.getByName("build").dependsOn(packForXcode)
88+
```
89+
90+
We need to refresh the Gradle project to apply these changes. Click on the `Sync Now` link or
91+
use the *Gradle* tool window and click the refresh action from the context menu on the root Gradle project.
92+
The `packForXcode` Gradle task is used for Xcode project integration. We will discuss this later in the
93+
tutorial.
94+
95+
## Adding Kotlin Sources
96+
97+
The idea is to make every platform show similar text: `Kotlin Rocks on Android` and
98+
`Kotlin Rocks on iOS`, depending on the platform. We will reuse the way we generate the message.
99+
Let's create the file (and missing directories) `SharedCode/src/commonMain/kotlin/common.kt` with the following contents
100+
under the project root directory
101+
102+
```kotlin
103+
package com.jetbrains.handson.mpp.mobile
104+
105+
expect fun platformName(): String
106+
107+
fun createApplicationScreenMessage() : String {
108+
return "Kotlin Rocks on ${platformName()}"
109+
}
110+
111+
```
112+
113+
That is the common part. The code to generate the final message. It `expect`s the platform part
114+
to provide the platform-specific name from the `expect fun platformName(): String` function. We will use
115+
the `createApplicationScreenMessage` from both Android and iOS applications.
116+
117+
Now we need to create the implementation file (and missing directories) for Android in the `SharedCode/src/androidMain/kotlin/actual.kt`:
118+
```kotlin
119+
package com.jetbrains.handson.mpp.mobile
120+
121+
actual fun platformName(): String {
122+
return "Android"
123+
}
124+
125+
```
126+
127+
We create a similar implementation file (and missing directories) for the iOS target in the `SharedCode/src/iosMain/kotlin/actual.kt`:
128+
```kotlin
129+
package com.jetbrains.handson.mpp.mobile
130+
131+
import platform.UIKit.UIDevice
132+
133+
actual fun platformName(): String {
134+
return UIDevice.currentDevice.systemName() +
135+
" " +
136+
UIDevice.currentDevice.systemVersion
137+
}
138+
```
139+
140+
Here we can use the [UIDevice](https://developer.apple.com/documentation/uikit/uidevice?language=objc)
141+
class from the Apple UIKit Framework, which is not available in Java, it is only usable in Swift and Objective-C.
142+
The Kotlin/Native compiler comes with a set of pre-imported frameworks, so we can use
143+
the UIKit Framework without having to do any additional steps.
144+
The Objective-C and Swift Interop is covered in detail in the [documentation](/docs/reference/native/objc_interop.html)
145+
146+
## Multiplatform Gradle Project
147+
148+
The `SharedCode/build.gradle.kts` file uses the `kotlin-multiplatform` plugin to implement
149+
what we need.
150+
In the file, we define several targets `common`, `android`, and `iOS`. Each
151+
target has its own platform. The `common` target contains the common Kotlin code
152+
which is included into every platform compilation. It is allowed to have `expect` declarations.
153+
Other targets provide `actual` implementations for all the `expect`-actions from the `common` target.
154+
A more detailed explanation of multiplatform projects can be found in the
155+
[Multiplatform Projects](/docs/reference/building-mpp-with-gradle.html) documentation.
156+
157+
Let's summarize what we have in the table:
158+
159+
| name | source folder | target | artifact |
160+
|---|---|---|---|
161+
| common | `SharedCode/commonMain/kotlin` | - | Kotlin metadata |
162+
| android | `SharedCode/androidMain/kotlin` | JVM 1.6 | `.jar` file or `.class` files |
163+
| iOS | `SharedCode/iosMain` | iOS arm64 or x86_64| Apple framework |
164+
165+
Now it is again time to refresh the Gradle project in Android Studio. Click *Sync Now* on the yellow stripe
166+
or use the *Gradle* tool window and click the `Refresh` action in the context menu on the root Gradle project.
167+
The `:SharedCode` project should now be recognized by the IDE.
168+
169+
We can use the `step-004` branch from the
170+
[github.com/kotlin-hands-on/mpp-ios-android](https://github.com/kotlin-hands-on/mpp-ios-android/tree/step-004)
171+
repository as a solution for the tasks that we've done above. We can also download the
172+
[archive](https://github.com/kotlin-hands-on/mpp-ios-android/archive/step-004.zip) from GitHub directly
173+
or check out the repository and select the branch.
174+
175+
Let's use the `SharedCode` library from our Android and iOS applications.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Shared Kotlin Code for Android
2+
3+
For this tutorial, we want to minimize Android project changes, so we add an ordinary dependency from that
4+
project to the `SharedCode` project.
5+
It is also possible to use the `kotlin-multiplatform` plugin directly in an Android
6+
Gradle project, instead of using the `kotlin-android` plugin. For more information, please refer to the
7+
[Multiplatform Projects](/docs/reference/multiplatform.html) documentation.
8+
9+
Let's include the dependency from the `SharedCode` project to the Android project. We need to patch
10+
the `app/build.gradle` file and add the following line in the `dependencies { .. }` block:
11+
12+
```groovy
13+
implementation project(':SharedCode')
14+
```
15+
16+
We need to
17+
assign the `id` to the `TextView` control of our activity to access it from the code.
18+
Let's patch the
19+
`app/src/main/res/layout/activity_main.xml` file
20+
(the name may be different if we changed it in the new project wizard).
21+
Select the _Text_ tab at the bottom of the preview to switch it to XML
22+
and add several more attributes to the `<TextView>` element:
23+
```
24+
android:id="@+id/main_text"
25+
android:textSize="42sp"
26+
android:layout_margin="5sp"
27+
android:textAlignment="center"
28+
```
29+
30+
Next, let's add the following line of code to the end of the `onCreate` method from the `MainActivity` class
31+
in the `/app/src/main/java/com/jetbrains/handson/mpp/mobile/MainActivity.kt` file, :
32+
33+
```
34+
findViewById<TextView>(R.id.main_text).text = createApplicationScreenMessage()
35+
```
36+
37+
You will need to add the import for the `android.widget.TextView` class. Android Studio
38+
will automatically suggest adding the import. Depending on the Android application package,
39+
we may also need to add the import for the `createApplicationScreenMessage()` function too.
40+
We should see these two lines at the beginning of the `MainActivity.kt` file:
41+
```kotlin
42+
import com.jetbrains.handson.mpp.mobile.createApplicationScreenMessage
43+
import android.widget.TextView
44+
```
45+
46+
Now we have a `TextView` that will show us the text created by the shared
47+
code function `createApplicationScreenMessage()`. It shows `Kotlin Rocks on Android`.
48+
Let's see how it works by running the Android application.
49+
50+
We can use the `step-005` branch of the
51+
[github.com/kotlin-hands-on/mpp-ios-android](https://github.com/kotlin-hands-on/mpp-ios-android/tree/step-005)
52+
repository as a solution for the tasks we've done above. We can also download the
53+
[archive](https://github.com/kotlin-hands-on/mpp-ios-android/archive/step-005.zip) from GitHub directly
54+
or check out the repository and select the branch.
55+
56+
## Running the Android Application
57+
58+
Let's click on the `App` run configuration
59+
to get our project running on a real Android device or an emulator.
60+
61+
![Start the Application](./assets/mpp-ios-android/studio-start-app.png)
62+
63+
And now we can see the Application running in the Android emulator:
64+
65+
![Emulator App](./assets/android-emulator-kotlin-rocks-android.png)
66+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Creating iOS Application
2+
3+
We can start by opening Xcode and selecting the *Create a new Xcode project* option. In
4+
the dialog, we need to choose the iOS target and select the *Single View App* and click next. Fill in the next page with the defaults,
5+
and use `KotlinIOS` as the *Product Name*. Let's select _Swift_ as the language (it is possible to use
6+
Objective-C too). Use the `com.jetbrains.handson.mpp.mobile` string for the _Organization Identifier_ field.
7+
Now the _Next_ button should be available, let's click it to move on.
8+
In the file dialog, shown after clicking the _Next_ button, we need to select the root folder
9+
of our project, click the _New Folder_ button and create a folder called `native` in it.
10+
The folder should be selected now, and we can click the _Create_
11+
button to complete the dialog. We will use relative paths in the configuration files later in this tutorial.
12+
13+
The created iOS application is ready to run on the iOS simulator or iOS device. For the device to run
14+
it may require an Apple developer account and a developer certificate. Xcode does its
15+
best to get us through the process.
16+
17+
Let's make sure we can run the application on the iPhone simulator or device by clicking the play button
18+
from the XCode window title bar.
19+
20+
The `step-006` branch of the
21+
[github.com/kotlin-hands-on/mpp-ios-android](https://github.com/kotlin-hands-on/mpp-ios-android/tree/step-006)
22+
repository contains a possible solution for the tasks that we have done above. We can also download the
23+
[archive](https://github.com/kotlin-hands-on/mpp-ios-android/archive/step-006.zip) from GitHub directly or
24+
check out the repository and select the branch.

0 commit comments

Comments
 (0)