保護猫好きになったので1週間でFlutter/Firebaseフル活用して無料アプリを作った話


概要

コロナの影響でリモートワークになったタイミングで、保護猫を飼い始めました。

これまで犬派だったのに、いつの間にか虜になってしまいました。
気づいたら保護猫専用アプリまで作り出したのでその技術的な紹介をしたいと思います。

構成

個人で時間も限られているのでmBaaSをフル活用し、Firebase + Flutterにてアプリを構成することにした。

  • Flutter 2.0.3
  • Firebase Analytics
  • Firebase Authentication
  • Firebase Cloud Firestore
  • Firebase Storage
  • Firebase Hosting
  • Firebase Crashlytics

Flutterはちょっと触ったことあったが、かなり良さげでOSSも充実している模様。
Firebaseとの連携はFlutterFireを利用したらめちゃ楽でした。(さすがGoogle)
※ Remote Configも入れたかったが開発時点(2021/04)ではバグのため導入ができなかったため断念

Flutter

マルチプラットフォームでアプリ開発する上でとてもよかったです。
DartもJS Likeでとっつきやすく、iOSのstoryboardやらXcodeの重さに苦労しまくっていた自分としては神がかったツールでした。(もうあの頃に戻れない)

ドキュメントを順番に読んでいくと理解しやすいと思います。

FlutterFire

FlutterとFirebaseの連携のためのプラグインでドキュメントが充実しています。
導入手順がわかりやすいので入れたい機能をそのまま対応するだけで基本問題なかったです。

Firebase Authentication

今回SNSログインを入れるため、Twitter,Google,Apple認証をそれぞれ導入しました。
マルチ認証(1ユーザーに複数SNS連携)もサポートしており、それぞれの導入手順(SNS側の設定)も記載があったのでそこまで困らなかったです(Twitterを除いて)

Twitterは公式がOSSから撤退している関係上、FlutterFireでの説明ではうまく導入できませんでした。
筆蹟時点でも最終更新が2019年...
色々試した結果flutter_twitterを元にForkした自作パッケージを利用することにした

pubspe.yaml
#  flutter_twitter_login: ^1.1.0  # ビルドが通らなかった
#  flutter_twitter: ^1.1.3        # ビルドは通るが、UIWebviewを利用しApple申請できない
  flutter_twitter:
    git:
      url: https://github.com/ateliee/flutter_twitter_login

Twitterアプリの申請はこの辺りを参考にするとすぐに利用できました。

Firebase Cloud Firestore

Firebaseの新しいNo SQL DBがいつの間にかリリースされていたので導入してみた。
Realtime Databaseに比べて多少とっつきにくかったが、比較的簡単に導入できます。
Indexやトランザクション処理もサポートしており、今の所良さげ
RDBに慣れ切っている自分としては、スキーマ構成が一番悩ましかったです(作っては消してを繰り返す)
どんなものでも入れる反面、初期のスキーマ設計はとても重要になる印象でした。

Firebase Storage

写真の保存先に利用。5GBまでの制限化で無料で使える。
保存先のパスをFirestoreに保存するのだが、合わせてurlも保存してあげる
(Firebase Functionを利用すればパスから自動で付与できるが、有料プランに切り替える必要があったので今回はクライアントで設定してあげた)

イメージとしては下記みたいな感じ

    String userId = FirebaseAuthService().currentUser().uid;
    var result = await FirebaseCloudStorageService().upload(
        '$dir/$filename',
        bytes,
        SettableMetadata(
          contentType: mimeType,
          customMetadata: {
            'orgName': orgFilename,
            'userId': userId,
          },
        )
    );
    var doc = FirebaseFirestoreService().collection('users').doc(userId);
    value.update({
      'thumbnail_path': result,
      'thumbnail_url': await FirebaseCloudStorageService().downloadUrl(result),
      'thumbnail_width': uploadFile.metadata.width,
      'thumbnail_height': uploadFile.metadata.height,
      'updated_at': DateTime.now(),
    });

Firebase Hosting

これもCLIで簡単にデプロイできる
手動で実行する場合、自分はfirestone,storageのruleなども合わせて利用しているのでhostingのみをデプロイする

# ログイン
firebase login
# 初期化
firebase init
# デプロイ
firebase deploy --only hosting

Firebase Crashlytics

クラッシュレポートツールだが、お金をかけれるならsentryのほうが良さげ。
今回は無料で導入も簡単であったため入れておく(少し前までFirebaseはFabricを利用していたが、サポート終了のため混同しないよう注意)

