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-Cas an example as in the official example. - You still need to have the according to
ViewController.handViewController.mfor 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 AppObj-Cproject. Named it asmyAppor whatever you want.In that
scene, add 3 new views,UILabel,UIButtonand aUITextField. Create 2outletsin theObj-C, name them aslabel,textFieldandbutton. And connect to the according view in the storyboard. The buttons should have anactionnamedbuttonPressed. 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 methodin theimplementation:
1
2
3- (IBAction)buttonPressed {
// It's empty 'cos the code will be in Kotlin-side.
}- In the
Open the
Building Settingsof 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 Phasenamed[KN] Build binary from Kotlin source, the command is./gradlew -p $SRCROOT compileKonanApp. - Add another
New Run Script Phasenamed[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 binaryCopy Bundle Resources
Now the iOS part is done. Let’s add the Kotlin code:
Create a
src/main/kotlinfolder and abuild.gradlefile at the same level of your Xcode project. So the folders look like this:- myApp
myAppmyApp.xcodeprojbuild.gradlesrc
- myApp
Paste the following code to your
gradlefile: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
myApproot folder akotlinproject withgradlesupport. The easiest way is to openIDEA, chooseimport project, and import your newly createdbuild.gradle. Everything will be setup for you.
Now add a
main.ktfile 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 AppSwiftproject. Named it asmyAppor whatever you want. - Add a
UILabelto thescene. - Drag the
UILabelto theViewController.swiftto create anoutletand named it asmyLabel. - In the
viewDidLoadmethod, let’s change the text tookby modifying the code to this:
1 | override func viewDidLoad() { |
- Now let’s add the Kotlin support. Create a
src/main/kotlinfolder and abuild.gradlefile at the same level of your Xcode project. So the folders look like this:- myApp
myAppmyApp.xcodeprojbuild.gradlesrc
- myApp
- Paste the following code to your
gradlefile:
1 | buildscript { |
Now you need to make your
myApproot folder akotlinproject withgradlesupport. 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.ktin yoursrc/main/kotlinfolder:
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 abuildfolder inside your project asbuild/konan/bin, inside thebin, there will be 2 folders,iphoneandiphone_sim. Your framework will be there.Move one of the
nativeLibs.frameworkto thebuildfolder manually, we will need it for a while.Now we have the
framework. Let’s go back toXcodeproject. Choose your project in theProject navigator. From the menu,File -> Add Files to myApp, choose thenativeLibs.frameworkfrom thebuildfolder.Add a new
Run script phasenamed[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.frameworkfrom theProject navigatorto the list.Add a new
Run script phasenamed[KN] Embed Frameworks, drag thenativeLibs.frameworkfrom theProject navigatorto the list, choose theDestinationasFrameworks.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.