How to create Kotlin Native iOS project
Kotlin native is a nice way to share your code between Android and iOS. And you can fully write an iOS app in Kotlin, and it’s not like Xamarin
which has its own convention, Kotlin Native iOS follows Apple’s convention which just like write an iOS app in another language.
At least currently when this blog is written. The biggest disadvantage is the toolchain. And the hardest part is setting up the project. In this blog, I will show you 2 different ways to set up the project (without CLion
, no need to touch that at all), one is to implement everything with Kotlin Native. The second is using Kotlin native as a lib, which generates an iOS framework for sharing the coding with existing Swift / Obj-C
Xcode project.
This is a very independent way, even in the future, this blog should be still valid I suppose.
Let’s start!
Check this blog if you want to learn more about code sharing between iOS and Android.
1. Implement everything in Kotlin Native (Obj-C + Kotlin)
1.1 Something to remember
- I will use
Obj-C
as an example as in the official example. - You still need to have the according to
ViewController.h
andViewController.m
for the related UI you wanna add logic. Even though it will be kept the default. - All UI can be designed via storyboard, it’s totally fine.
- Everything else is in Kotlin. I mean even for codes like
AppDelegate
, you can code in Kotlin.
1.2 Blueprint
Basically, you still have a 100% Xcode Obj-C project. The only differences happen in the building phase. Kotlin native will generate the code via gradle
, and you will swap the Obj-C
counterparts with it. That’s all.
1.3 Step by step
Create a fresh new Xcode
Single View App
Obj-C
project. Named it asmyApp
or whatever you want.In that
scene
, add 3 new views,UILabel
,UIButton
and aUITextField
. Create 2outlets
in theObj-C
, name them aslabel
,textField
andbutton
. And connect to the according view in the storyboard. The buttons should have anaction
namedbuttonPressed
. Which means:- In the
ViewController.h
, you should have the following things inside theinterface
:
1
2
3
4
5@property (weak, nonatomic) IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UITextField *textField;
@property (weak, nonatomic) IBOutlet UIButton *button;
- (IBAction)buttonPressed;- In the
ViewController.m
, you should have anaction method
in theimplementation
:
1
2
3- (IBAction)buttonPressed {
// It's empty 'cos the code will be in Kotlin-side.
}- In the
Open the
Building Settings
of your project:- Press the Plus sign and select
New Run Script Phase
: Add a new script named[KN] Remove original binary
, the command isrm -f "$TARGET_BUILD_DIR/$EXECUTABLE_PATH"
. We start swapping! - Add another
New Run Script Phase
named[KN] Build binary from Kotlin source
, the command is./gradlew -p $SRCROOT compileKonanApp
. - Add another
New Run Script Phase
named[KN] Replace binary
, the command iscp "$SRCROOT/build/konan/bin/iphone/app.kexe" "$TARGET_BUILD_DIR/$EXECUTABLE_PATH"
.
- Press the Plus sign and select
Correct the order of all phases, the correct order should be:
- Target Dependencies
[KN] Remove original binary
- Compile Sources
- Link Binary With Libraries
[KN] Build binary from Kotlin source
[KN] Replace binary
Copy Bundle Resources
Now the iOS part is done. Let’s add the Kotlin code:
Create a
src/main/kotlin
folder and abuild.gradle
file at the same level of your Xcode project. So the folders look like this:- myApp
myApp
myApp.xcodeproj
build.gradle
src
- myApp
Paste the following code to your
gradle
file:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21buildscript {
repositories {
mavenCentral()
maven {
url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies"
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:0.5"
}
}
apply plugin: "konan"
konan.targets = ["iphone", "iphone_sim"]
konanArtifacts {
program('app')
}Now you need to make your
myApp
root folder akotlin
project withgradle
support. The easiest way is to openIDEA
, chooseimport project
, and import your newly createdbuild.gradle
. Everything will be setup for you.
Now add a
main.kt
file to yoursrc/main/kotlin
. The code is:
1 | import kotlinx.cinterop.* |
The code should be pretty straightforward. As I said, it fully follows Apple’s convention, but with a different language. And the binding between Obj-C
and Kotlin
happens at that decorator @ObjCAction
.
Now, in the Xcode
, build the project, run it, press the button, you will see Kotlin says: 'Hello ABC'
, if you enter ABC
in the text field
.
2. Using Kotlin as a lib (Swift + Kotlin)
2.1 Blueprint
The project is fully Apple Swift
Xcode project. Your Kotlin code will be compiled to an iOS framework
, so it will include the according to Obj-C to Swift
bindings for the Swift
code to invoke. And in the swift, you just need to invoke that framework
. The tricky part is for the simulator and real iOS device, the framework is different (‘cos the different architecture), you should make it auto-swap according to the target you will run against. Take easy, we have bash script
for that part.
2.2 Step by step.
- Create a fresh new Xcode
Single View App
Swift
project. Named it asmyApp
or whatever you want. - Add a
UILabel
to thescene
. - Drag the
UILabel
to theViewController.swift
to create anoutlet
and named it asmyLabel
. - In the
viewDidLoad
method, let’s change the text took
by modifying the code to this:
1 | override func viewDidLoad() { |
- Now let’s add the Kotlin support. Create a
src/main/kotlin
folder and abuild.gradle
file at the same level of your Xcode project. So the folders look like this:- myApp
myApp
myApp.xcodeproj
build.gradle
src
- myApp
- Paste the following code to your
gradle
file:
1 | buildscript { |
Now you need to make your
myApp
root folder akotlin
project withgradle
support. The easiest way is to openIDEA
, chooseimport project
, and import your newly createdbuild.gradle
. Everything will be set up for you.Now add a
main.kt
in yoursrc/main/kotlin
folder:
1 | package com.nocare.nativeLibs |
You get it, we will invoke the `getWords()` from the `swift` side and display the return value as the label text rather than our current `ok`.
Invoke the
./gradlew build
, there will be abuild
folder inside your project asbuild/konan/bin
, inside thebin
, there will be 2 folders,iphone
andiphone_sim
. Your framework will be there.Move one of the
nativeLibs.framework
to thebuild
folder manually, we will need it for a while.Now we have the
framework
. Let’s go back toXcode
project. Choose your project in theProject navigator
. From the menu,File -> Add Files to myApp
, choose thenativeLibs.framework
from thebuild
folder.Add a new
Run script phase
named[KN] Compile Kotlin Native to iOS framework
, the command is:
1 | case "$PLATFORM_NAME" in |
You see, we will check your building target from the environment variable and copy the according to source to the `build` folder.
Go to the
building phases
, in the existingLink Binary With Libraries
, drag thenativeLibs.framework
from theProject navigator
to the list.Add a new
Run script phase
named[KN] Embed Frameworks
, drag thenativeLibs.framework
from theProject navigator
to the list, choose theDestination
asFrameworks
.Now the correct building orders should be:
- Target Dependencies
[KN] Compile Kotlin Native to iOS framework
- Compile Sources
- Link Binary With Libraries
- Copy Bundle Resources
[KN] Embed Frameworks
Now open your
ViewController.swift
:import nativeLibs
- Inside
viewDidLoad()
method, add the following 2 statements.
1
2private let words = NativeLibsWords()
myLabel?.text = words.getWords()You will see that there is even a auto-completion suggestion thanks for the
obj-c bindings.
Now Run your iOS app, enjoy :)
3. End
That’s pretty all of it. As we use Kotlin native’s gradle
plugin for the building phase, this blog should be future-proof.
And it seems pretty complex at first, but in fact, it’s always the same, build the kotlin code and replace its iOS counterpart.
Hope it helps.
Thanks for reading!
Follow me (albertgao) on twitter, if you want to hear more about my interesting ideas.