Kotlin/Native を Android/iOS アプリ開発に導入しよう

Kotlin/Native が Beta 版になりましたね！

[Kotlin/Nativeがベータに到達、Kotlin 1.3にバンドル。Win/Mac/iOS/Android/WebAssemblyのバイナリ生成。KotlinConf 2018](https://www.publickey1.jp/blog/18/kotlinnativekotlin_13winmaciosandroidwebassemblykotlinconf_2018.html) - 2018年10月11日 

Beta 版リリースの記事が出たばかりですが、私はすでに Kotlin/Native を Android/iOS 両方のアプリに導入してアプリをリリースしています。

Kotlin/Native を導入した経緯などまとめます。

(2018/10/16追記) Kotlin/Native を実際に使いはじめる人向けの記事を書きました → [Kotlin/Native Multiplatform プロジェクトで Android/iOS 向けの共通ライブラリを作る](https://qiita.com/irgaly/items/eebd1273b38e1e671fcf)

## Kotlin/Native を使うまでの経緯

Kotlin/Native を調べはじめたのは 2018/3/6 ごろ

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Kotlin/Native調べてたんだけど、iOSフレームワークも普通に呼べるし、Swiftから呼び出せるframework形式でKotlin/Nativeライブラリも作れるから、もしかしてこれ普通にビジネスロジック共通化のライブラリ作れるのでは。</p>&mdash; いーがりー (@irgaly) <a href="https://twitter.com/irgaly/status/970965811178258432?ref_src=twsrc%5Etfw">2018年3月6日</a></blockquote>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">kotlin native frameworkをiOSで動かせた</p>&mdash; いーがりー (@irgaly) <a href="https://twitter.com/irgaly/status/979833737675390977?ref_src=twsrc%5Etfw">2018年3月30日</a></blockquote>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Kotlin/Nativeでクロスプラットフォームな社内ライブラリ書き上げた</p>&mdash; いーがりー (@irgaly) <a href="https://twitter.com/irgaly/status/1001459657720778752?ref_src=twsrc%5Etfw">2018年5月29日</a></blockquote>

そして 2018/7/30 に Kotlin/Native を導入した iOS アプリをリリースしました。(業務で)
その Kotlin コードを使って Android アプリもリリースしています。(業務で)

当初、こちらの[リリース記事(Kotlin/Native v0.5 released: calling Kotlin from Swift and C, LLVM 5 and more)](https://blog.jetbrains.com/kotlin/2017/12/kotlinnative-v0-5-released-calling-kotlin-from-swift-and-c-llvm-5-and-more/)の `calling Kotlin from Swift` という表現を見て Kotlin/Native を調べはじめました。

それまでアプリをクロスプラットフォーム構成で開発する場合には Xamarin を選択していましたが、Xamarin はアプリプロジェクトとアプリ基盤を Xamarin で構築する必要があります。Swift ベースのプロジェクトから C# で作成したライブラリを呼び出すということはできません。すでに Android Kotlin や Swift で開発されたアプリに対して Xamarin を導入するのはなかなか気軽にできるものではありません。

リリース記事を読む限り、Kotlin/Native では Swift プロジェクトであるアプリから Kotlin ライブラリを扱えるということだったので、既存のアプリにマルチプラットフォーム開発手法を取り込むには最適です。

## Kotlin/Native バージョン

本記事は 2018/10/12 時点の情報であり、
Kotlin 1.3.0-rc-57, Kotlin/Native 0.9.2-4008 の組み合わせにおいての情報です。

私が Kotlin/Native の導入を決定した当時は Kotlin/Native v0.5でしたが、
開発中にも v0.6, v0.7, v0.8, v0.9 とたどり、ようやくビルドシステムもアプリの動作も安定してきた感があります。

## Kotlin/Native とは

Kotlin/Native は Kotlin でプログラムを書き、LLVM を使ってネイティブバイナリを作成する仕組みです。
各プラットフォーム・アーキテクチャ向けのバイナリが生成されるので、専用の VM 環境なども必要なく、ネイティブ環境のままで動作します。

Kotlin/Native の Kotlin コードからは Windows系API, Mac系API, POSIX API, iOS系API を直接呼び出すことが可能です。
それぞれのプラットフォームの API が Kotlin 側にそのまま見えているので、基本的にはできないことはありません。
プラットフォーム API は、Kotlin からは `import platform.Foudation.NSURLSession` とか `import platform.darwin.*` とか記述することで使えるようになり、C 言語/Objective-C 言語の型とKotlinの型がマッピングされることで、透過的に呼び出されます。

※ 現時点では一部の API が Kotlin から見えていないことがあったりして、一部使えない機能があるようです

Objective-C と Kotlin のマッピングは [OBJC_INTEROP.md](https://github.com/JetBrains/kotlin-native/blob/master/OBJC_INTEROP.md) の表の通りですが、たとえば Kotlin の `String` は Objective-C の `NSString` に、Kotlin の `Map` は Objective-C の `NSDictionary` へ透過的に変換される。といった具合です。

## Kotlin/Native でできること

Kotlin/Native では以下のことが実現できます。

* Kotlin だけで、Windows アプリを作る
* Kotlin だけで、Mac OS アプリを作る
* Kotlin だけで、Linux アプリを作る
* Kotlin だけで、Android アプリを作る
* Kotlin だけで、iOS アプリを作る

ライブラリも作れます。

* Kotlin だけで、Windows / Mac / Linux 向けのライブラリ (.so, .a, .dylib, .dll, .framework...) を作る
    * Kotlin 専用の .klib ライブラリ形式もあります
* Kotlin だけで、Android 向けのライブラリ (.jar, .aar) を作る
* Kotlin だけで、iOS 向けのライブラリ (.framework) を作る

Kotlin/Native が動作する環境は以下の通りです。

* iOS (arm32, arm64, emulator x86_64)
    * 32bit 環境は Kotlin/Native v0.8 から追加サポートされたものなので、安定動作するかは不明です
* MacOS (x86_64)
* Android (arm32, arm64)
    * Android でもネイティブバイナリを動かす場合
* Windows (mingw x86_64)
* Linux (x86_64, arm32, MIPS, MIPS little endian)
* WebAssembly (wasm32)

(2018/10/12: [Kotlin/Native for Native](https://kotlinlang.org/docs/reference/native-overview.html) の内容を引用する形に書き直しました)

## Kotlin/Native のメリット

### ネイティブバイナリである

完全なネイティブバイナリを生成するため、OS やネイティブ環境との相性が良いです。普通に動きます。

Xamarin のように Mono ランタイム環境をパッケージする必要がないため、バイナリサイズも気持ちも軽量です。

### 共通ロジックのワンソース化

マルチプラットフォーム開発における最大のメリットでしょう。
Kotlin は JVM 環境でもネイティブ環境でも動作するため、API Server、iOSアプリ、Androidアプリで共通ロジックを
Kotlin ワンソースにまとめるということも可能です。

特に、通信系やモデルクラスの共通化はサーバサイドまで共通化できる余地があるので夢があります。
API Server は JVM 系で maven や gradle を使ってビルドするフレームワークを使っていれば、
Java (や Scala など) と Kotlin ソースを共存させて開発するということも容易です。

### Kotlin である

比較的新しい言語である Kotlin が使える点です。
Kotlin Coroutine も使えます。
IntelliJ 系の IDE や Android Studio が使える点も、アプリエンジニアとしては非常に助かります。

Kotlin/Native はマルチプラットフォームアプリ開発のために設計されているので、
プラットフォームに依存せず、以下の機能が提供されます。（提供予定も含む）

* http
    * 通信処理です。プラットフォームが提供する NSURLSession などを経由せずに、Kotlin だけで通信処理を完結できるように Kotlin 向けライブラリが提供されます。
* serialization
    * シリアライゼーション処理の提供。Kotlin モデルクラスと JSON を Kotlin だけでシリアライズ・デシリアライズできるようにするライブラリです。
* coroutines
    * Kotlin Coroutine をどのプラットフォームでも使えるようにします

## Kotlin/Native のデメリット

### ネイティブとの相性

Kotlin/Native はまだ Beta 版であることもあり、ネイティブでの動作がどれくらい安定するかは動かしてみないと分かりません。
この点は各プラットフォームの Kotlin/Native 利用者が増えれば改善していくでしょう。

### iOS サポートはまだ最低限のレベル

Kotlin/Native は iOS のサポートに積極的ですが、まだ足りていない機能もあります。

* `.framework` を、iOS bitcode 対応形式で書き出せない
* `.framework` は Objective-C 向けとして生成される。Swift 向けのインタフェース対応はまだ中途半端です。

このあたりは [kotlin-native リポジトリ](https://github.com/JetBrains/kotlin-native) にも Issue が切られているので、いずれ対応されるでしょう。

### まだ Beta 版である

これは時間が解決してくれますが、まだ苦労せずして使えるものではありません。
覚悟を決めてください。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Kotlin/Nativeの地雷踏み抜きまくってるけど地道に迂回しまくってる</p>&mdash; いーがりー (@irgaly) <a href="https://twitter.com/irgaly/status/1001771141944328194?ref_src=twsrc%5Etfw">2018年5月30日</a></blockquote>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">KotlinNativeで一番闇の深い罠にはまってしまった</p>&mdash; いーがりー (@irgaly) <a href="https://twitter.com/irgaly/status/1006169225243684869?ref_src=twsrc%5Etfw">2018年6月11日</a></blockquote>

それでも、v0.5 のころはまだ怪しいプロジェクト感のあった Kotlin/Native ですが、v0.9 から Kotlin のビルド環境に Kotlin/Native がバンドルされるようになり、マルチプラットフォーム開発環境としての Kotlin に本気を出している気配が感じられます。Android, iOS だけでなく、Windows なども含めて、丁寧に整備されていきそうです。

## どのように Kotlin/Native を使ったか

Kotlin/Native で `.jar` と `.framework` 形式のライブラリを作成して、Android アプリと iOS アプリそれぞれにライブラリとして導入しました。
プロジェクト概観としては以下の通りです。

![kotlin-native-library.png](https://qiita-image-store.s3.amazonaws.com/0/17931/b22d8b6c-7379-d292-55e0-cbfdc4a183e4.png)

Android アプリプロジェクトと iOS アプリプロジェクトは通常のアプリプロジェクトです。`.framework` も `.jar` も、それぞれのプラットフォーム向けのライブラリとして完結しています。このため、アプリプロジェクトからしてみれば、生成されたライブラリが安定動作さえしてくれれば、アプリへ組み込むにあたって特殊なことはありません。

ライブラリは共通の API Server へアクセスして、その結果をローカルに保持して管理するような機能を持っており、Android と iOS それぞれの画面などは扱わないものとしました。
API アクセスからデータの管理までであれば、Kotlin ワンソースでロジックの共通化が容易です。今回の開発要件と共通ロジックライブラリ化の要件がマッチした形です。

実際に、Android Kotlin と Swift でそれぞれまったく同じロジックのコードを実装するコストが省け、Kotlin 実装だけを管理して高い品質を確保できています。

## 使ってみて困ったことは？

情報が少ないです。
Kotlin/Native はまだ Beta 段階であるため、gradle プロジェクト構成が大きく変更されたりします。
とにかく Github 上の [Kotlin リポジトリ](https://github.com/JetBrains/kotlin) と [Kotlin/Native リポジトリ](https://github.com/JetBrains/kotlin-native) が一次ソースになります。

Android 環境の動作は安定していますが、iOS 環境での動作は不安定なところがあります。
スレッド処理は Kotlin 側の管理が不完全なようで、通信処理後のスレッド実行完了時に稀にアプリがクラッシュすることがあるようです。
これは　[Kotlin/Native リポジトリに Issue](https://github.com/JetBrains/kotlin-native/issues/2052) があがっていますが、まだ改善を確認できていません。

## ビルド環境・開発環境

Kotlin/Native アプリは gradle でビルドします。

Kotlin/Native アプリの開発には IntelliJ IDEA, Android Studio, AppCode, CLion のいずれかが利用できます。
私は当初 IntelliJ IDEA, Android Studio, AppCode すべて使って開発し、
iOS 向けのコードを書き終えたあとはほぼ Android Studio のみで開発していました。

ほぼ Android Studio 上で開発できるのは、アプリ開発者的には嬉しいですね！

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">いーがりーさんほどにもなるとKotlin/Native開発のためにIntelliJ, AppCode, AndroidStudio全部同時に起動して作業してるから</p>&mdash; いーがりー (@irgaly) <a href="https://twitter.com/irgaly/status/1003785683612266497?ref_src=twsrc%5Etfw">2018年6月4日</a></blockquote>

AppCode には [Kotlin/Native Plugin](https://plugins.jetbrains.com/plugin/10619-kotlin-native-for-appcode) があり、Kotlin のシンタックスハイライトやコード補完、ドキュメント参照などが使えます。

[Kotlin/Native Plugin for AppCode](https://blog.jetbrains.com/kotlin/2018/04/kotlinnative-plugin-for-appcode/)

ビルドの実行は gradle 環境があるだけで良いのですが、iOS 向け .framework をビルドするときにはビルド環境が Mac である必要があります。

## まとめ

Kotlin/Native は複数のプラットフォーム向けにネイティブバイナリを作成できること。Kotlin/Native でライブラリを作成することで、ワンソースで共通ロジックを実装することができ、Android/iOS アプリから気軽に使うことができます。

Kotlin/Native を実際に使ってみたい人は、 [Kotlin/Native Multiplatform プロジェクトで Android/iOS 向けの共通ライブラリを作る](https://qiita.com/irgaly/items/eebd1273b38e1e671fcf) を参照してください。