Kotlin愛好会 vol7で談義してきました

会について

Kotlinについてワイワイする会に参加してきました。気になっていたのですが今回が初参加。

love-kotlin.connpass.com

もくもく会に参加したいので予定あけておかないと。

blog.applibot.co.jp

会場を提供してくださった、アプリボットさんのブログにあるように、談義ののちに懇談会となりました。

談義

昨年に転職して以来、Kotlinでワイワイプロダクト作っている中で考えていることを話してきました。 資料はこちら。

speakerdeck.com

Kotlinを既存プロダクトに導入・導入を推進する中で、結構重要だった感じがしたことを列挙しつつ補足しつつ、な談義をしました。 が、会場では喋りすぎたり喋り足りなかったりするところもあったので、簡単に文章で補足しておきたいなと思います。

1. Kotlin std lib

Kotlinのstdライブラリの選定について、Androidの場合には(一応) minSDK を見ておきたい。

2019年、多くのアプリでは minSDK21 以上だと思うので std-jdk7 で良さそう。ただ、そうでない場合に stdstd-jdk7 かは、Android DeveloperのAPIを見ながら考えた方が良さそうという話でした。 もちろん、ライブラリを作っている時には minSDK に合わせた適切なバージョンを選ぶ必要があります。

2. Rx-Streamの置き換え

既存プロダクトでは、Stream APIの利用やHTTTP GETの非同期処理にRx-Streamを使うことが多いと思います。 Kotlin導入の手順としてはまず、Kotlin標準のList操作へRxの処理を置き換える。HTTPレスポンスをKotlin Coroutinesへ置き換えてる、といったあたりから始めるのがよいのでは、という提案です。

この置き換えを行っていくと"Rx-Streamでなければ対応できない"処理が見つかりやすくなるので、プロダクトのどこにRxが必要なのかがわかりやすくなります。 RxでなければならないところをRxで、それ以外をKotlinの標準APIで実装することで、コードの見通しがグッとよくなります。

3. Enum + Kotlin Extension

Androidアプリを書いていると、時たま以下のようなEnumを作成するかと思います。

public enum UserType {
    FREE("free", R.string.plan_free),
    PREMIUM("premium", R.string.plan_premium); 

    public final String code;
    public final int stringResId;

    UserType(String code, int stringResId) {
        this.code = code;
        this.stringResId = stringResId;
    }
}

ただ、こういったenumでは下記のような問題が生じやすいかと思います。

  1. 要素の追加/削除がしにくい
  2. enumによるリソース/設定値の追加/削除がしにくい
  3. 「とりあえずnull」を入れるとNullableとして他の全てのケースを考慮しなければならくなる
  4. R.string.hoge の文字列が長いことによる改行が発生する

そこで、Kotlinならば下記のように2つに分離して書けば良いのでは、と考えています。

enum UserType(val code: String) {
    FREE(“free”),
    PREMIUM(“premium”)
}

fun UserType.stringResId() = when(this) {
    UserType.FREE -> R.string.plan_free
    USerType.PREMIUM -> R.string.plan_premium    
    else -> R.string.other
}

このケースでは、enumが"列挙"に終始しています。このケースでは何か別の要素を追加する際に、UserTypeの中では特に問題が発生しません。 また stringResId ではない別の要素 (ButtonColor など)をたす場合に、 stringResId のことを考慮する必要はありません。

この対応は、既存のプロダクトにあるコードをリファクタリングするだけなので、簡単に取り組むことができると思われます。

4. 2つのKotlin Data Class

主にアプリの内部で利用するロジック用のData Classと、サーバーAPIのレスポンスをパースするためのData Classを分離してみると便利では、という話です。 というのもServer API ResponseJSON Parsed ClassIn-App Dataとそれぞれを呼称すると次のような関係があると思います。

Server API Response JSON Parsed Class In-App Data
目的 サーバー側ロジックのアウトプット APIレスポンスをJVM上で扱いやすくする アプリ内ロジックに適した形でデータを表現する
特徴 サーバー側ロジックの変化による、追加/削除に対応しやすい APIレスポンスの変更に対応しやすい アプリロジックに合わせて変更しやすい

KotlinのData Classは作成しやすく、作成すればequalやhasCodeと言った比較に必要なメソッドを自動で生成してくれます。 この特徴を生かし、少々コードは増えますが2種類のData Classを使い分けてみると、ロジックがすっきりしてくるのでは、という話です。

5. Coroutines + Result

昨年末に書いた下の記事の内容です。

dr1009.hatenablog.com

Coroutinesの結果をResultでハンドリングし、同時にLiveDataで通信状態をActivity/Fragmentへ伝えていくと処理が書きやすいです。Kotlin CoroutinesによってIOスレッドとUIスレッドの接続はできますが、操作するUIパーツがAndroidのライフサイクル的に安全かどうかは保証できません。LiveDataを使うことでこの問題はカバーでき、難解なスレッド操作処理を手で書かなくてよくなります。

ResulterrorThrowableをハンドリングすることに合わせ、自ViewModelからThrowableをActivity/Fragmentへ伝播させることが最近多くなってきました。賛否があるかもしれませんが、Throwableの型によって起きたエラーが ネットワーク起因 サーバー状態起因 アプリロジック起因 なのか判定しなければ、ユーザーに次の操作を促せない状態に複数遭遇しているのがきっかけです。

このThrowableについては、もう少し考えをまとめておきたいなと思います。

おわりに

談義は10分ほどでしたが、その後には多くの方とKotlinの話をすることができました。とても楽しかったです。 まだまだKotlinの勉強するぞー。