ミステリーデートのシステム構成

ミステリーデートというアプリをリリースすることになったのですが、

開発の観点から、システム構成を書いておきます。

サーバーシステム

DBはFirebaseのFirestoreにおきました。Firebaseは前から使っていたけど、Firestoreを使うのは今回が初めて。
APIを作ろうと思ってFirebaseに紐づけてGAEの設定もしたのですが、Queryかけてデータを返すだけだったらクライアントからやったほうが楽!
Firestoreすごい。
とうわけで用意していたGAEは管理画面のみで利用することにして、アプリからアクセスする分はすべてFirestoreとCloud Functionsでやることになりました。

iOSのクライアントアプリ

Podfileの中身はこんな感じです。

platform :ios, '11.0'
use_frameworks!

def install_pods
	pod 'SVProgressHUD'
	pod 'JSONModel'
	pod 'SDWebImage'
	pod 'FirebaseUI/Auth'
	pod 'FirebaseUI/Storage'
	pod 'Firebase/Firestore'
	pod 'Firebase/Core'
	pod 'Firebase/Messaging'
	pod 'Firebase/Storage'
	pod 'Firebase/Analytics'
	pod 'Fabric'
	pod 'Crashlytics'
	pod 'GoogleMaps'
	pod 'R.swift'
	pod 'Stripe'
	pod 'RxSwift'
	pod 'RxCocoa'
	pod 'RxFirebase/Firestore'
	pod 'RxFirebase/Auth'
	pod 'RxFirebase/Functions'
	pod 'JTAppleCalendar', '8.0.0'
	pod 'RealmSwift'
	pod 'EAIntroView'
	pod 'Zoomy'
	pod 'libPhoneNumber-iOS', '~> 0.8'
	
	script_phase :name=> 'Fabric',
	:script=> '"${PODS_ROOT}/Fabric/run"',
	:input_files=> ['$SRCROOT/$PRODUCT_NAME/$(INFOPLIST_PATH)']
end

target "mysterydate" do
 install_pods
end

target "mysterydate staging" do
 install_pods
end

stagingとproductionで構成を切り替えるのと、Fabricのやり方がちょっと面倒でした。
Rx系はいちおう使ってるけど、そんなに必要なかったかな。
あと、JSONModelはCodableでも良かったなと後で思いました。
言語はSwiftです。

管理画面の構成

管理画面はとりあえずは自分しか使わないのですが、勉強のためにGAE上でNuxt.jsで作りました。ここはハマりが多かった!

FirebaseはGAEと融合しているようなので、GAEのDatastoreの関数でそのまま紐づいているFirestoreが操作されるそう。でもこれがどうやってもDatastoreの関数ではうまく初期化すらできませんでした。
なのであきらめて普通のFirestoreの関数でアクセスすることにしました。
ローカルでデバッグする時もそのほうが楽だし、最初からそうしておけばよかった・・

 const { Datastore } = require('@google-cloud/datastore')
fallback.js:75 Uncaught (in promise) Error: {}You need to pass auth instance to use gRPC-fallback client in browser. Use OAuth2Client from google-auth-library.
   at new GrpcClient (fallback.js:75)
   at Object.<anonymous> (index.js:38)
   at Object../node_modules/@google-cloud/datastore/build/src/index.js (index.js:856)
   at __webpack_require__ (bootstrap:790)
   at fn (bootstrap:150)
   at _callee2$ (index.js:1)
   at tryCatch (runtime.js:45)
   at Generator.invoke [as _invoke] (runtime.js:271)
   at Generator.prototype.<computed> [as next] (runtime.js:97)
   at asyncGeneratorStep (asyncToGenerator.js:3)

ただし、その際もちょっとだけ問題が。
Nuxtはサーバーモードとクライアントモードとあるのですが、普通にFirebaseのインポートすると

These dependencies were not found:
* @firebase/app in ./node_modules/@firebase/database/dist/index.cjs.js
* dns in ./node_modules/@grpc/grpc-js/build/src/resolver-dns.js
* http2 in ./node_modules/@grpc/grpc-js/build/src/server.js

To install them, you can run: npm install --save @firebase/app dns http2 

みたくなってうまくいきません。

if (process.server) {
   var admin = require('firebase-admin')
   if (!admin.apps.length) {
       admin.initializeApp({
           credential: admin.credential.applicationDefault(),
           databaseURL: process.env.firebaseConfig.databaseURL
       })
   }
}

のように、サーバーモードの時だけ処理を分けることで対処しました。

StagingとProductionでGoogleのCredentialを分ける

ミステリーデートで予約が入ると、店舗ごとのGoogleカレンダーに追加されるようにしたのですが、Cloud FunctionsからGoogle Calendar APIを呼ぶ時に利用するCredentialをStagingとProductionで切り替えるのを工夫した結果、環境変数を使う方法に落ち着きました。
やり方としては、

firebase functions:config:set --project mystery-date credentials.client_id="xxxxxx.apps.googleusercontent.com"
firebase functions:config:set --project mystery-date credentials.client_secret="xxxxxxxxxxxxx"
firebase functions:config:set --project mystery-date credentials.refresh_token="xxxxxxxxxxxxx"

firebase functions:config:set --project mystery-date-staging credentials.client_id="xxxxxx.apps.googleusercontent.com"
firebase functions:config:set --project mystery-date-staging credentials.client_secret="xxxxxxxxxxxxx"
firebase functions:config:set --project mystery-date-staging credentials.refresh_token="xxxxxxxxxxxxx"

のように、一個づつ環境変数に入れました。これで、ソースからは

let authClient = new google.auth.OAuth2(
	functions.config().credentials.client_id,
	functions.config().credentials.client_secret,
	"https://developers.google.com/oauthplayground"
  );
authClient.setCredentials({
	refresh_token: functions.config().credentials.refresh_token
});

のように初期化して利用できます。
refresh_tokenの作り方はこちらを参考にしました。

その他ハック

いつも一人で開発している時は、ハマって解決してもログに残らないので、今回からSlackにハマりを書いておく専用チャンネルを作りました。
ハマった時に書いて、解決したらそのスレッドに返信するようにしておくと、今回みたいな記事もすぐ書けて便利です。

この記事が気に入ったらサポートをしてみませんか?