Naoki Otsu

React Native + FirebaseでiOS & Androidアプリを開発してリリースしました

2019-05-19

React Native(Expo) + Firebaseで「TouchLife」という家計簿アプリを友人と2人で開発して、iOS & Androidアプリとしてリリースしました。
どんな感じのアプリなのか、是非体験してみてください👍
touchlife.jp

2019/12/8
・匿名ログイン機能を追加
2019/11/20
AdMob by Google導入
2019/11/3
・PC/SPのランディングページ作成
2019/10/20
・会員登録後にチュートリアルを追加
2019/10/07
・Twitterログインを追加
2019/09/15
・ワンちゃんといいねボタンを追加
2019/8/16
・レシート読み取り機能を追加 (Google Cloudの機械学習Vision APIを使用)

TouchLifeとは

お金の支出と収入を記録していく、いわゆるシンプルな家計簿アプリです。

機能はシンプルですが、
記録していけばいくほど、より細かく分析して今の状況を教えてくれる
というのが一番の特徴です。
なるべく面倒な計算をユーザーがしなくて良いように、というコンセプトで作りました。

普段は自分もエンジニアとして会社に所属しているので、その合間で開発を進めました。友人が企画 + デザイン、自分がエンジニアリングを担当。

以下、技術メインで紹介できればと思います。

使った技術

  • React Native(Expo)
  • Firebase
  • React Navigation
  • redux + thunk
  • Sentry
  • Google Analytics

React Native(Expo)

メインはReact Native + Expoです。
最初はExpoは使わずReact Nativeのみで開発していましたが、React Nativeのバージョンアップに追従させる時や何か特別なことを入れようとする度に赤いエラー画面になってしまい、機能の実装よりそのエラーの解決に労力が割かれてる感がずっと気になっていました。(アプリ開発経験が豊富じゃないと辛い印象だった。React Native開発の経験値不足もある。)

これはいかんということでExpoで1から作り直したのですが、この開発体感がびっくりするぐらい良かった。
基本Expoのレール上の実装になるので機能はExpoで提供されているものに制限されるデメリットはありますが、その分エラーになる事がほとんどなくなり、結果的にそれまでの2倍ぐらいの開発スピードになったと思います。
今回のアプリの要件がExpoの提供範囲内で十分開発可能だったこともExpo移行の理由でした。

また今回は遠隔でやりとりしながらの開発だったんですが、Expoの場合、Expo ClientのiPhoneアプリを通じて遠隔地でもビルドなしでiPhoneの実機で確認可能で、これが大変役に立ちました。

Firebase

ユーザー認証やデータベースへの保存の仕組みが必要で、認証の仕組みが予め備わっていてモバイルアプリに特化しているFirebaseを使う事にしました。
主に使った機能は、Authentication、Firestore、Functions, Hostingの4つです。

Authentication

  • メールアドレス/パスワード認証
  • Facebook/Google認証

の2つを実装しました。
特にユーザーの入力したパスワードはFirebase側のみで管理されて自前で管理しなくてよくなる(開発者も知る事ができない)ので、よりセキュア + DBにencryptして保存のような実装もいらなくなるので、その分メインの開発に時間を割く事が出来ました。

Firestore

ユーザー情報とそれに紐づく支出、収入のデータのDB保存が必要だったので、Firestoreを使いました。
usersコレクションにユーザーごとのデータ、その中に支出、収入のサブコレクションを持つ構成にしました。
リリースしたばかりなので無料の範囲内で利用できています。

Functions

お問い合わせすると、その内容をGmailに送信する必要があったのでFunctionsを使って実装しました。
内部でNodemailerを使っています。下記の記事が非常に参考になりました。
Vue.js + Firebase functionsでお問い合わせフォームを作成する

Hosting

主に利用規約、プライバシーポリシーの静的ページの為に使いました。
ExpoのWebViewを使って、アプリ内から参照するようにしています。
またメール認証した後にアプリに戻すリダイレクトが必要だったため、リダイレクト専用のhtmlファイルも配置しています。

ゆくゆくはランディングページにも利用する事になりそうです。
その際は現行のフロントエンドのベストプラクティスが詰まってるNuxtgenerateで作るのが良いかなと思ってます。(このブログもnuxt generate + markdownです)

React Navigation

ルーティングとナビゲーションはReact Navigationで実装しました。
最初はreact-native-router-fluxを使っていましたが、このモジュール自体がReact Navigationをベースに作られていてReact Navigationのドキュメントがしっかり準備されていたので、途中からReact Navigationに変更しました。
これから始めるならReact Navigationで良い印象です。

redux + thunk

状態管理はreact-redux
またDBへのアクセス時に非同期通信が必要なので、ミドルウェアはredux-thunkを入れました。
redux-sagaの選択肢もありましたが、thunkの方がなじみがあったのでこちらにしました。

Sentry

バグ検知はExpoで公式にサポートされているSentryを導入。
ユーザーの端末でエラーが起きるとGmail, Slackに通知が飛ぶようになっています。
sentry-expoを使っていて、今のところ無料範囲内で利用できています。

Google Analytics

アプリのインストール数などはApp Store Connectのアナリティクスでも確認できますが、具体的な時間帯や詳しい情報も欲しかったのでGoogle Analyticsを導入しました。
expo-analyticsを使っています。
さらに踏み込んだ分析をする場合は、Amplitudeなどを導入すると良さそうです。

