FirebaseだけでiOS版マッチングアプリ「Rose Me」をリリースするまでの話

限定公開した日付になっていたので再投稿しました泣

普段は京都大学で学生をしており、iOSエンジニアとしての歴も3年目となりました。

NoSQLもFirebaseも全く触ったことのなかった僕が、バックエンド全てFirebaseで完結させた知見を残しておきます。

# 何を作ったのか
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/5d6540e1-08b1-5357-1527-1965278d0091.png" width=400>
ランディングページ
https://app-rose.me/

AppStore
https://apps.apple.com/us/app/rose-me/id1453049174?l=ja&ls=1

**恋愛は試着する時代**

という信念のもと、試着感覚のデートを提供するマッチングアプリ**Rose Me**を開発しました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/880b31cc-7e32-decd-cd21-40c7133fa7d2.png" width=400>

**空いている時間**に、**好きな場所**で**1時間だけのデートのおさそい**をして、ローズを送ることでデートに行きたいという気持ちを伝えることができます。

デート後に、お互いが**気になるボタン**を押していれば**連絡はとりあうことができ**、片方でも押さなければ**連絡はとれなくなる**といったシステムです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/f6751672-7330-691d-bf61-cdad46062664.png" width=400>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/1d200cd0-1171-63a4-09a6-3f3ae2eda50c.png" width=400>

バラを実際に渡す感じを実装したのでぜひアプリで触ってみてください！

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/ee7c70fa-18b4-3270-b11f-ceee3380503b.png" width=400>

# 開発チーム
iOSエンジニア2人というめちゃくちゃに偏ったチームでした。

役回りとしては

```
僕: プロダクト設計・DB設計・Firebase周り全て・アプリ全般・年齢確認Webアプリ・書類全般・UI
相方: 「チャット・プロフィール・マイページ」等の主要画面の開発・LP作成・UI
```

って感じでした。

後半はZenHubを使用してWeek単位でタスク管理をして開発を進めていました。

ちなみに僕はプロダクト設計・DB設計(NoSQL)・Firebase・Webアプリ(React)は全部初めてでした。

UIは二人とも素人で、これだけを参考にして画面作ってましたww
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/f974e37a-87ec-71a8-49aa-dc4a655eb2be.jpeg" width=400>


# なぜFirebaseだけでマッチングアプリを作ったのか

iOSエンジニアの友人と法人を立ててマッチングアプリを作ろうということで、2018年の10月から開発をスタートしました。

そこで直面した問題が

**「バックエンドできる人いなくね？www」**

でした。

