SocialHub を支える技術 - iOS Android 共通ライブラリ編 - 

# SocialHub とは？

　まずはアプリの紹介をさせてください。SocialHub とは、先日筆者がリリースした iOS 向けマルチ SNS クライアントアプリです。大きな特徴として挙げられるのが、**複数の SNS を同時に見て投稿できる** ことで、現在対応している SNS の種類は **Twitter, Mastodon, Slack, Tumblr** になります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75245/ddf39647-8e65-65ae-6dae-b2cf169537ec.png" width="80">
[SocialHub - マルチ SNS クライアント - | App Store](https://apps.apple.com/us/app/socialhub-socialmedia-client/id1474451582?l=ja&ls=1)

# マルチプラットフォーム

　SocialHub は現在 iOS のアプリしかリリースしていませんが、Android のアプリも見据えたプロジェクトで、複数の SNS を統合して扱う都合上複雑なロジックから逃げられないため、できればワンコードでロジックを記述したい気持ちがありました。昨今流行りのマルチプラットフォームなフレームワークを用いての実装を、当初 (2018年初旬) 考えており、当時のマルチプラットフォームというと、[Xamarin](https://docs.microsoft.com/ja-jp/xamarin/) や [React Native](https://facebook.github.io/react-native/) が選択肢としてあったのですが、自分がその分野に明るくなかった事や、一番の問題として、そのライブラリで本当に自分の目指した UI/UX が実現できるか自信がなかったことです。

　また、技術選定中に [Flutter](https://flutter.dev/) も注目を浴びましたが、こちらについてもサンプルプロジェクトを作って動作を検証したのですが、パフォーマンスが気になる事や、情報の少なさから、自分の求める UI が作成できるか自信がなかったので採用を見送りました。

## 共有ライブラリという選択

　そこで注目したのが、iOS/Android で共有ライブラリを作ることのできるフレームワークです。共有ライブラリを用いることで、共通のロジックは全て単一の言語で記述し、クライアントアプリ側はネイティブで UI を記述することによって、リッチな体験を担保します。以下に選択肢として考えたものについて記述します。(理解が浅い部分があったらコメントくださると助かります)

### Embeddinator-4000
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75245/2b881a9e-b764-6020-fd6f-ce3cbfea3c43.png" width="400">
　共有ライブラリという方針を決めてから、一番初めに見つけたのが [Embeddinator-4000](https://github.com/mono/Embeddinator-4000) です。これは Xamarin のサブプロジェクトの一つで C# で書かれたコードを iOS/Android 向けの共有ライブラリとして扱うことができるようにするコンパイラ? (適切な表現かどうか不明) です。細かい内容については、以下の自分のブログ記事を御覧ください。

　[.NET のコードを他言語に変換？ Embeddinator-4000 を使ってみた。| urushi blog](http://urushi.hatenablog.jp/entry/2018/02/07/231750)

　動作確認までしたのですが、正直 iOS 向けにバイナリをビルドする際に、C# のジェネリクスが使えない事がかなりの痛手で、ほぼまともに動作するライブラリを作成することができませんでした。理由としては iOS 向けに mono のランタイムをアプリに同梱することができないためで、一朝一夕でなんとかなる問題ではありませんでした。また、開発もあまり活発的ではなく、上記の問題の解決が期待できないので諦めざるを得ない状況でした。

### J2ObjC
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75245/dbec5fd9-9f07-c24c-ce0f-2036876c83b9.png" width="80">
　次に発見したのが [J2ObjC](https://developers.google.com/j2objc) です。Google が主に開発を進めているプロジェクトで、**Java のコードを Objective-C に変換してくれるコンパイラ** になります。既に公開から八年以上経っているプロジェクトですが、まだ開発が行われており、Google 社内でも恐らく使われているようなので、安心感があります (?) J2ObjC についても、ブログ記事を書いたので、詳しくは以下を参照してください。

　[Java のコードを Objective-C に？ J2ObjC を試してみた。 | urushi blog](http://urushi.hatenablog.jp/entry/2018/02/16/013303)

　**SocialHub はこの J2ObjC を採用しました。** 理由としては、

 * 自分に Java に対しての言語理解があること
 * プロジェクトがメンテされていること
 * パフォーマンスの劣化がほぼ無いこと

　が挙げられます。J2ObjC はコンパイル前後の言語 (要するに Java と Objective-C) の知識をある程度必要とするので、問題に対応する際に、Java が分からないでは対処が難しいケースが存在します。

　**ということで SocialHub で作成したコアライブラリがこちら。** 

　[SocialHub | github](https://github.com/uakihir0/SocialHub)

　(ライブラリ名も SocialHub) J2ObjC でここまでコンパイルしている例って Google 外ではあまりないんじゃないかと思う。(そもそも J2ObjC なんて誰も使ってねえよ、っていう声は聞かないことにします) コンパイルは GitHub Actions に代行させており、大体所要時間は 15 分程。Java を Objective-C に変換した後、マルチアーキテクチャ向けにバイナリを作成し、Cocoapod のレポジトリを作成するところまで実行します。

　補足として、J2ObjC のサブプロジェクトでは、既にメンテナンスされていないものも存在し、ややエコシステムとして不便な部分もありました。J2ObjC はコンパイルする際に make (or bazel) を使うケースが多いのですが、自分はめんどくさがりなので、[J2ObjC の Gradle Plugin](https://github.com/j2objc-contrib/j2objc-gradle) を用いてコンパイルを行っていました。Plugin を使うと Gradle で記述されている依存関係についても自動的にコンパイルターゲット加え、かつ import での参照関係も確認してくれるため、余計なコンパイルをしないで済みます。しかし Gradle Plugin は既にメンテされなくなっており、Java11 では動作しない問題を抱えていました。そのため [Fork](https://github.com/uakihir0/j2objc-gradle) して修正しました。(結局手間がかかっている)

　正直、J2ObjC での初期の実装は茨の道でしたが、その道を通り抜けると、それなりに快適な開発環境を整えることができました。**ということで、SocialHub では Java のライブラリを使用して iOS アプリを作成しています。** 

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">SocialHub の権利表記画面頑張りすぎた。。。こんながんばらんでもええだろ、誰も見ねえよ。。。 <a href="https://t.co/Q93wySWfRt">pic.twitter.com/Q93wySWfRt</a></p>&mdash; うるし (@U_Akihir0) <a href="https://twitter.com/U_Akihir0/status/1193460564254527489?ref_src=twsrc%5Etfw">November 10, 2019</a></blockquote>

　アプリ内の権利表記にはコアライブラリ内で使用した Java のライブラリも記載しています。[Twitter4J](http://twitter4j.org/ja/index.html) をはじめとした各種 SNS クライアントライブラリを使っています。

#### Kotlin/Native

　一応、共有ライブラリということなので、忘れてはいけないもう一つの選択肢である 
[Kotlin/Native](https://kotlinlang.org/docs/reference/native-overview.html) について記載します。こちらは Kotlin で書かれたコードを LLVM エコシステムに混ぜてしまおうという、実にシンプルな目的のプロジェクトです。

　ここで厄介なのが、Kotlin で書かれたコードといっても、その内容に制約がかかることです。Kotlin といえば一般的に Java の資産がそのまま流用できるイメージを持たれていると思いますが、Kotlin/Native においては JVM 外での Kotlin の使用になるので java, javax 以下のパッケージのライブラリには使用の制限がかかりますし、既存の Kotlin ライブラリもマルチプラットフォーム用に記述されていないと使用できません。

　利点としては、プラットフォーム API 等を用いたより高度な抽象化ロジックを記述することができる事にあります。Kotlin/Native では iOS の UIKit を扱うためのライブラリ等が提供されているため、共有ライブラリのレベルで実現できる範囲が広いのが特徴です。SocialHub ではあまりその恩恵を受けられないので、J2ObjC を選択しました。将来的に Java のライブラリが困難なく使用できるレベルになってきたら使用するか検討したいと考えています。(そんな日が来るのかあんま詳しくない)


# まとめ

* **マルチプラットフォームを選択する場合、本当に実現したい事が何かに立ち戻る**

　SocialHub の場合ユーザー体験について妥協することがどうしても嫌だったので、UI やパフォーマンスに対してフレームワークを理由に妥協することがどうしてもできなかった → 共通ライブラリという選択

　無論、何を意識するかで全てが変わるので、実装スピードやスキルセットに合わせて Flutter や ReactNative を使用することは全然アリです。というか、かなり自分は異端だと感じています。

* **J2ObjC 使ってる人いないけどいいぞ**
 
　共通ライブラリをまとめると以下のような印象。

| フレームワーク | 言語 | 導入難度 | 知名度 | 実用性 | 
|---|---|---|---|---|
| [Embeddinator-4000](https://github.com/mono/Embeddinator-4000) | C# | 難しい | ほぼ無名 | ✗ |
| [J2ObjC](https://developers.google.com/j2objc) | Java | 難しい | ほぼ無名 | ○ |
| [Kotlin/Native](https://kotlinlang.org/docs/reference/native-overview.html) | Kotlin | 普通 | 有名 | △ |


　J2ObjC のすごいところは、Java の標準ライブラリをほぼ Objective-C で再実装されている点で、思った以上に普通に Java がコンパイルできることです。でも Java なんだよなぁ。。。