使用したライブラリ一覧

以下、TouchLifeで使った主なライブラリ一覧です。

date-fns

5月1日(水)のような日本語にフォーマットされた日付が必要だったので使いました。

date-fns

UltraDate.js

給料日が土日祝の場合はその前の平日を取得するなどが必要で、そちらが取得できるUltraDate.jsを使いました。
ちゃんと実装すると面倒な祝日やうるう年の計算など、Dateオブジェクトを便利に拡張したライブラリです。

UltraDate.js

react-native-svg + d3-shape

円グラフの為に使いました。
最初はreact-native-pie-chartを使っていたのですが、円グラフをアニメーションさせようとするとreact-native-pie-chartだと難しそうでこちらを使いました。(アニメーションしない円グラフだけの表示ならreact-native-pie-chartでも十分だと思います)
d3-shapeで円のデータを作成、それをreact-native-svgで描画する流れです。

下記の記事が参考になりました。
Pie animation in React Native using SVG

react-native-svg
d3-shape

formik + yup

メールアドレスやパスワードなどフォームのバリデーションが扱いやすくなるので使いました。Reactの公式ドキュメントでも紹介されているものです。
別の選択肢だとredux-formもあり、こちらはフォームの状態をreduxのstoreに保存するようですが、Reactコミュニティの一般的な見解だとフォームの状態をreduxで管理しない方向のようで、formikreact-final-formを使うのが良いとのことでした。

formik
yup

native-base

主にフォームのセレクトボックスで使用。
最初セレクトボックスは、react-native-picker-selectを使っていたのですが、Androidで見た時に表示がおかしかったので、安定していたnative-baseに切り替えました。

native-base

react-native-calendars

カレンダーの実装に使っています。
monthFormat={"yyyy年MM月"}などで日本語のフォーマットでカスタマイズ可能なのも良かったです。

react-native-calendars

react-native-easy-toast

マイページなどユーザー情報を変更した時の、成功、失敗をユーザーに知らせる目的で使いました。
表示する位置や色は自由にカスタマイズ出来ます。

react-native-easy-toast

react-native-keyboard-aware-scroll-view

画面の下の方にInput要素があると、キーボードが下から出てきた時に入力している部分が隠れてしまい何が入力されているのか分からない問題があったのですが、その部分が隠れないようにスクロールしてくれるモジュールです。自分で実装するとそれなりに大変そうだったので助かりました。

react-native-keyboard-aware-scroll-view

react-native-modal

シンプルなモーダルです。
簡単に表示、非表示が出来て、スタイルもカスタマイズ可能です。

react-native-modal

react-native-scrollable-tab-view-universal

タブ切り替えが必要な箇所に使いました。
スワイプ操作にも対応しているのでUXも良い印象です。
最初は、react-native-scrollable-tab-viewを使っていましたが、ScrollViewの中で使おうとするとAndroidだけ中の要素が表示されない不具合があり、それが解決されているuniversalを使うことにしました。
universalもreact-native-scrollable-tab-viewをForkしてるものになるので、この不具合が修正が改善されれば、react-native-scrollable-tab-viewで良いと思います。

react-native-scrollable-tab-view-universal

react-native-snap-carousel

オープニングやグラフのカルーセルに使いました。
次や前の月のグラフもちょっと見せるUIにしかったので、それが可能だったこちらを使いました。

react-native-snap-carousel

react-native-walkthrough-tooltip

ツールチップの表示に使いました。
アプリの使い方を伝える最初のチュートリアルにも使えそうです。
ツールチップが画面外にはみ出ないように、ライブラリ側で位置を自動計算してくれて使う側がそこを意識しなくてよいのが良かったです。

react-native-walkthrough-tooltip

npm-run-all

実践Expoに書いてあって初めて知ったんですが、これが便利でした。
yarn add -D npm-run-all後に、package.jsonのscriptsを下記のように書いておいて、

"scripts": {
  "build:ios": "npm-run-all -s ios:*",
  "ios:run": "expo build:ios --non-interactive",
  "ios:file": "mkdir -p .build && wget \"$(expo url:ipa)\" -O .build/app.ipa",
},

yarn build:iosすると、ios:runios:fileなどのios:*のscriptsが順番に実行される、というものです。
これでipaファイルが.build/app.ipaに生成されるので、後はfastlaneを使ってApp Store Connectにデプロイ → 実機確認 → 申請 → 公開 という感じで無事リリース出来ました。

実践Expoは開発の途中からKindle版を買ったんですが、特にExpoを使ったリリース方法 + AnalyticsやSentryの設定方法も載っていて、とても為になりました。Expoで開発する場合はオススメの一冊です。

まとめ

React Native良いですね。
開発途中でAndroidで実機確認したんですが、最初にAndroidでiPhoneと同じように動作した時は感動しました。

また途中からExpoで作り直したのも良かったなと思っています。
最初はExpoって公式のReact Nativeからちょっと外れたニッチな開発のように感じて、公式から外れるのはどうかな的な感覚があったのですが使ってみると使いやすくて、必要ならejectしていつでも公式のReact Nativeに戻せるというのも移行した理由になりました。

TouchLifeも定期的に更新して、常に改善している事を感じてもらえるアプリにしていきたいと思います。
最後まで読んでいただき、ありがとうございました👍