Mercari Engineering Blog

We're the software engineers behind Mercari. Check out our blog to see the tech that powers our marketplace.

Kotlin Multiplatformでアプリ作ってみた!

Mercari Advent Calendar 2018 の18日目は Mercari US Android チームの @panini がお送りします。

メルカリではアプリ開発でKotlinというプログラミング言語をよく使っています。 主のユースケースではKotlinはJVM上で使うのは一番有名だと思いますが、公式のKotlinJSとKotlin/NativeプロジェクトでJavaScriptとNative(C言語)の環境でもKotlin使えます!

Kotlin Multiplatform

Kotlin Multiplatformで、一つのコードベースで複数プラットフォーム対応出来るようになります。

Problem

僕はDRYコーディングは大事だと思っています。

プラットフォームごとにアプリ作っちゃうとbusiness logicは完全にrepeatしてるのであんまり効率は良くないですね。さらに、プラットフォームごとにlogicのバグも生み出すこともあるので、メンテは大変になる。 この問題は結構痛いので、ReactNativeやFlutterみたいなフレームワークを利用して1アプリ複数プラットフォーム対応出来るアプリは作りたくなりますが、やはりそうしちゃうとそれぞれのプラットフォームの特集ポイントは使えなくなる、もしくは if(platform == "iOS") みたいなコードは出てきて、全体的にアプリの設計はぐちゃぐちゃになってしまいます。

共通にしたいのはlogicだけなので、logicだけ共通しているAndroid/iOSアプリを作ってみました!

Architecture

このプロジェクトは今の所あくまでMVP(Minimum Viable Product)なので、一番簡単に作れそうなアーキテクチャのMVVMにしました。 普通のMVVMではこういう作りになります。

http://www.plantuml.com/plantuml/png/SoWkIImgAStDuSfFoafDBb48oqmjvqBc-EQdfEOeL7FLmWI3KulACfDJOToWr8ByuioI_A9ABYwGUWLTEuG-BZWBA0pN2QM1GiYw7LBpKe0E0G00

ポイントは依存はView→ViewModelになっています。ViewModelはViewのことを知る必要はないので、ViewModelが例えばlibraryに入っていても問題はありません。

このプロジェクトで作りたい設計は以下の通りです。

http://www.plantuml.com/plantuml/png/NSzH2i8m30RWzvmY5t0d66KUG8GDVE_MCAAr7LbLXdXtezIjs9VIho7vqyp9IhfTFKnskDYHq7ClLXQiwvQ6PCFeoHXy0cBipofuOdeSeYEFQgjs97SUgAIMfZXpYhB02_HatWSdaTsdpO_us7sVKFnXLPbj5kIIFuKjpCwi6VAjL6PeA3XTz0q0

Project structure

Kotlin multiplatform プロジェクトでは、Kotlin standard library以外のクラスは使えません。これはどういう意味かと言うと、普段使ってる java.langjava.util クラスは一切使えません。時間を計算したい時には java.util.Date も使えないし、ネットワーク通信したい時に java.net.Socket も使えません。Kotlin standard libraryでSocketなどは提供されていないので、これは使うものにならないのでは?と思っているのでしょう。そこでKotlin Multiplatformの特徴ポイントが出てきます。

Multiplatformでは expectactual というキーワードがあって、これはmultiplatform専用のインターフェイスで使われています。あるplatformのクラスを使いたい場合、Kotlinのほうで expect class Hogeを定義する。これは「このクラスはplatformの方である」というフラグ!たとえば、Dateの場合はこうかけます。

expect class Date {
   fun getTime(): Long
}

そして、platformごとに、このexpectクラスをactualで実装しないといけません。multiplatformのprojectがそれぞれのplatformにコンパイルしたタイミングで、expectのクラスはactualのクラスになります。 もちろん、このactualクラスはplatform自体のクラスでも使えます

actual class Date = java.util.Date

Result

作ったプロジェクトはこちらのGithubになります! 細かい実装は次のブログで書きますが、思ったより簡単に実装出来て、Android/iOSで同じlogic使って、それぞれのplatformでUI作って、アプリを作りました。

Gotchas

実装自体はそこまで大変ではなかったが、いくつかハマる点がありましたのでここで紹介します。

Full KotlinのライブラリかMultiplatformのライブラリか自分でwrapper作るしか使えません。

これはそんなに問題ないかなーと思いました。logic layerではライブラリいらないでしょうと思っていたが、Androidの開発でよく使われてるRxJavaなどでも使えないので、ちょっと困りました。そして、RxJavaの作りはちょっと複雑ので、wrapper作るのもちょっと現実的ではないです。

Network通信 😇

Network通信もOkHttp+Retrofitのコンビ使いたくなりますが、これもJavaなのでアウトです。これは数週間どうするかいろいろ調べてみて、自分で実装しないと無理かなとなった時に Ktorがhttp clientも提供してるの気づいて、しかもmultiplatformなのでこれで救われました。

AndroidStudio

AndroidStudio3.3使っていますが、うまくmultiplatformのクラスなど認識しないので、IDE上でimport出来ないとautocompleteは動かないが、こんぱいるすると普通に動きますので一応使えます。今後3.4で試してみるつもりです

Conclusion

このプロジェクトは結構面白かったです。UIは各platformで書くことで、ReactNativeやFlutterよりは実装量は多いですが、各platformっぽいUIも作れるし、簡単にwebでも対応出来るのは大きなポイントです。 そして、このlogicのライブラリはアプリの一部として使えるので、microservice architectureとすごく相性が良いです。是非試してみてください!

明日 19 日目の執筆担当は @nekobato です。引き続きお楽しみください😸