ツイキャス主専用マッチングiOSアプリ「キャスコン」をつくってリリースしてみた

今年1月くらいに[ツイキャス新API](http://twitcasting.tv/indexapiv2.php)が公開され、それに伴って先日開かれた[ツイキャスハッカソン](https://connpass.com/event/48266/)を遠くから見ていて感化されてしまい、**ついかっとなって作って、リリースしました**。
ツイキャスAPIを使ったものだったらネタは何でも良かったんですが、ツイキャス主が配信をしていない**配信外の時間**に新しいキャス主を見つけて仲良くなってもらえるといいかなと思って、マッチングアプリにしてみました。

# キャスコン
![ストアサンプル.png](https://qiita-image-store.s3.amazonaws.com/0/20014/0062c6a5-2555-f898-2453-c911dce33b09.png)

[ダウンロードはこちら](https://itunes.apple.com/jp/app/%E3%82%AD%E3%83%A3%E3%82%B9%E3%82%B3%E3%83%B3-%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%9E%E3%83%83%E3%83%81%E3%83%B3%E3%82%B0%E3%82%A2%E3%83%97%E3%83%AA/id1206641725?mt=8)

# 構成
- Swift3
- Firebase
- Node.js

# 開発中のトピック
## ツイキャスAPI
ツイキャスアカウントでログインして、ユーザーのプロフィールや過去の配信画像を取得するのに利用しました。APIv2はまだベータ版で、これからも更新ありそうな気がするけど、**ツイキャス配信用のURL(RTMP)を取得**できたり、**新着のライブをリアルタイムに WebSocket 経由で取得**もできたりと、実はできること結構あって作れるモノの幅は広いと思うので**オススメ**です。[PR]

## Firebase Authentication
Firebase認証では、

- パスワード認証
- Facebookログイン
- Twitterログイン
- GitHubログイン
- Googleログイン
- カスタム認証

に対応していて、今回はツイキャスでログインを実装するために、**カスタム認証**を利用しました。ツイキャスのアクセストークンの検証と、ユーザーに対応するFirebaseカスタム認証トークンを生成するために**別途サーバーが必要**になるので、nodeでサーバーを立てて対応しました。(カスタム認証でない場合は必要ありません)

Firebase AuthでLINEログインを実装した記事があったので、そちらを参考にさせてもらいました。
[Authenticate your Firebase users with LINE Login](https://firebase.googleblog.com/2016/11/authenticate-your-firebase-users-with-line-login.html)

ちなみに、今回やりたいことをやるためには、サーバー側に管理者権限のあるFirebase `admin` SDKを入れる必要があり、サービスSDKの方を入れてて少しハマりました…
[サーバーに Firebase Admin SDK を追加する](https://firebase.google.com/docs/admin/setup?hl=ja)

## Firebase Realtime Database
アプリのチャットやマッチ機能を実装するのに利用しました。**NoSQLデータベース**で、あまりしっかり使ったことがなかったので、先にwebアプリとして実装してから、それをベースにアプリに適応しました。
非同期でデータを取るのに適していて、チャットなどはかなり楽に実装できました。

```
// messageをリッスンして取得する
let handleRef = messageRef.observe(.childAdded, with: { (snapshot) in
    let messageDict = snapshot.value as? [String: Any]
```

不安があるとすれば、[Firebase Status Dashboard](https://status.firebase.google.com/)を見ると、ちょいちょい気軽に落ちたりしてることですかね…

## Firebase Cloud Messaging
通知機能もFirebaseにあるということで使ってみました。別途サーバーを立てる必要があるのですが、使い勝手は良かったかなと思います。
(細かいですが、通知を送る際に、`priorityをhighにしておかないとバックグラウンドへ行った時に通知が届かない`というのは少しわかりにくかったです。)
あと、Firebaseのコンソールから端末やグループに通知を送れる機能も使えます。

サービスとしては良かったんですが、`APNs 証明書をFirebaseにアップロードできない`というバグがあって、サポートに連絡して対応してもらったのですが、解決に3週間くらいかかってて、そこはつらみありました。

## OneSignal
結局Firebaseサポートの解決を待てなかったので、評判の良さそうな[OneSignal](https://onesignal.com/)を使いました。
**完全無料**で、APNｓ証明書周りの取得・アップロードも自動化を試みていたりと、良い感じです。コンソールから通知送れる機能や、通知のA/Bテスト機能もあります。アプリを入れてる端末名称・国・アプリバージョンなども取れるので、それを元にセグメント化して通知の使い分けもできます。

## Firebase Analytics
イベント取得やコホート、セッション、エンゲージメントと一通りデータを取れます。BigQueryとつなげることで、より使い勝手がよくなる模様ですが、これからいじってみようと思います。

# その他
ここ1,2年、仕事ではビジネス側のマネジャー業務をやっており、swiftは初学者でしたが、早く出すぞの精神でアプリ開発自体は3週間くらいでできたのですが、アプリ初心者にとっての難関は**アップルの審査**でした。（早く出すぞの精神が裏目に出て、抜け漏れがありました…）リジェクトされること複数回、**約6週間後**にやっと審査が通りました。代表的なリジェクト理由を挙げますと、

#### 1. アプリタイトルに価格に言及したものを含めてはいけない
例によって、アプリタイトルに「無料」とか入れてたんですが、審査でダメになったみたいですね。Twitterでも報告してる方がちらほらいました。
<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">アプリ名称に「無料」が含まれるという事でリジェクトされた件、Appleの審査部門に電話しました。先方の説明としては下記４点の趣旨でした。<br>１）直近、審査基準にアップデートがあり、無料等の価格を含められなくなった。</p>&mdash; 櫻田晋一 アプリ開発会社9期目TL35黄 (@sakuradasb) <a href="https://twitter.com/sakuradasb/status/841830605167910912">March 15, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

#### 2. IPv6対応できてないぞ
ログイン認証や通知でAPI叩いてるところのサーバー側で、IPv6対応できてませんでした。自サーバーの方は大人しくすぐ対応したのですが、利用しているツイキャスAPI側はIPv6対応されていないけど、IPv6環境でアプリを試してみるとたしかに正しく動作したり、とちょっとよくわかっていません。

#### 3. User-Generated Contentの対応をしなければならない
規約だとこちらですね。
[1.2 ユーザー生成コンテンツ](https://developer.apple.com/app-store/review/guidelines/jp/#user-generated-content)
ユーザーがコンテンツを投稿できるアプリの場合はこの基準を満たしていないといけないということですね。

- 利用規約を用意
- 通報・ブロック機能を実装
- 通報・ブロックを即時キャッチできる管理画面を作成

で、対応しました。

# まとめ
色々初めてのものを実装してみて楽しかったです。
需要があれば、改善もしようと思うので、良かったらアプリ試してみて下さい。
[キャスコン](https://itunes.apple.com/jp/app/%E3%82%AD%E3%83%A3%E3%82%B9%E3%82%B3%E3%83%B3-%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%9E%E3%83%83%E3%83%81%E3%83%B3%E3%82%B0%E3%82%A2%E3%83%97%E3%83%AA/id1206641725?mt=8)