Firebaseを活用したiOSアプリ開発事例

こんにちは。新規サービス開発部の中村です。

最近Komercoで販売されている鉄のフライパンが欲しいです。クリエイターさんたちの作品は見ているだけで本当に楽しいですね。

そんなKomercoはバックエンドにFirebaseを活用していますが、実は弊社からKomercoの他にもFirebaseを活用したサービス「Cookin'」をリリースしています。

本稿ではCookin'のFirebaseを活用した事例についてご紹介します。

Cookin'とは

Cookin'は料理動画撮影アプリです。手順ごとに3秒間取るだけで1本の料理動画が投稿できるサービスとして、2017年12月にiOSアプリとしてリリースしています。

このサービスの特徴は、簡単に料理動画が作成できるほかに、投稿から斬新なアイデアを得られたり、コメント欄から料理のコツやポイントを気軽に質問できるところです。

料理をしながら動画撮影するのは難しいですが、慣れると楽しいです。

App Store リンク

誠に勝手ではございますが、Cookin’は提供サービスの見直しにともない、2018年7月末をもちまして、提供を終了いたします。ご利用のお客様には、ご迷惑をおかけいたしますことを深くお詫び申し上げます。サービス終了に関するご案内はこちらをご覧ください。

FIrebaseの活用事例

先ほど述べた通り、Cookin'のバックエンドはFirebaseを活用しています。Firebaseには様々なプロダクトが用意されていますが、Cookin'では以下のプロダクトを活用しています。

アプリの開発とテスト ユーザー層の拡大と利用促進
Authentication Cloud Messaging
Cloud FIrestore Google Analytics for Firebase
Cloud Storage
Cloud Functions

以降ではこれらの概要と活用事例をご紹介します。

Authentication

Authenticationは安全な認証システムを提供しています。メールアドレスとパスワードの組み合わせ、電話番号認証、匿名認証、Google、Twitter、Facebook、Githubのログイン等をサポートしています。

活用事例

ユーザーの認証に匿名認証を活用しています。これにより、データベースやストレージのセキュリティーを堅牢にしつつ、初回起動時のアカウント作成プロセスを省略することでユーザーの離脱を防いでいます。

匿名ユーザーは必要に応じてメールアドレスとパスワードでの認証に切り替えることも可能です。

匿名ユーザーのアカウントを停止したい場合は一手間必要になります。詳細はこちらのブログ記事をご覧ください。

Cloud Firestore

Cloud FirestoreはNoSQLドキュメントデータベースです。オフラインの場合でもデータにアクセスして変更を加えることができ、オンラインに復帰すると自動的に変更したデータを同期します。スケーリングは自動的に行われ、セキュリティールールを書くことでセキュリティーを堅牢にできます。

活用事例

  • 投稿情報、コメント、ユーザー情報等を保存しています。
  • 投稿画面に閲覧しているユーザーのアイコンをリアルタイムで表示しています。このようなリアルタイム機能を素早く実装できることも大きな特徴です。

    閲覧ユーザーアイコンのキャプチャ

  • ユーザーページの投稿一覧は、クエリ機能を利用して全体の投稿から特定のユーザーの投稿を取得して表示しています。また、クエリを利用してソートやページングを実装しています。

    全体の投稿から特定のユーザーの投稿を取得するクエリの例

    postsRef.whereField("authorID", isEqualTo: user.id)

    ユーザーページのキャプチャ

現時点ではCloud Firestoreにバックアップ機能が提供されていないため、バックアップする場合は自前で行う必要があります。

Cloud Storage

Cloud Storageは写真や動画等の容量が大きなファイルを保存できます。Cloud Firestoreと同様オフラインサポートと高いスケーラビリティを備え、セキュリティールールを書くことでセキュリティーを堅牢にできます。

活用事例

  • 動画やサムネイル画像を保存しています。

  • セキュリティールールの例を上げると、Cloud Storageにアップロードされる動画のファイルサイズを1MB以下に制限するために、下記のセキュリティールールを書いています。

セキュリティールールの例

service firebase.storage {
  match /b/{bucket}/o {
    match /version/1 {
      match /video/{videoID}/file/{file} {
      
        // 認証済みユーザーのみ動画ファイルの読み込みが可能
        allow read: if request.auth != null;
        
        // 動画ファイルが書き込まれる際の条件
        allow write: if (request.auth != null && request.resource == null)
                     || (request.auth != null &&
                     // 1MB 以上の動画ファイルは許可しない
                     request.resource.size < 1 * 1024 * 1024 &&
                     request.resource.contentType.matches('video/.*'));
      }
    }
  }
}

