React Native + Firebaseで本の読書記録に草を生やすアプリを作った話

## はじめに

こんにちは。後藤と申します。普段は神奈川でWebエンジニアをしています。
今回個人でセコセコ作っていたアプリがiOS, Android両Platformでリリースすることが出来ました！
その技術的な話とかなんで作ったのかとかを書きたくて記事にまとめています。

## 作ったもの

「hondana」というアプリを作りました。

![スクリーンショット 2019-04-10 22.09.55.png](https://qiita-image-store.s3.amazonaws.com/0/194069/d051c5c4-480b-37e5-e938-5cd393c80f74.png)


iOS
https://itunes.apple.com/jp/app/hondana/id1454093822?mt=8&ign-mpt=uo%3D4

Android
https://play.google.com/store/apps/details?id=com.gtongyproduction.hondana

このアプリは本の読んだ時間、ページ数に対してgithubのように草を生やしながら本を管理することが出来るアプリです。

## なぜ作ったのか

みなさんは本を大量に買いますか？

自分は

 - ググってもどうしても腑に落ちないところ
 - 新しい技術とか触ってみて自分が分かった気になっていないか不安で補填的な知識を調べる
 - 新しい言語にチャレンジする時のガイドラインとして
 - タイトルで完全に釣られてジャケ買い

と割と様々な理由で、本を大量購入し読みます。

いろんな考え方やためになる知識とかも身につくし、本を読むこと自体は好きでよく買います。
気になった本は気になったタイミングで買ってしまうので、まあ積読が加速しちゃうんですよね。

ただ、本って積めば積むほどやる気は下がるので、せっかく本を読んでその時に試したかったことや気になったこと学んでさらに次のモチベーションに繋げたいはずなのに、本を買うことでモチベーションが下がるのはもったいない！
なんとかしたいなぁと思っていました。

自分は30分やるぞ！って決めた時間で本を読むと、読んだ時間は同じでも途中で読み終わった感覚が残って、そこから追加で30分読もうって思って続いて継続して本を読めるんです。

そこで、本の読書するときの時間を記録出来て、githubみたいに草生やしたら継続して本読むモチベ上がるなと思い、このアプリを作りました。

## 使った技術

hondanaは主に

 - React Native(expo)
   - react+redux
 - Firebase
   - Firestore
   - Firebase Authentication

を使って開発しています。

モバイル側の実装は、元々Reactは触ったことがあったのもあって、React Nativeを使って実装しました。
イベントの状態管理は、Reduxを使っています。

サーバーサイド側の実装は開発速度を出したかったためBaas(firebase)で書くことで一旦フロントの実装に集中することにしました。
個人的にはFirebase Authenticationの使いやすさに驚きでした。

単純なログイン, ログアウト機能は

```js
// signin
firebase.auth().signInWithEmailAndPassword(value.email, value.password);
// logout
firebase.auth().logout();
```
の記述で実装できてしまうし、githubとの連携も

```js
// githubのaccess tokenを取得後の処理
const credential = firebase.auth.GithubAuthProvider.credential(token);
const user = await firebase.auth().signInAndRetrieveDataWithCredential(credential);
```

みたいな感じで、github - firebaseの連携も少ないコード量で収まるのでめちゃ使いやすかったです.
github - firebaseの連携は以下記事がわかりやすいので参考ください。

[Firebase & Github Authentication with React Native](https://blog.expo.io/firebase-github-authentication-with-react-native-2543e32697b4)

後使ったパッケージは以下に記載しておきます。

```
 - form関係
    - formik
    - yup
 - 多言語対応
    - i18n-js
 - グラフ
    - react-native-chart-kit
 - modal
    - react-native-modal
 - 画面遷移
    - react-navigation
 - テスト
   - Jest
 - アナリティクス
   - expo-analytics
```

## 辛かったこと

### プラットフォームごとの分岐が泥臭い
クロスプラットフォームごとの差分がやっぱり出てくるところがやっぱりあるので、そこを埋めるために自前で用意してやる必要があるのですが、そこの実装がどうしても泥臭くなってしまいます。

入力欄のあるpromptは自作で作る必要があったり、androidだとfont sizeが違ったり。
そこで、環境ごとの違いを``.ios.js``, ``.android.js``で呼び出しのファイルを分けるか、Platform.OSで分岐する必要があって、処理内に

```js
Platform.OS === "ios" ? hoge : fuga,
```
を記述していたんですが、三項演算子が頻発してしまい可読性は低いな...と。

### Expoで使えない機能にどうしても直面してejectを迫られる

今回のTimer機能の実装には[KeepAwake](https://docs.expo.io/versions/latest/sdk/keep-awake/)を使って実装していて、タイマーを起動している間に画面が消えるのを防いでいます。
というのも、バックグラウンドでタイマーを起動することができることは出来るのですが、バックグランド実行するために必要なライブラリはejectしないと使えないためにこの対応になっています。

React Nativeで実装する時にejectは本当に最終手段です。場面によっては仕方がない時もあるのでしょうが避けられるなら避けたい。
もちろんExpoの更新は活発で日々新しいAPIも出てきて、めっちゃ使いやすいんですが、ネイティブを全く触ったことない素人の自分にはかなりヒヤヒヤものでした。

 - [How to run background tasks in React Native – Exposition](https://blog.expo.io/how-to-run-background-tasks-in-react-native-e1619acef48f)
 - [GitHub - ocetnik/react-native-background-timer: Emit event periodically (even when app is in the background)](https://github.com/ocetnik/react-native-background-timer)

### リジェクトの理由がモバイル特有でハマる

今回リジェクトを初めて貰った時に

 - カメラスキャンするときのパーミッションのinfoPlistの記述漏れ + 日本語と英語の表記がごっちゃになってる

理由でリジェクトをもらいました。
まずinfoPlistとは？というところからExpoのPermission、言語ごとに分けられるの？ってところでかなりハマりました。

app.jsonで

```json:app.json
{
  "expo": {
    "locales": {
      "ja": "./languages/ja.json",
      "en": "./languages/en.json"
    }
  }
}
```

を記載して

```json:ja.json
{
  "NSCameraUsageDescription": "このアプリでは本のISBNを読み込むためにカメラを使用します"
}
```

```json:en.json
{
  "NSCameraUsageDescription": "This app access your camera to scan book ISBN in this app."
}
```
のように、言語ごとにカメラ利用の理由を記載すればOKみたいです。

### Expo Clientが便利すぎてスタンドアローン独自のデグレに気づけなかった

ExpoはExpo Clientというアプリを経由して動作を確認することが出来ます。
このClientが本当に便利で、QRコードを読み込むことでiPhoneの実機でも、Simulatorを立ち上げるとリアルタイムで変更を反映することが出来るので、爆速で開発することが出来ます。

しかし直前までスタンドアローンアプリを作成しなくても開発が出来てしまうので、スタンドアローン独自のデグレに気づきにくい特性もあります。

今回自分はgithubの認証時にリダイレクト元のアプリのスキームを指定する必要があったのですが、開発中はアプリはexpo clientをさしていて自分が作ったアプリをさしていなかったので、開発中には全く気づけないデグレに困惑でした。
遷移先からアプリへのリダイレクトする時のスキームは、

```json:app.json
{
  "expo": {
    "scheme": "hondana"
  }
}
```
のようにアプリ名をスキームとして設定することで正しく動作しました。
リリースする前には必ずTestFlightでビルド済みのスタンドアローンアプリの動作確認はしましょう(自戒)。

## 幸せだったこと
### Reactの知識とちょっとのやる気があればモバイルアプリを作れる

Reactである程度の知識があれば、手軽にモバイルに手を出せる。
Webのプラットフォーム以外にも技術領域を広げられて、興味範囲を広げられたのは何よりもよかった。

### 個人開発なので好きなように技術を貪れる

業務内だと、どうしても既存の技術によって好きなように開発はできなかったりすることもありますが、個人だと好きな技術を好きなだけ触ることができて幸せでした。
Firebaseを今回初めて触りましたが、ドキュメント指向型のDBは普段RDBばっか触っているので、設計手法もまた違うため新鮮だしモチベもあがります。
それに、学んだことを業務中にも生かせるので良いです。

### 周りを巻き込んだらいい感じに軌道に乗った

今回デザインは [@skinnybrian_tw](https://twitter.com/skinnybrian_tw)にお願いしました。
自分ではどうしてもうまくできないところだったのですが、爆速で素材とかレイアウトの相談だったりとかを引き受けてくれて助かりました。
自分でやるよりかははるかに最高なデザインになりました。ありがとう。

### Expoがまあ便利

App Storeにあげてみるまでを実装してみて思ったのはExpoは本当に便利でした。
自分が触ってみて

 - リアルタイムでデバッグが出来るのでほぼWeb
 - OTA UpdateでApp Storeで審査を通さなくてもアプリを更新出来る
    - ``expo publish``で反映したソースがアプリ先でも自動更新される
 - Android, iOS両方を同ソースで管理出来る
    - ただ、promptのようなiOSにしかないやつもあったりするのでまあその時は独自で実装する
 - React慣れてれば記法の違和感が少ない
 - styleの記述はcss慣れてれば割とすんなりいける

等、実際触ってみて恩恵を感じるところが多かったなぁと感じました。

## まとめ

Expo自体はかなり便利で、Reactの基本が理解出来ていれば動くレベルのものなら結構サクッと作れるのがかなり好印象でした。
ただ、ネイティブのこと何もわからないとハマった時に辛いですね。自分のネイティブの技術力はまだまだ低いのでこれから学んでいきたいです。

本をたくさん読む皆さん方のサポートができるアプリとして使っていただけると幸いです。
これからエンジニアを始める方、本を読む機会も多いと思うので是非!

そして、このアプリを作っている時間、アプリ作成に集中するあまり全く本を読めず、今自分も本を積読してしまっているので、このアプリを使って本を消化して行こうと思います。

ここまでお読みいただきありがとうございます。