default image

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 です。引き続きお楽しみください😸