Jenkins

fastlane使ってみたいなーと思いつつ、環境があったのでJenkinsを利用。
ビルドからApp Store Connect,Google Play Consoleへ自動アップロードする仕組みも用意があったのでそれを利用した。

Flutterの便利そうなところ

flutter_native_splashを利用してスプラッシュ画像を更新

flutter pub run flutter_native_splash:create

flutter_launcher_iconsでアプリアイコンを更新

flutter pub run flutter_launcher_icons:main

font_awesome_flutter

FontAwesomeのFlutterバージョン。アプリが1ランクアップする。

wechat_assets_picker

multi_image_pickerを使おうとしたが、開発終了していたため代用で利用
multi_image_pickerを利用する場合、開発時点ではmetadataでエラーになったが利用したい人は下記で使えるかと思います。

pubspec.yaml
  multi_image_picker:
    git:
      url: git://github.com/Sh1d0w/multi_image_picker.git

logger

ログ周りで重宝するが、もっといいのないかな...
UI部分のlogger_flutterは開発が止まっているので使えるものを利用する

pubspec.yaml
  logger: ^1.0.0
  logger_flutter:
    git:
      url: https://github.com/etq-labs/logger_flutter

provider

Widget間でのデータの共有に利用している。とにかく難しい...

Flutter Material Theme editor

アプリのテーマエディターとして有能そうなのですが、開発時点ではクリック数回でフリーズするので利用できなかった。
かなり良さげなので今後に期待。

Flutter Gallery

基本的なUIが揃ってるので、ほぼコピペから開発できます。

Flutter Delivery Template

デザインの参考にしようとしたが、全くの別物に...ただ、テンプレートがおしゃれ

flutter_sliding_tutorial

おしゃれなチュートリアル画面が簡単にできました。

ハマったところ

iOSリリースビルド時のエラー

開発時点でiOSのビルドをすると下記のようなエラーが表示されました。

       Undefined symbols for architecture armv7:
         "_OBJC_CLASS_$_FIRComponent", referenced from:
             objc-class-ref in FIRExperimentController.o
         "_OBJC_CLASS_$_FIRComponentType", referenced from:
             objc-class-ref in FIRExperimentController.o
         "_OBJC_CLASS_$_FIRDependency", referenced from:
             objc-class-ref in FIRExperimentController.o
         "_FIRLogDebug", referenced from:
             -[ABTConditionalUserPropertyController clearExperiment:variantID:withOrigin:payload:events:] in ABTConditionalUserPropertyController.o
             -[ABTConditionalUserPropertyController setExperimentWithOrigin:payload:events:policy:] in ABTConditionalUserPropertyController.o
         "_OBJC_CLASS_$_FIRApp", referenced from:
             objc-class-ref in FIRExperimentController.o
         "_FIRLogError", referenced from:
             _ABTDeserializeExperimentPayload in FIRExperimentController.o
         "_FIRLogInfo", referenced from:
             -[ABTConditionalUserPropertyController setExperimentWithOrigin:payload:events:policy:] in ABTConditionalUserPropertyController.o
             _ABTExperimentsToSetFromPayloads in FIRExperimentController.o
             _ABTExperimentsToClearFromPayloads in FIRExperimentController.o
             -[FIRExperimentController updateExperimentConditionalUserPropertiesWithServiceOrigin:events:policy:lastStartTime:payloads:completionHandler:] in FIRExperimentController.o
             -[FIRExperimentController latestExperimentStartTimestampBetweenTimestamp:andPayloads:] in FIRExperimentController.o
       ld: symbol(s) not found for architecture armv7
       clang: error: linker command failed with exit code 1 (use -v to see invocation)

FirebaseCoreがうまくいっていないらしいが、armv7は今回不要だと思ったので下記のようにarm64のみに修正

スクリーンショットの作成

APPLAUNCHPADがよさそうでしたが、無料だと制限が厳しそうな印象でした。
普段はSketchを利用しているのでSketchToAppStoreを元にスクリーンショットを作成しました。
他にもshotsもいいかもしれません。

カラーバランスはmaterialuiを参考にしました。

LPの作成

bootstrapを利用したフリーテーマnew-ageを元に作成しました。

申請周り

Apple審査時の苦労話は下記にまとめました。

完成品

とりあえず、一通りの機能もできたのでリリースしてみた。

期間としては開発1週間 + 申請周り等1週間程度でとても簡単にできました。(と、実は申請周りで手間取り2週間かかっている)

もしよかったら利用してみてください。