「バックエンドを簡単に作れるFirebaseみたいなものがあるらしい。」ぐらいの知識でしたが、[Cookpadさんの記事](https://techlife.cookpad.com/entry/2018/07/17/090000)をみて、藁にもすがる思いで速攻採用しました。

結論から言うと、Firebaseを選択して大正解でした！！

# 使用した技術

### Firebase
- Authentification
    - Facebookログインに使用しました
- Cloud Firestore
    - NoSQLのデータベース、必須です
- Cloud Functions
    - DBの変更を監視してプッシュ通知を送ったり
- Cloud Storage
    - プロフィール画像を保存しています
- Hosting
    - 年齢確認用のWebアプリをのっけてます

### 主要なライブラリ
- [Pring](https://github.com/1amageek/Pring): Firestoreでモデルを扱うのにめちゃくちゃ重宝しました。今はdepricatedで[Ballcap](https://github.com/1amageek/Ballcap-iOS)を使うと良いみたいです。
- [HGCircularSlider](https://github.com/HamzaGhazouani/HGCircularSlider): Rose Meでは時間を視覚的に扱いたかったので採用しました。
- [RxSwift](https://github.com/ReactiveX/RxSwift): 言わずもがな
- [IGListKit](https://github.com/Instagram/IGListKit): タイムラインの差分更新するのに使用しました。
- etc...

### アーキテクチャ
開発速度を重視したかったのでMVVMを採用しました。

MVVMを触ったことがなくこれもイケるやろ！とおもって採用したら、結局MVCとMVVM足して割る2みたいなコードができあがりました。ホクホク。

# 苦労したこと

### Cloud FunctionsがNode.jsのみ対応
GCP版のCloud Functionsを使えばNode.js・Python・Goに対応していますが、Firebase版ではNode.jsのみです。
これに気づかずTypeScriptを1から勉強して書きました。

ただし僕は、TypeScriptで書くことをオススメします。
動的型付けはかなり辛くて、ドキュメントとエディタの行き来で1日が終わります。

### Cloud Functionsをローカルで実行できなかった
Firestoreの変更をトリガーにして処理をするFunctionを書いていました。
そのためデプロイして、Firestoreのフィールドを弄って、ログを見て正しく実行されているかチェックしていました。

このせいで無駄なデプロイを100回はした気がします。

ローカルエミュレータなるものがあるみたいですが、今回は諦めました。
https://firebase.google.com/docs/functions/local-emulator?hl=ja

### クライアント側にFirestoreの操作を記述する必要がある
クライアント側でクエリを構成してFirestoreからのフェッチ処理を記述しているので、Android版やWeb版を作る際に通信ロジックをそれぞれに記述しなければならないことが大変。

もしマルチプラットフォームを見据えているのであればCloud FunctionsでREST APIを構成したらいいのかもしれない。

### Firestoreの監視による無限ループ
これはあるあるかもしれないですが、Firestoreでの変更を監視して、そのドキュメントに対して何か書き込みをした場合、再び`onUpdate`が呼ばれ無限に書き込みをしてしまうことがありました。

RxSwiftでは、適切に`distinctUntilChanged()`を噛ませないと大変なことになります。(アプリも、請求金額も)

### テストデータを手作業で突っ込んでいた
日時を指定してデートを募集するアプリであるため、タイムラインに過去のデートは表示されない仕組みになっています。
(Develop環境は過去のデートを表示しても良かったですが、そこまで頭が回りませんでした)

毎回相方とアプリ内で操作して、募集を作って、ローズを送りあってマッチしてました。

最初の段階でCloud FunctionsでHTTPメソッド叩くだけでテストデータが生成されるような仕組みを作っておいた方がいいです！
絶対に！！

### Appleの審査で必ずやりとりをしなければならなかった
合計5回ぐらいAppleに審査を出したんですが、毎回英語でメッセージのやり取りがありました。

毎回テストアカウントを用意して登録フローからレビューしてもらうのですが、

「アプリの全ての機能が使えない。」

と言われました。

というのも、年齢確認が完了していないと投稿や応募ができないため審査ができないとのこと。

やり取りした後も1-2日待たされて、審査に4日はかかります。

テストアカウントだけは年齢確認のフローは飛ばせるようにした方が審査は圧倒的に楽になります！

### インターネット異性紹介事業の届出
個人的に何よりも辛かったです。

書類を10個以上用意して警察に持っていくんですが、京都ではインターネット異性紹介事業の届出の前例がなく受理できるかわからないと言われ、3日ほど待たされました:zipper_mouth:
Webアプリじゃないから固定のURLがないのでダメという理由の一点張りで、何を言っても通じませんでした。
奥の方からITに詳しい人が出てきましたが、「GCP...？」「ハッピーメールと一緒？」と言われ悲しい気持ちになりました。

# よかったこと

### Cloud Firestoreが正式リリースした
2019年2月にFirestoreの正式リリースが行われ東京リージョンができました。(asia-northeast1)
新規開発をする人は、Realtime DatabaseじゃなくてFirestore一択で!!

### オンライン/オフラインをあまり気にしなくていい
端末自体にFirestoreのキャッシュを持っているので、キャッシュに対してクエリを実行することができます。

なので、オフライン時に投稿やプロフィールの変更を行ったとしても、次にオンラインになった時に書き込みをしてくれるのでオフラインのハンドリングに神経質にならなくてもよくなりました。

### Hostingが簡単すぎる
年齢確認をするために、WebアプリをReactで作成しました。
`Buid`ディレクトリを指定してdeployするだけで`https://{project-id}.web.app`が生成されます。

こんな感じのWebアプリを作りました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264666/593edbbb-43dc-ceb2-659e-fdabe9941942.png" width=400>

自分たちで地道に1件1件、年齢確認をしていきます。笑

### Pringが神
例えばこれだけでメッセージのスキーマが作れます。
開発者の@1amageek さんに感謝しかありません。

``` Swift:Message.swift
import Pring

@objcMembers
class Message: Object {
    dynamic var text: String?
    dynamic var senderRef: Reference<User> = Reference()
}
```

# 結局Firebaseはどうなのか
開発スピードとスケーラビリティを重視するのであれば自信を持ってオススメします！

iOSエンジニアしかチームにいなくて、誰もFirebaseを使ったことがなくても全然なんとかなります。

Firebaseとの連携作業も慣れれば10分で終わります。

# ひとこと
今日の朝シャワーを浴びながら思ったことを書こうと思います。

技術しか知らなかった僕が去年の6月に会社を立てて、マッチングサービスを出す！
と意気込んでからちょうど1年がたったのかとシャンプーをしながらしみじみと感じていました。

思えばDeNAのインターンに行ってからユーザーのニーズを考え抜いてサービスを作る大事さ、楽しさを知ったなと思ったり。

デザイナーさんを雇うお金もないのでダサいUIだったのですが、たまたま東京で会うことのできたデザイナーさんにしつこく画面を見せてレビューしてもらったり。(あの時は本当にありがとうございます。)

男だけで恋愛バラエティをみてこんなデートいいなぁ〜っていいながら着想を得たり、毎日大学にこもって開発したり。

「マッチングはレッドオーシャンだから難しい。」
「試着できるデートは斬新で面白そう。」
「ネットで出会うなんてあり得ない。」
と色々な意見をもらって、自信がついたり無くしたりを繰り返していました。

走馬灯のように色々思い出す中で、どれだけの人が自分たちのサービスを使ってくれるのか、パートナーが見つかるのか、不安とドキドキでいっぱいです。

Qiitaにそぐわない記事になってしまいそうなので、ここらへんにしておきます。

名前は伏せさせていただきますが、Rose Meを考案するにあたって様々な方にアドバイスを頂き、無事リリースすることができました。
本当にありがとうございます!