Cloud Functions

Cloud FunctionsはCloud FirestoreやCloud Storageへのデータの追加や変更、またはHTTPSリクエストによりトリガーされたイベントに応じてバックエンドコードを自動的に実行できます。

活用事例

  • プッシュ通知やSlackへのメッセージ送信に活用しています。例えば、ある投稿に新しいコメントが書き込まれたときに、投稿者にコメントが書き込まれたことをプッシュ通知で知らせたり、アプリから不適切な投稿が報告されたときにSlackにその報告を流しています。

    Cloud Functionは1つのイベントを元に複数回トリガーされることがあるため、関数は何回実行されても問題ないように実装しておく必要があります。詳細はこちらのブログ記事をご覧ください。

  • 関数のディレクトリ構造・命名は以下のようにしています。これにより、コードを修正をするときにどのイベントから実行される関数なのか把握しやすくしています。

それぞれのイベントから実行される関数が明確になります。

├── functions
│   ├── auth
│   ├── db
│   │   ├── comment
│   │   │   └── onCreate.ts
│   │   ├── feedback
│   │   │   └── onCreate.ts
│   │   ├── post
│   │   │   └── onCreate.ts
│   │   └── report
│   │       └── onCreate.ts
│   └── storage
├── index.ts
├── test

どのイベントから実行される関数なのかファイルの中を確認しないと分からないため把握しにくいです。

├── functions
│   ├── notifyPost.ts
│   ├── notifyComment.ts
│   ├── notifyReport.ts
├── index.ts
├── test

関数のテスト

テストフレームワークはJestを利用しています。

テスト方法はオフラインモードとオンラインモードの2種類あり、オフラインモードで行う場合、データベースの書き込みを全てスタブしなければならないため、オンラインモードで行っています。

オンラインモードではデータベースへの書き込みやユーザーの作成などが実際に行われ、テストコードがその結果を検査できるように、テスト専用のFirebaseプロジェクトとやり取りするテストを作成します。

注意点として、Firebase Test SDKのfirebase-functions-testnpmモジュールをテスト専用のFirebaseプロジェクトの構成値で初期化した後、メイン関数ファイルをモジュールとしてインポートしなければなりません。

この順序を守らないと予期しないFirebaseプロジェクトとやり取り(書き込み・読み込み)が行われてしまうことがありました。

防止策として、JestのsetupFilesのタイミングでfirebase-functions-testnpmモジュールを初期化するようにしています。

Cloud Messaging

Cloud Messagingは、iOS、Android、ウェブ(JavaScript)クライアントアプリに通知メッセージを送信することができます。

活用事例

Cloud FunctionsからAdmin FCM APIを利用してプッシュ通知を送信しています。

ユーザーが料理を始めたことを知らせる通知自分の投稿にコメントが届いたことを知らせる通知の2種類を送信しています。

Google Analytics for Firebase

Google Analytics for Firebaseは、最大で500種類のイベントに関するレポートを無料で無制限に生成できます。Firebaseのコンソールからダッシュボードを見ることができます。

活用事例

ユーザーの動画閲覧から動画投稿までのファネルを作成して活用しています。

その他のライブラリ

以降はFirebaseの他に活用しているライブラリの一部をご紹介します。

Pring

データの読み込み・書き込みにPringを活用しています。

PringはCloud FirestoreとCloud StorageのO/Rマッパーで、Cloud FirestoreとCloud StorageのAPIを意識せずにデータの読み込み・書き込みができます。

Pringを活用すると開発スピードを加速させることができます。

FilterCam

iOS SDKのCore Image FIlterを適用した動画を簡単に作成することができます。

撮影した料理がより魅力的になるように彩度と中間色の明るさをやや上げたフィルターをFilterCamに適用して活用しています。

適用前(左) 適用後(右)

まとめ

本稿を通してiOSアプリのバックエンドにFirebaseを活用した事例をご紹介しました。

Firebaseは開発が活発で新しい機能が追加され続けているので、今後もより使いやすくなることが期待できます。プロダクトの性質に合わせて、うまく活用できれば大きなメリットが得られるのではないでしょうか。

最後になりましたが、冒頭に記載しました通り、Cookin'は7月末をもちまして提供を終了いたします。これまでCookin'を支えてくれた皆さまに感謝申し上げます。ありがとうございました。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/