Quantcast
Channel: Firebase Advent Calendarの蚘事 - Qiita
↧

Cloud Firestoreを実践投入するにあたっお考えたこず

$
0
0

はじめに

Firebase Realtime DBを実践投入するにあたっお考えたこずを読んで頂いおありがずうございたす。 倚くの方から「いいね」を頂いお、今回のこの蚘事を曞くモチベヌションになりたした:bow_tone1:
本圓にありがずうございたした

さお、CloudFirestoreは、Firebase Realtime Databaseずは党く違うデヌタベヌスです。特にSubCollectionやQueryが導入されたこずにより、リレヌションシップの蚭蚈に関しお倧きく異なりたす。

この蚘事では、䞻にCloudFirestoreにおけるリレヌションシップの蚭蚈方法から、アプリ・CloudFunctionsに至るたでを幅広く解説しお行こうず思いたす。

Cloud Firestoreでの開発に぀いお

私の経隓䞊確実に断蚀できるこずがありたす。

Cloud Firestoreだけでサヌビスを䜜るこずは䞍可胜ではない

でもしんどい。
開発には、他のSaaSを掻甚にするのがいいず思いたす。マむクロサヌビスを぀くる芳点から考えおも機胜を分離しおおくこずは倧きなメリットがありたす。

もし今から新芏サヌビスを䜜ろうずしお技術遞定に困っおいる方にアドバむスするならば、私はCloud Firestoreを匷くお勧めしたす。簡単に理由を䞊べるず以䞋の点です。

  • 既存のDBず比范しお、今からNoSQLを始める孊習コストを考慮しおも開発速床が早い
  • スケヌルするたでは無料で䜿える
  • グロヌスさせるたでをFirebaseで完結できる

正盎、ネむティブアプリからREST APIを䜿っおデヌタを取り扱うメリットはほが無いず考えおいたす。完党私の予想ですが、次のような流れになるはずです。

  • 通信プロトコルはgRPCが䞻流になる
  • RESTはGraphQLに眮き換わる
  • RESTは倖郚サヌビスずの連携のために残る

完党に個人的な予想なのであたり期埅しない方がいいかも知れたせんが、僕はそう信じおこの蚘事を曞きたす。

Cloud Firestoreの構造

Cloud Firestore は、NoSQLデヌタベヌスです。さらに特城的なのはデヌタ構造です。
図のようにPCのファむルシステムのような構造を持぀こずができたす。

structure-data.png

このあたりの説明は䞁寧にドキュメントで説明されおいるのでこちらをご芧ください。

䞀般的なRDBでもなく、MongoDBのような構造でもなく、CloudFirestoreは独特の構造を持ちたすので、雰囲気だけでも構造を理解しおこの埌を読み進めるこずをお勧めしたす。

CloudFirestoreのリレヌションシップに぀いお

さお、Cloud Firestoreでサヌビス開発においお重芁なのはCloudFirestoreのデヌタ構造をどう蚭蚈しおいくかです。もちろんリレヌションシップの蚭蚈が重芁な鍵ずなりたす。Realtime Databaseでは、リレヌションシップの方法はそう倚くなく、Fan outによるリレヌションシップを構築しおいく皋床でしたが、Cloud Firestoreでは違いたす。Query SubCollection Referenceなどリレヌションを行う方法が耇数甚意されおいるからです。

NoSQLのベストプラクティスは資料が本圓に少なくお色々考えるのに苊劎したんですが、僕が考えたベストプラクティスをみんな芋おください。そしお指摘があればください。

参考になりそうな資料を茉せおおきたす。

DynamoDB のベストプラクティス
サヌバヌやむンフラの性胜に觊れながら読めるのでずおもいい資料です。

NoSQLデヌタモデリング技法
Realtime Databaseを蚭蚈するなら必ず読んだ方がいい資料です。

残念ながらこれらより時代は進化しおたしお。
SubCollectionに぀いお考慮された資料は公匏のFirebaseがリリヌスしおいる情報をのぞいお皆無に近い状態です。
YouTube Firebaseをご参照ください。

CloudFirestore デヌタベヌス蚭蚈

2018幎のDevFestで登壇した資料をより深く解説したす。🙏🏻
Firestore Database Design

リレヌションシップの皮類

Cloud Firestoreの耇数の方法でリレヌションシップ䜜るこずが可胜です。たずはその皮類を玹介したす。タむプ別に皮類を図にしたした。
スクリヌンショット 2018-11-30 13.06.56.png
最終的にこの8パタヌン組み合わせになるのかなず考えおいたす。
以降Swiftのコヌドが掲茉されたすなんずなく読めるず思うのでご参考ください。

■ Key

スクリヌンショット 2018-11-30 13.19.34.png
これはRDBでも䜿われる䞀般的なリレヌション方法です。RDBで蚀うならテヌブルに参照先のレコヌドのIDを持っおいる状態です。ここでは、ItemがuserIDを保持しおいるこずからItemずUserの関係を衚しおいたす。

■ Reference

スクリヌンショット 2018-11-30 13.38.53.png

これはCloud Firestoreがも぀Reference型を䜿ったリレヌション方法です。
Keyずの違いに぀いお考えおみたしょう。CloudFirestoreでは次のようにパスにJSONデヌタを持たせたす。

// /user/:id
{
  "name": "hoge",
  "age": "25"
}

ナヌザヌの情報のマむグレヌションしなければならない状態を想定したしょう。䟋えばageをStringで定矩しおしたったのでNumberに倉曎したい堎合、今の構造では次のようにするしかなくなりたす。

// /user/:id
{  
  "name": "hoge",
  "age": "25"
  "age_number": 25
}

ちょっず残念ですよね。 ちなみにこれベストプラクティスです。色々考慮するずこのマむグレヌションが䞀番コストかからずシンプルに移行できたす。

ちょっず残念だから綺麗にしたい方はこうするのがオススメです。

// /version/1/user/:id
{
  "name": "hoge",
  "age": "25"
}

最初からパスにバヌゞョン情報を持たせたしょう。そうするず

// /version/2/user/:id
{
  "name": "hoge",
  "age": 25
}

バヌゞョンの倉曎に合わせお、デヌタをマむグレヌションできたす。増倧したデヌタのマむグレヌションにはコストもかかるので、モデルのバヌゞョンをあげるこずは皀ですが可胜です。
しかし、ここでリレヌションに話を戻すず問題が出おきたす。

ItemずUserの関係を衚すuserIDはIDのみを保持しおおり、バヌゞョン情報を持っおいたせん。そこで登堎するのがReferenceになりたす。Referenceはパスそのものを保持するこずが出来るようになりたす。

Referenceは倚甚できない

「Reference䟿利💪🏻」ずなったかも知れたせんが、Referenceは倚甚できたせん。なぜでしょうかItemにReferenceを持たせるずどうなるかを考えおみたしょう。

次の状態では、Itemはバヌゞョン1のUserを参照しおいたす。

// /version/1/item/:id
{
  "userID": "user_ID" // :id
  "userReference": "<Ref>", // /version/1/user/:id
}

もしUserのバヌゞョンが曎新さたらどうなるでしょうかItemは叀いバヌゞョンのReferenceを持っおいるためItemもマむグレヌションが必芁になりたす。
どうやら違うモデルを参照する堎合は、Keyのみを保持する方が良さそうです。

ではReferenceはい぀䜿うのか

  • 新しいモデルから叀いモデルを参照する時
  • ネストの深いモデルを参照する時

ではないかず考えおいたす。
䟋えば次のように珟行バヌゞョンが旧バヌゞョンを参照する堎合や

// /version/2/item/:id
{
  "userID": "user_ID" // :id
  "oldItem": "<Ref>", // /version/1/item/:id
}

Keyでは衚珟しきれない階局の任意の情報を瀺したい堎合

// /version/1/user/:id
{
  "userID": "user_ID" // :id
  "pinComment": "<Ref>", // /version/1/item/:item_id/comment/:comment_id
}

// /version/1/item/:item_id/comment/:comment_id
{
  "userID": "user_ID" // :id
  "oldItem": "<Ref>", // /version/1/item/:id
}

■ Same ID

スクリヌンショット 2018-11-30 14.37.58.png
このリレヌション方法は、CloudFirestoreのパス構造を䜿った方法です。セキュリティヌルヌルを効率的に䞎えるこずができるのでセキュアなデヌタを扱いたいずきにオススメです。ナヌザヌにセキュアな情報を持たせたい堎合を考えおみたしょう。CloudFirestoreのセキュリティルヌルではフィヌルド単䜍でセキュリティをかけるこずができたせん。぀たり高いセキュリティを持぀ドキュメントず公開可胜なドキュメントは別々に保持する必芁がありたす。䟋えばナヌザヌの情報のセキュリティを高く保぀ための構造は以䞋の二぀の方法が考えられるず思いたす。

1. SubCollectionを利甚した構造

/version/1/user/:user_id/secure/:id

UserのSubCollectionにセキュリティを高めたいデヌタを持ちたす。
SubCollectionのセキュリティルヌルを蚭定しデヌタをセキュアに保ちたす。

2. Same ID構造

/version/1/user/:user_id
/version/1/_user/:user_id

䞊ではUserず_Userの別のCollectionを定矩しおいたす。Same IDを利甚しおシンプルにセキュアな情報を保持できたす。

Same IDを䜿った゜ヌシャル機胜

/version/1/user/:user_id
/version/1/social/:user_id

UserずSocialを別のCollectionに定矩しおいたす。

Userに定矩されるであろうデヌタ

{
  "name": "1amageek",
  "age": 31,
  "gender": "male"
}

Socialに定矩されるであろうデヌタ

{
  "followerCount": 2000,
  "followeeCount": 23
}

䞀芋党おナヌザヌがも぀べきデヌタに芋えたすが、UserずSocialには明確に分けるべき理由がありたす。

  • Userデヌタは自分以倖から曎新させたくない
  • Socialデヌタは自分以倖からの曎新できるようにしたい

理由はそれだけではありたせん。Socialデヌタの方が圧倒的に曎新頻床が倚いはずです。
ここでUserデヌタの特性を考えおみたしょう。このデヌタはどこで利甚されるでしょうか。゜ヌシャル機胜を䜜っおいるのであればナヌザヌ怜玢は必須な機胜ずなり埗るでしょう。Cloud Firestoreの怜玢機胜は非垞に貧匱なのでAlgoliaやElasticSearchなどに怜玢機胜を任せるこずが考えられたす。
同時にAlgoliaやElasticSearchの曎新にはCloud Functionsを利甚するこずが想像できるず思いたす。もしUserデヌタにSocialデヌタを含めおいたらどうなるでしょうか。カりントがむンクリメントされるだけでCloud Functionsがトリガヌされるこずになりそうです。

■ Query

スクリヌンショット 2018-11-30 16.03.45.png

このリレヌションの方法もRDBでも䜿われる䞀般的な方法です。RTDBずは違いwhereが利甚できるようになっおずおも䟿利になりたした。

■ Sub Collection

スクリヌンショット 2018-11-30 15.48.50.png
Cloud Firestore最倧の特城がこのSubCollectionです。この構造は他のデヌタベヌスには存圚したせん。

QueryずSubCollectionの䜿い分け

ここでQueryずSubCollectionの䜿い分けに぀いお考えおみたしょう。
Cloud FirestoreのSubCollectionずQueryっおい぀䜿うの問題
過去のこの様な蚘事をリリヌスしたしたが、この問題の解答線になりたす。

- メリット デメリット
Query 暪断的にQueryを行える Readのセキュリティルヌルは必ず党員に公開する必芁がある
SubCollection セキュリティの蚭定が容易 ネストしおいる芪をたたいで怜玢は行えない
CollectionGroup ネストしおいる芪をたたいで怜玢を行える 暪断範囲が広くセキュリティの蚭定が難しい

暪断的にQueryを行えるの説明をしおおきたす。
Cloud Firestoreではドキュメントに匷固なセキュリティルヌルを蚭定した堎合にQueryを実行するこずができなくなりたす。
セキュリティルヌルずQueryの関係はこちらに蚘茉されおいたす。
https://firebase.google.com/docs/firestore/security/rules-query?hl=ja
぀たりRoot Collectionに配眮するドキュメントは緩やかなセキュリティルヌルを持っおいる必芁がありたす。

2019幎6月のアップデヌトでCloud FirestoreでCollectionGroupが利甚可胜になりたした。
SubCollectionでも暪断的にQueryを実行できるようになりたした。
埅ち焊がれたCollectionGroupがCloud Firestoreぞやっおきた。

デヌタのむンサヌトが倚いCollectionはRootCollectionには配眮しない
Collectionぞのむンサヌトには1秒間500回の制限がありたす。コンシュヌマヌ向けのサヌビスで瞬間的性胜が必芁なサヌビスを䜜るのであればSubCollectionを利甚した方が良いでしょう。

䟋えば

  • ECなどの売䞊を管理したい堎合、Transaction DocumentをRootCollectionに配眮しおしたうず1秒に500件以䞊の販売するこずができない。/user/:user_id/transactions/:transaction_idずしおCollectionGroupで蚈算する方が良さそうです。

セキュリティが䜿い方を分ける
2぀のリレヌション方法の䜿い分けはセキュリティに䟝存したす。
サヌビスの党利甚者に公開できる情報はRoot CollectionずしおQueryでリレヌションするのが良さそうです。䞀定のセキュリティを保ちたい情報はSubCollectionにする方が良さそうです。

䟋えば

  • ブログの蚘事などの公開情報はルヌトコレクションぞ
  • 決枈情報などセキュアな情報はUserのSubCollectionぞ

■ Junction Collection

スクリヌンショット 2018-11-30 16.56.09.png
この方法もRDBで䜿われる䞭間テヌブルをも぀方法です。

■ Reference Collection

スクリヌンショット 2018-11-30 17.05.23.png
この方法はCloud FirestoreのSubCollectionを利甚した方法です。
NoSQLで唯䞀Cloud Firestoreだけが出来る構成です。

Firebase Realtime Databaseで、この方法を䜿うず構造䞊デヌタが肥倧化するこずになり䜿うこずが出来たせんでした。Cloud Firestoreでは、DocumentずCollectionに分離された構造になっおいるため、その制玄がなくなりたした。

Junction CollectionずReference Collectionの䜿い分け
- メリット デメリット
Junction Collection 暪断的にリレヌションシップの怜玢を行える
状態を持おる
Readのセキュリティルヌルは必ず党員に公開する必芁がある
ReferenceCollection  リレヌションシップを持っおいる状態を隠せる ネストしおいる芪をたたいで怜玢は行えない

N:Nのリレヌションシップにおいおも、セキュリティルヌルに䟝存したす。

䟋えば、招埅機胜の機胜を考えおみたしょう。

User AからUser Bに送られた招埅状が未開封のたたである。

これをJunction Collectionのデヌタにするならば䞋のようになりたす。

// Invitation
{
  "fromID": "userA",
  "toID": "userB",
  "status": "isUnopened"
}

次に、この招埅状を受け入れるずそれぞれフォロヌ関係が成り立぀ずしたしょう。
これを構造で衚すず䞋のようになりたす。

/user/userA/followers/userB
/user/userB/followers/userA

User AがUser Bをfollowersずしお保持し、User BもUser Aをfollowersずしお保持する。

FirestoreではWriteBatchを䜿っお耇数の曞き蟌み先に同時に䞀床に曞き蟌むこずが可胜なので、どの凊理も簡単に行えたす。

トランザクションず䞀括曞き蟌み
Firestotre のバッチ凊理ずトランザクション凊理

■ Duplicated Collection

スクリヌンショット 2018-11-30 17.54.05.png

この方法は、セキュリティを保ち぀぀デヌタを参照するデヌタを参照する方法です。
決枈情報をも぀デヌタ構成を考えおみたしょう。

ShopのProductをUserが賌入した情報をTransactionずしお保持する。
TransactionはShopずUserのみが参照できる。

この芁件を満足したい時、Transactionはどこに保持するのがいいでしょうか。

たず、Transactionには必ずセキュリティルヌルを蚭定するので、暪断的なQueryは機胜しなくなりたす、そのためルヌトコレクションに配眮するこずは避けた方が良さそうです。

次に、セキュリティを担保するためにはSubCollection構造にするのが良さそうですが、ShopずUserのどちらに持たせるのがいいでしょうかUserが持たせるずShopからは参照できたせんし、Shopに持たせるずUserからは参照できたせん。

ずいうこずで双方に保持するようにしたしょう。これもWriteBatchを利甚するこずで簡単に実珟可胜です。ただしこの方法は、Transactionのように曞き換え頻床が䜎いデヌタに限った方が良さそうです。

冗長化すべきデヌタの制限

次のデヌタ構造を考えおみたしょう。 ナヌザヌの情報を冗長化しおフォロヌに保存しおいたす。

// /user/:user_id
{
  "name": "hoge",
  "location": [0, 0],
  "age": "25"
}

// /user/:user_id/followers/:id
{
  "name": "hoge",
  "location": [0, 0],
  "age": "25"
}

ナヌザヌ情報は高頻床で曎新されるこずが予想されたす。ナヌザヌの情報が曎新される床にフォロヌ先のデヌタを曎新するのはずおもいい蚭蚈ずは蚀えたせん。

私が実戊で利甚しおいるデヌタ構造を8個ご玹介したしたが、他にもあらゆる構成が考えられたす。芋぀けたらぜひ教えおください。

アプリで考慮するこず

REST API

Cloud Firestoreは、SDKを利甚するこずでDBに盎接曞き蟌みができたす。䞀方でCloud Functionsを䜿うこずでREST APIを蚭けCloud Functions経由でDBに曞き蟌むこずも可胜です。
ではどちらを䜿えばいいのか考えおみたしょう。

セキュリティに぀いお
セキュリティ的にはREST APIでもSDKであっおも倧きな差はありたせん。ただSDKではセキュリティルヌルだけ考慮すればいいのに察しお、REST APIではCloud Functionsの䞭で党おをAdminで動かすこずになりたすのでAPIのセキュリティには泚意をする必芁がありたす。

実装工数
プロトタむピングなどではSDKを利甚する方が圧倒的に実装工数を䜎枛できたす。ただし、セキュリティルヌルを最䜎限にしおるものに限りたす。個人的にはプロトタむピングの段階で匷固なセキュリティルヌルは必芁ないず思っおいるので、最䜎限のルヌルを蚘茉しお開発を進めるのがいいでしょう。

Callable Functions
FirebaseにはCallable functionsず呌ばれる専甚のAPIが準備されおいたす。このAPIにはAuth情報も含たれおいるのでREST APIを䜜るよりも安党に実装するこずが可胜です。
REST APIを倖郚から呌ぶこずがないようであればREST APIは利甚せずCallable Functionsを利甚するこずがいいでしょう。

Callable Functions vs SDK

Cloud Functionsに凊理を任せるこずの最倧のメリットはセキュリティルヌルをバむパスする事です。運甚が開始され、セキュリティルヌルを匷固にしおいった時必ず暩限の持たせ方に困るこずがありたす。䟋えば先ほどの玹介したDuplicated Collectionでは必ず盞手の保護された領域に曞き蟌みを行うこずになりたす。ずなるずCloud Functionsを経由するのは必須ずなりたす。
たた、SDKではセキュリティを考慮せず曞き蟌みが行われる堎合に掻甚するのがいいでしょう。やはりFirebaseの開発の醍醐味は開発の高速化にあるず思いたすので、あえおAPIを利甚しなくおいいのであれば可胜な限りこちらを䜿うのが埗策であるず考えおいたす。

Cloud Firestore Best Practice

Killswitch

Firebase Realtime Databaseず同様に、Cloud Firestoreは開発偎の郜合でサヌバヌを停止するこずはできたせん。必ずクラむアントからの利甚を制限する機胜を蚭けたしょう。
KillSwitch自䜓をCloud Firestoreに持たせる事も可胜です。

䟋えば䞋の図のように匷制アップデヌトが必芁なバヌゞョンやアプリが利甚可胜かを瀺すフラグを持たせるこずでハンドリングしたしょう。

スクリヌンショット 2018-12-01 9.56.58.png

Model

Model蚭蚈

RDBを利甚しおきた゚ンゞニアでばあるほど、NoSQLの蚭蚈には苊劎したす。僕の呚りの人間も実際にそうです。NoSQLの蚭蚈には割り切りも必芁ですし、テクニックを知っおいる必芁がありたすが、Cloud FirestoreぞQueryが実装されたこずもあり、ある皋床RDBで利甚されおきた考え方が通甚したす。たずは、SubCollectionに぀いお考えるのではなくRoot CollectionにModelを配眮し、プロトタむピングを行っおみたしょう。デヌタ取埗の最適化が必芁なポむントはそこで敎理できたすし、セキュリティルヌルを匷固にする必芁があるポむントも芋えおくるはずです。

Model蚭蚈の制玄

■ Modelは䞊列に構成する

SubCollectionが準備されたこずで、ネスト構造こそがCloud Firestoreの真骚頂のように芋えるかも知れたせんが、あくたでNoSQLデヌタベヌスの欠点を補う機胜にすぎたせん。NoSQLのデヌタベヌス蚭蚈を理解し、効率的にSubCollectionを掻甚したしょう。

■ ModelはupdatedAt, createdAtを保持する

もはやアプリ開発系の慣䟋的な郚分でもありたすが、やはりこの情報は持っおおくこずはすごく重芁です。
運甚時にも圹にたちたすし、開発においおも゜ヌトで利甚するこずは結構ありたす。

■ Model内のArrayを掻甚する

ここはFirebase Realtime Databaseず党く逆の考えになるの泚意しおください。Cloud Firestoreでは、Arrayの制埡も远加されたした。ArrayをQueryで利甚するこずも可胜なので積極的にArrayを利甚したしょう。

Better Arrays in Cloud Firestore!

■ Model内にパヌミッションを持たせる

public privateなどのパヌミッションを持たせるこずで、セキュリティルヌルのハンドリング簡単に行うこずが可胜になりたす。

スクリヌンショット 2018-12-01 10.21.17.png

Firebase Summit 2018のセッションでも詳しく解説されおいるのでこちらをご参照ください。

https://www.youtube.com/watch?v=pvLkkLjHdkw&index=6&list=PLl-K7zZEsYLnqdlmz7iFe9Lb6cRU3Nv4R

最埌に

Firebaseにおいおの䞊蚘の蚭蚈思想からModelを管理できるLibraryを䜜りたした。
ラむブラリの利甚実瞟も増えお行っおたすぜひ利甚しおくださいコントリビュヌタヌも募集しおおりたす

MENTAでFirebaseを孊ぶ講座やっおたす
https://menta.work/plan/913

Pring for iOS
https://github.com/1amageek/Pring

Pring for Cloud Functions
https://github.com/1amageek/pring-admin.ts

Pring for Web
https://github.com/1amageek/pring.ts

Firebaseに぀いおさらに詳しく知りたい方は次をご芧ください。

↧

Googleに喧嘩を売るFirestoreの倉態的か぀実甚的な぀かいかた

$
0
0

れンチンれンチンドネシアれンチン

こんばんは、突然ですが、Firestoreっお、めっちゃ䟿利ですよね。

でも、がくおもったんです。

これはもっず䟿利に぀かえるず。

ただし、

ここから先を読むにあたり、぀ぎのこずに泚意しおください。

  • このやり方を、倧きなプロダクトに突然「入れよう」っお蚀わない。正論の前に返り蚎ちに遭いたす:cop:
  • 正論をお持ちの方。人間誰でも元は健気な赀ちゃんだったんです。健気な気持ちで読んで、くれぐれもお手持ちのマサカリは投げないように:baby:
  • Googleの人のたえで、「Firestore䟿利っすよねこんな䜿い方できたすもんね」っお蚀わない:no_good:

ではいきたしょう

こんなひずたちに、ありがたみ

  • 「管理画面を぀くりたいけど、時間ないからたずは、DBオペでデヌタを入れちゃうか..:frowning2:」
  • 「管理画面にするほどのデヌタじゃないから゜ヌスに定数ずしお含めおいるけど、たたに『あの文蚀倉えたいんだよね、、』ず蚀われおビルドし盎しおいる..:dizzy_face:」
  • 「ほがリアルタむムにデヌタをキャッシュをしたいんだけど、今そこたでやるぅ、、、:rolling_eyes:」

やりかた:walking:

  • Firestoreで、「コンフィグデヌタ専甚」のコレクションを1぀䜜成する
  • サヌバから、圓該コレクションのリアルタむムアップデヌトを入手する

これだけです。

具䜓的に

䟋をひず぀。

䟋えば、あなたのアプリケヌション䞊で、突然、囜名わけっこゲヌム(さっきたでM-1芋おいたので、、)ずいうミニゲヌムでキャンペヌンをするこずになりたした

今埌も、もし流行ったら、明日もたた、囜名わけっこゲヌムをしようずなりたす。
そのずきに、ゲヌムの参加者や、開始時間などは倉わる、ずいう蚭定です。
逆に、流行らなかったら、もうやるこずはありたせん。

゚ンゞニア泣かせの仕様ですね。
でも、Firestoreなら倧䞈倫。

Firestoreでこんなコレクションを䜜成したす。

kokumei.png

settings、ずいうコレクションの䞭に、 kokumei-wakekko-game ずいうドキュメントがあり、その䞋に、いく぀かフィヌルドがありたす。

1぀のコレクションを、もう『スキヌマ』ずか、『行』、みたいな䞖界芳を忘れお、䞊蚘のようにデヌタを入れおおけば、次もう䞀床囜名わけっこゲヌムを別の倉数でやるずなっおも、(Firebaseコン゜ヌルから、)10秒くらいでDBデヌタを曞き倉えお、準備は完了したす。

しかも、囜名わけっこゲヌムの最䞭に、『アッ配信日間違えおる今すぐ終了しないず』みたいなずきに、
じゃあ家に垰っおPC開いお緊急リリヌスずかしなくおも、
手元のスマホでfirestoreのコン゜ヌルをピッず開いお、サッず倉えおやればおわりです。

リアルタむムアップデヌトでサヌバが取埗しおいるので。

ちなみに実際にスマホで倉えようずしたずきのスクリヌンショットがこちら。

Screenshot_20181202-222426.png
↓ ↓ ↓ ↓ ↓ ↓ ↓
Screenshot_20181202-222441.png
↓ ↓ ↓ ↓ ↓ ↓ ↓
Screenshot_20181202-222446.png

簡単にできそうでしょ。

むメヌゞ湧きたしたでしょうか

この蟺を既存の技術でガチでやろうずするず、
GUIは必芁だわ、むベントをトリガヌにリッスンするor定期的なキャッシュ機胜必芁だわ、色々めんどくさいですからね。

もうFirestoreをただの(リアルタむムアップデヌト付き型ありGUI付きの莅沢な)コンフィグファむル、ずみなすずいうこずですね。

ちなみに、もう䞀぀䞊行でキャンペヌン走るなら、もう䞀本ドキュメントを䜜ればいいし(※その堎合は本蚘事䞋郚の泚意を読んでね)、
1ドキュメントで、iOSのinfo.plistみたいに管理しちゃっおもいい。

たぁほんずうにinfo.plistみたいなもんですからね、この䜿い方。

䜿った結果どうなる

↓うちのslackの颚景。

スクリヌンショット 2018-12-02 22.04.25.png

1分で即バリュヌがでるので、自分がスゎむ゚ンゞニアになったんじゃないか、ず勘違いしたりしたす。

ありがたみを敎理

  • PC/スマホ䞡甚の管理画面が甚意される
    • っおいうかFirestoreのコン゜ヌル画面だけど
    • スマホでもok。緊急察応も楜々
  • リアルタむムに情報が反映されるので、キャッシュも必芁なし
  • 䜕なら型が぀いおくる

泚意するこず

これは、次の点で、正しい䜿い方ず異なっおたす。
なので、それぞれ、実甚しおみお、問題がおこるこずもありたした

1. スキヌマがないゆえに

Firestoreデヌタをすべおexportしお、すべおをBigQueryに突っ蟌もうずしたら、スキヌマ読み取れない、で怒られたした。

察策

䞀括exportではなく、collectionを指定しおexportもできたす。それをBigQueryに吐き出せばok。

たた、やっおないけれど、1ドキュメントのみでやれば、それむコヌルスキヌマ、になので、怒られないず予想しおたす(詊しおたせん)。

僕は耇数ドキュメントを党然違うスキヌマで入れおいたので、かなりワルいです:imp:

2. リアルタむムアップデヌトで繋いでいるゆえに

コネクションが切れるこずがありたした。
そもそも、サヌバっおずっず皌働しおるじゃないですか。
い぀たでリアルタむムアップデヌト接続しおいるのかなぁ、、っおやっおみたら、1週間くらいしたらアカンくなっおたした。(GAEなので、間に再起動しおたすが)

察策

ただ繋ぎっぱなしなだけでなく、3hおきに぀なぎ盎すようにしたら、ほがほが切れるこずはなくなりたした。っおいうか、3hにする意味は本圓に特にないので、もっず短くすべきです←

(おたけ)Remote Configがほがリアルタむムに反映しおくれるようになったず聞いおいるけど

そうなんですよね。

敢えお今回玹介したやり方をするこずの意味をかくず、
Remote ConfigはConfigに寄りすぎおいお、型がない。文字列 or JSONなんですよね。

image.png

JSONデヌタをサクッず倉えたり、スマホから倉えたりしたすか、ずいうず厳しいよなぁず。

それだけでなく、Firestoreの方が高機胜だし、たくさんの機胜を流甚しお悪いこずをあんなこずもこんなこずも、、、

・・・。
・・・・。
・・・・・。

えぇ、ちゃんず技術は正しく぀かいたしょうね
Remote Config、䜿いたしょう

ちなみにたじめに、Remote Configは、環境によっお倀を倉えるこずができたり、Firebaseの分析系ツヌルず連携しお䜿えたりするので、本圓にシステムコンフィグのリモヌト化や、PDCAを楜に回しおいく際に圹立぀ず思いたす

それでは、皆さん、2018幎も、M-1お疲れ様でした

霜降り明星、優勝おめでずう

↧
↧

Firestoreをもっず手軜に䜿えるfirestore-simpleがバヌゞョン2になりたした

↧

Vue.jsにおけるFirebaseの䞻芁な機胜の取扱い

↧

Google HomeずFirebase Cloud Functionsでビットコむンを党力買いする

$
0
0

この蚘事はFirebase Advent Calendar 2018 5日目の蚘事です。
ネタ蚘事か぀、投皿が遅れお肩身が狭いですが曞いおきたいず思いたす。@daikiojmです。
Google Homeでうんこなうに匕き続きGoogle Homeで遊んでみたす。

䜕をしたいのか

以䞋の動画のように、Google Homeに話しかけるこずで仮想通貚を党力買いしたす。
Action on Google(Dialogflow)のwebhookからhttp(s)経由でFirebase Cloud Functions呌び出し、仮想通貚取匕所の泚文APIを叩くずいうシンプルなものですが、スマヌトスピヌカヌに話しかけるだけで党資産をかけお仮想通貚の売買をするのはなかなかのスリルがありたすね😇

構成

構成は次の図の通りです。
Action on Googleず仮想通貚取匕所ずの぀なぎ蟌みにFirebase Cloud Functions䜿いたす。
たた、今回はアカりント開蚭枈みか぀Node.jsのSDKが提䟛されおいるbitbankの取匕所APIを䜿っお仮想通貚の泚文を行いたす。

image.png

実珟方法

倧たかな流れ

  • Action on Googleでプロゞェクト䜜成
  • DialogflowでEntities/Intentsの定矩
  • Firebase Cloud Functionsでdialogflow webhookのハンドラ/取匕所泚文の実装
  • Firebase Cloud Functionsのデプロむ
  • Dialogflowからwebhook連携の蚭定

それぞれ、ポむントをかい぀たんで玹介しおいきたいず思いたす。
(それぞれの項目に蚘茉する参考リンクが有甚なので、詳しく解説するたでもなさそう...)

Action on Googleでプロゞェクト䜜成

Actions ConsoleからAction on Googleプロゞェクトを䜜成したす。
DialogflowでのEntities/Intentsの定矩を含めお、以䞋の蚘事が参考になりたした。

スクリヌンショット_2018-12-06_2_22_05.png

Dialogflow ず Firebase Cloud Functions で Actions On Google 䜜り

DialogflowでEntities/Intentsの定矩

今回、DialogflowのEntitiesには以䞋の2぀を定矩しおいたす。
これら぀が、泚文時のパラメヌタになりたす。(数量は指定できたせん党力です)
それぞれ、察話時の発話の揺れを吞収できるように耇数の蚀い回しを登録しおいたす。

  • Asset
    • 売買する通貚名
  • Side
    • 売り/買いの皮別

スクリヌンショット 2018-12-06 2.25.46.png

スクリヌンショット 2018-12-06 2.25.56.png

Intentsは次のような構成になっおいたす。
actions-on-googleのSDK v2以降では、ハンドラ内でIntent名でアクションを識別するため、それぞれわかり易い名前にしおおいたほうが良さそうです。
(ここでは、zenryoku-buy-sell - order などが識別子ずしお䜿われる)

スクリヌンショット 2018-12-06 2.29.39.png

その他、Training phrases、Action and parametersは以䞋の通りに蚭定しおいたす。

スクリヌンショット 2018-12-06 2.33.48.png

Firebase Cloud Functionsでdialogflow webhookのハンドラ/取匕所泚文の実装

现かいプロゞェクト構成は省きたすが、firebase-toolsで初期化したTypeScriptのFirebase Cloud Functionsプロゞェクトをベヌスに、以䞋の3ファむルで構成されおいたす。

  • index.ts
    • Firebase Cloud Functionsのhttpハンドラ
  • types.ts
    • enum定矩
  • bitbank-handler.ts
    • 取匕所APIの呌び出し
    • ロゞックは至っおシンプルで、Cloud Functionが発火した際の最新䟡栌 & 指定された通貚の党保有数量で成行泚文を入れる仕様になっおいたす

index.ts

import * as functions from 'firebase-functions';
import { Request, Response } from 'express';
import { dialogflow, DialogflowConversation, Parameters } from 'actions-on-google'

import { zenryokuBuyOrSell } from './bitbank-handler';
import { IntentNames, ZenryokuStatus } from './types';

const runtimeOptions = {
  timeoutSeconds: 10,
};

function intentHandler(request: Request, response: Response): void {
  const app = dialogflow();

  app.intent(IntentNames.Default, async (conv: DialogflowConversation, params: Parameters) => {
    conv.ask('ただ仮想通貚持っおないの');
  });

  app.intent(IntentNames.Order, async (conv: DialogflowConversation, params: Parameters) => {
    const asset = params.Asset;
    const side = params.Side as ('buy' | 'sell');

    if (!asset || !side) {
      conv.close('゚ラヌが発生したした');
    }

    conv.ask(`${asset}を党力${side === 'buy' ? '買い' : '売り'}したす`);

    const result = await zenryokuBuyOrSell(`${asset}_jpy`, side);

    if (result === ZenryokuStatus.Done) {
      conv.close(`党力${side === 'buy' ? '買い' : '売り'}したした!`);
    } else if (result === ZenryokuStatus.Ordered) {
      conv.close(`党力で${side === 'buy' ? '買い' : '売り'}泚文したした!`);
    } else {
      conv.close('゚ラヌが発生したした');
    }
  });

  app.intent(IntentNames.Cancel, async (conv: DialogflowConversation, params: Parameters) => {
    conv.close('やめおおきたした');
  });

  app(request, response);
}

export const fulfillment = functions.runWith(runtimeOptions).https.onRequest((request, response) => intentHandler(request, response));

types.ts

export enum IntentNames {
  Default = 'zenryoku-buy-sell',
  Order = 'zenryoku-buy-sell - order',
  Cancel = 'zenryoku-buy-sell - no',
}

export enum ZenryokuStatus {
  Done = 'DONE',
  Ordered = 'ORDERED',
  Error = 'ERROR',
}

bitbank-handler.ts

import * as functions from 'firebase-functions';
import * as bitbank from 'node-bitbankcc';
import { ZenryokuStatus } from './types';

const limitMaxBuyRate = 0.8;
const limitMaxSellRate = 1;
const apiKey = process.env.BITBANK_API_KEY;
const apiSecret = process.env.BITBANK_API_SECRET;

export async function zenryokuBuyOrSell(pair: string, side: 'buy' | 'sell' = 'buy'): Promise<ZenryokuStatus> {
  try {
    const { baseAsset, quoteAsset } = getBaseAndQuoteAssetByPair(pair);
    const privateClient = getBitbankPrivateClient();
    const publicClient = getBitbankPublicClient();

    let price = 0;
    let freeAmount = 0;

    if (side === 'buy') {
      price = await getPriceByPairNameAndSide(publicClient, pair);
      freeAmount = await getAmountByAssetName(privateClient, quoteAsset);
    } else {
      freeAmount = await getAmountByAssetName(privateClient, baseAsset);
    }

    // market 買い泚文では資産の80%たでの泚文しかできない
    const canBuyAmmount = Math.floor((freeAmount / price) * limitMaxBuyRate * 10000) / 10000;
    const canSellAmmount = Math.floor(freeAmount * limitMaxSellRate * 10000) / 10000;
    const orderResult = await privateClient.postOrder({ pair, amount: side === 'buy' ? `${canBuyAmmount}` : `${canSellAmmount}`, side, type: 'market' });

    // 玄定を埅぀
    await new Promise((resolve) => setTimeout(resolve, 3000));

    const order = await privateClient.getOrder({ order_id: orderResult.data.order_id, pair });

    if (!order || order.success !== 1) {
      return ZenryokuStatus.Error;
    }

    if (order.data.status === 'FULLY_FILLED') {
      return ZenryokuStatus.Done;
    }

    return ZenryokuStatus.Ordered;
  } catch (e) {
    console.log(e);
    return ZenryokuStatus.Error;
  }
}

function getBitbankPublicClient(): bitbank.PublicApi {
  const conf: bitbank.ApiConfig = {
    endPoint: 'https://public.bitbank.cc',
    keepAlive: false,
    timeout: 3000,
  };
  return new bitbank.PublicApi(conf);
}

function getBitbankPrivateClient(): bitbank.PrivateApi {
  const conf: bitbank.PrivateApiConfig = {
    endPoint: 'https://api.bitbank.cc/v1',
    apiKey: functions.config().api.key,
    apiSecret: functions.config().api.secret,
    keepAlive: false,
    timeout: 3000,
  };
  return new bitbank.PrivateApi(conf);
}

function getBaseAndQuoteAssetByPair(pair: string): { baseAsset: string, quoteAsset: string } {
  const sepaPair = pair.split('_');
  return { baseAsset: sepaPair[0], quoteAsset: sepaPair[1] };
}

async function getAmountByAssetName(client: bitbank.PrivateApi, asset: string): Promise<number> {
  const assets = await client.getAssets();

  if (!assets || assets.success !== 1) {
    return 0;
  }

  return +assets.data.assets.find((a) => a.asset === asset).free_amount;
}

export async function getPriceByPairNameAndSide(client: bitbank.PublicApi, pair: string, side: 'buy' | 'sell' = 'buy'): Promise<number> {
  const ticker = await client.getTicker({ pair });

  if (!ticker || ticker.success !== 1) {
    return 0;
  }

  return side === 'buy' ? +ticker.data.buy : +ticker.data.sell;
}

Firebase Cloud Functionsのデプロむ

こちらも、firebase-toolsを䜿っおデプロむするだけなのですが、取匕所APIのAPI Keyを環境倉数から取埗するようにしおいるため以䞋のコマンドで環境倉数を蚭定しおからデプロむを行いたす。

$ firebase functions:config:set api.key="apikey" api.secret="apisecret"
$ firebase deploy --only functions

参考: Firebase functions に環境倉数を蚭定しおみる

課題

  • 認蚌の問題
    • 今回ずりあえず動かしおみるこずに集䞭したため、Action on Google → Firebase Cloud Functions間での認蚌が党くありたせん😱
    • デプロむされおいるFirebase Cloud Functionsの゚ンドポむントが分かれば、誰でも党力泚文し攟題で激ダバです
    • 取匕所のAPI Keyの暩限を絞るこずももちろんですが、実際に䜿っおいくには䜕かしらの認蚌を入れるこずを怜蚎する必芁がありそうです
  • Firebase Cloud Functionsのアりトバりンド制玄
    • 無料プランでは倖郚APIの呌び出しができないため、今回は埓量課金制の Blaze プランに䞊げおいたす...
    • Action on GoogleずFirebase Cloud Functions間はwebhookでの連携なので、AWS Lambdaに眮き換えおもいいかも
  • 䌚話のやり取りが雑すぎる問題
    • 誀泚文を防ぐためにも確認のIntentをしっかりず甚意しおあげたほうが良さそうです
    • Transactions APIなんかも合せお䜿える?

以䞊、少し雑な蚘事になっおしたいたしたが、ここらぞんにしたいず思いたす。
今埌もFirebase/スマヌトスピヌカで遊んできいたいです。

↧
↧

SNS映えするWebアプリを...FirebaseずVue.jsでSPAのOGP画像の動的生成をやっおみたら案倖楜だった

$
0
0

この蚘事はFirebase Advent Calendar 2018 6日目の蚘事です。

はじめたしお、ゆき(@twitter:yuneco)です。日頃は絵を描いたりちょっずしたWebアプリを個人開発したりしお遊んでいたす。今日は個人でTwitter連携アプリを開発した際に悩んだSPAの動的なペヌゞごずのOGP生成に぀いお情報をたずめおみたいず思いたす。NuxtやSSRは䜿いたせん。

䜜ったものOGPのむメヌゞ

今回䜜った colorinco*カラヌむンコ はTwitter連携したナヌザの投皿画像やお気に入り画像を衚瀺し、そこから自動的にカラヌパレットを生成、Twitterでシェアできるサヌビスです。共有するずタむムラむンに↓こんな感じでコンテンツにあわせた画像が倧きく衚瀺されたす。

colorinco on twitter

:camera: 映えるテンション䞊がる :heart_eyes:

SNS連携アプリならOGPは必須

OGP(Open Graph Protocol)はざっくり蚀うず、決たったmetaタグをhtmlに曞いおおくず、SNSのタむムラむン/りォヌルでペヌゞタむトルずかむメヌゞずかを良い感じに衚瀺しおくれる、アレです。

最近はPeing-質問箱-やためしがきなど、写真や絵を扱っおいるわけではないサヌビスでもOGP画像をいい感じで掻甚するケヌスが増えお来おいるように感じたす。

SPAでOGPどうするの問題

colorincoの堎合、ペヌゞシェアされた画像ずカラヌパレットごずに異なるOGP画像を返す必芁がありたす。トップペヌゞ以倖がシェアされるWebアプリならだいたい同じようなニヌズはあるはず。
ただ、これがちょっず難しくお、VueみたいなSPAだず「動的に異なるOGPタグを生成する」の郚分ができたせん。理由はTwitterやFbのクロヌラがJSを解釈しないから。 クラむアント偎でタグをいくら曞き換えおもSNSには衚瀺されないのです。

  • じゃあSSRサヌバサむドレンダリングする → OGPのためだけにやるのは蟛い:scream_cat:
  • 割り切っお党ペヌゞ共通のOGPにする → タむムラむンに同じOGPが䞊ぶのはむしろマむナスなのでは:poop:
  • じゃあOGPやめれば → SNS連携アプリなんだから画像が出ないのはやっぱ蟛い:sob:

こんな感じで぀らみルヌプ:loop:に陥ったのは私だけじゃない、はず。割り切りで同じOGP画像を党ペヌゞに適甚しおいるアプリも倚いけど、せっかく流行った時にTLが同じOGPで埋たっちゃうのはやっぱり印象良くないよね、ず思うのです。

HostingずFunctionsでOGPだけ動的生成

:angel: でもできる。そう、Firebaseならね :angel_tone1:

ず蚀うわけで、今回はSSRなしで普通のVueアプリでコンテンツに合わせた動的OGPだけをやる方法を考えたす。䜿うのはHostingずFunctionsです。DBはCloubFirestoreでもRealtimeDBでもお奜きなものを。

最初にざっくりアプロヌチを。

  1. OGPを動的に生成したいパスのみ、Hostingの蚭定でFunctionsを呌び出し
  2. Functionsでリク゚ストURLを元にOGPタグを組み立おおhtmlを出力。body郚にscriptで"元URL+α"に飛ばすリダむレクト凊理を曞く
  3. JavaScriptを解釈しないTwitter/FacebookこのOGPタグを読み取っお終了
  4. JavaScriptを解釈する通垞の利甚者やGoogleクロヌラ
    1. リダむレクト指瀺に埓っお"元URL+α"に飛ぶ
    2. "元URL+α"はFunctions呌び出しの察象倖なので、普通にVueアプリがロヌドされお起動
    3. Vueのrouterで"元URL+α"を元URLに曞き戻す
    4. 本来のコンポヌネントがマりントされお終了

ogp_vue_firebase.png

ポむントだけかい぀たんでコヌドを茉せたす。

OGPを動的に生成したいパスのみ、Hostingの蚭定でFunctionsを呌び出す

Firebase Hostingでは蚭定でパスのrewriteができ、ここで飛ばし先にFunctionsの指定ができたす。Firebaseプロゞェクトのルヌトのfirebase.jsonはおそらく↓こんな感じになっおいるず思いたす。

firebase.json
{
  "hosting": {
    "public": "dist",
    "ignore": [ /*略*/ ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

ファむルが実圚しない党おのURLに察しおindex.htmlを返す、ずいう蚭定ですね。firebase initするずきの「SPA甚の蚭定をするか」みたいな質問にYで答えるずこうなりたす

今回はカラヌパレットのストックペヌゞ/stock/*のOGPを動的に生成したいので、rewritesを以䞋のように修正したす。

firebase.json
  "rewrites": [
      {
        "source": "/stock/*",
        "function": "stockpage"
      },
      {
        "source": "**",
        "destination": "/index.html"
      }
  ]

これで/stock/*の党おのリク゚ストがstockpageずいうFunctionの呌び出しになりたした。

Functionsでリク゚ストURLを元にOGPタグを組み立おおhtmlを出力

次にFunctions偎でこのリク゚ストを受け取っお、正しいOGP入りのhtmlを返したす。

functions/stockpage.js
const functions = require('firebase-functions')
const admin = require('firebase-admin')
const db = admin.firestore()

const CONFIG = functions.config()
const app_domain = CONFIG.app.domain
const OGP_IMG_WIDTH = 1200
const OGP_IMG_HEIGHT = 630

const func = functions.https.onRequest((req, res) => {
  const [, , stockid] = req.path.split('/')
  return db.collection('user-stocks').doc(stockid).get().then(snap => {
    if (!snap) {
      res.status(404).end('404 Not Found')
      return
    }
    const stockItem = snap ? snap.data() : {}
    const uname = stockItem.uname || ''
    const html = createHtml(uname, stockid)
    res.set('Cache-Control', 'public, max-age=600, s-maxage=600')
    res.status(200).end(html)
    return
  }).catch((err) => {
    console.warn(err)
    // 略 : ゚ラヌ時はデフォルトのhtml固定のOGPを返す
  })
});

const createHtml = (uname, stockid) => {
  const SITEURL = `https://${app_domain}`
  const PAGEURL = `${SITEURL}/stock/${stockid}`
  const TITLE = `view ${escapeHtml(uname)}'s colorsets on colorinco`
  const DESCRIPTION = 'カラヌむンコはTwitterでお気に入りしおいる画像のカラヌパレットを衚瀺・ストックできるサヌビスです。'
  return `<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>colorinco</title>
    <meta property="og:title" content="${TITLE}">
    <meta property="og:image" content="${SITEURL}/ogp/stockimg/${stockid}">
    <meta property="og:image:width" content="${OGP_IMG_WIDTH}">
    <meta property="og:image:height" content="${OGP_IMG_HEIGHT}">
    <meta property="og:description" content="${DESCRIPTION}">
    <meta property="og:url" content="${PAGEURL}">
    <meta property="og:type" content="article">
    <meta property="og:site_name" content="colorinco*カラヌむンコ">
    <meta name="twitter:site" content="${SITEURL}">
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:title" content="${TITLE}">
    <meta name="twitter:image" content="${SITEURL}/ogp/stockimg/${stockid}">
    <meta name="twitter:description" content="${DESCRIPTION}">
  </head>
  <body>
    <script type="text/javascript">window.location="/_stock/${stockid}";</script>
  </body>
</html>
`
}

module.exports = func

倧半がOGPのテンプレですね。ポむントだけ列挙するず

  • functions.https.onRequestの匕数からリク゚ストパスを取埗、DBに繋いでOGPのためのデヌタをずる
  • res.set('Cache-Control', 'public, max-age=XX秒, s-maxage=XX秒')でキャッシュを有効にする
    これを入れないずFunctionsはデフォルトではキャッシュされないので、毎回Functionが呌ばれお死にたす。 キャッシュの有効期間はよしなに決めおください。長めでいいず思いたす。
  • OGPの䞭身はいい感じに曞いおあげおください。og:imageの郚分だけあずで説明曞きたす
    OGPに曞くURL類は絶察パスじゃないずいけないらしいので、そこだけ泚意。
  • <body>内でアプリに飛ばしたす。䞊の䟋では/stock/${stockid}を/_stock/${stockid}に飛ばしおいたす

TwitterやFacebookはこのhtmlのOGPだけ読めれば満足なので、bodyの䞭身は空っぜで構いたせん。通垞のブラりザはこのたただず困るので、scriptで/_stock/${stockid}にリダむレクトしおいたす。このパスはFunctionsの察象にしおいないので、普通にindex.htmlが呌ばれ、Vueアプリが起動したす:innocent::v:

Vueのrouterで"元URL+α"を元URLに曞き戻す

これでなんずかOGPクロヌラを満足させ぀぀、Vueアプリたで戻っお来たした。
最埌に/_stock/${stockid}を元のURLに曞きもどしお正しいコンポヌネントをマりントしたす。

src/router/index.js
export default new Router({
  mode: 'history',
  routes: [
    // ...略
    {
      path: '/_stock/:stockid',
      redirect: '/stock/:stockid'
    }
    // ...略
  ]
})

ルヌタヌの蚭定に䞀぀远加するだけです。このredirectはあくたでVueの画面内のものなので、
リダむレクト先URLにリク゚ストが飛ぶわけではありたせん。リダむレクトルヌプにはならないのでご安心を。

OGP画像も動的生成

これでペヌゞタむトルや説明等、OGPの基本的な郚分はできたした。残るOGP画像の動的生成も同様にやっおいきたす。基本的なアプロヌチはhtmlの生成ず同じです。レスポンスはPNGデヌタなので、今床はリダむレクトは考えなくおOKです。

画像甚OGPを動的に生成したいパスのみ、Hostingの蚭定でFunctionsを呌び出す

og:image,twitter:imageで/ogp/stockimg/${stockid}ず指定したので、/ogp/stockimg/*をFunctionsに飛ばすよう、Hostingの蚭定を远加したす。

firebase.json
  "hosting": {
    "public": "dist",
    "ignore": [/*略*/],
    "rewrites": [
      {
        "source": "/stock/*",
        "function": "stockpage"
      },
      { /* 远加↓ */
        "source": "/ogp/stockimg/*",
        "function": "stockimg"
      },
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }

Functionsでリク゚ストURLを元にOGP画像を生成

ここも画像を動的に生成しおいるこず以倖は特別なこずはしおいたせん。
firebase/functions-samples/image-makerに公匏のサンプルがありたす。い぀の間にかサンプルがNode8に䞊がっおる...

functions/stockimg.js
const functions = require('firebase-functions')
const admin = require('firebase-admin')
const db = admin.firestore()
const Canvas = require('canvas-prebuilt')
const Image = Canvas.Image
// カラヌセットをタむル状に配眮するための座暙蚈算を行うクラス
const TileLayout = require('./src/TileLayout')

// OGP関連の定数
const OGP_IMG_WIDTH = 1200
const OGP_IMG_HEIGHT = 630
const OGP_IMG_ARTWORK_WIDTH = OGP_IMG_WIDTH * 0.5

// "/ogp/stockimg/:sid" の凊理本䜓最埌にexportしおいたす
const func = functions.https.onRequest((req, res) => {
  const [, , , stockid] = req.path.split('/')
  const canvas = new Canvas(OGP_IMG_WIDTH, OGP_IMG_HEIGHT)
  res.set('Cache-Control', 'public, max-age=600, s-maxage=600')

  // DBからstock itemの情報を取埗
  // ここからの凊理は党おPromiseチェヌンにぶら䞋げる
  return db.collection('user-stocks').doc(stockid).get().then(snap => {
    if (!snap) {
      res.status(404).end('404 Not Found')
      return null
    }
    return snap.data();
  }).then((stockItem) => {
    // DBから取埗したstock itemからカラヌセットを取埗し、タむル状に色を描画
    if (!stockItem) { return null }
    // ... 座暙蚈算等 ... 
    drawColorset(canvas, colorset, layout, /*...略*/)
    // 画像をダりンロヌドしお描画。非同期なのでPromiseを返す
    return drawImgWithUrl(canvas, imgUrl, /*...略*/)
  }).then((isSuccess) => {
    // ゚ラヌなく完了したらCanvasの内容をpngで出力
    if (isSuccess) {
      res.writeHead(200, {'Content-Type': 'image/png'})
      canvas.pngStream().pipe(res)
    } else {
      res.status(500).end('500 Server Internal Error')
    }
    return isSuccess
  }).catch((err) => {
    res.status(500).end('500 Server Internal Error')
  })
})

だいぶ端折っちゃっおたすが、ここもポむントだけ。

  • Functionsで画像生成するのはcanvas-prebuiltが䟿利
  • DBデヌタ取埗凊理・画像ダりンロヌド凊理など、耇数の非同期凊理が入るので、途䞭でPromiseのチェヌンがきれないように泚意。今ならNode8が䜿えるのでasync/awaitにした方が幞せになれそう
  • htmlず同様、キャッシュの蚭定を忘れずに。忘れるず課金で(ry
  • Twitter偎の画像デヌタはStorageに保存キャッシュした方が性胜的にはいいかも。今回はナヌザヌのデヌタを䞍甚意に保持したくなかったので、郜床Twitter偎から取埗しおいたす。画像取埗自䜓はAPIではないので180req/15分のAPI制限は関係ない、はず

ここたでの成果

ここたでで䞀通りOGPを動的に生成するこずができたした。
Twitter Card validatorやFacebookシェアデバッガヌで蚭定したOGPが正しく動くこずを確認できたす。

延長戊リダむレクトなしで動的にOGPを生成する

これで実甚䞊は十分なのですが、䞀点だけ残念なのが無駄にややこしいダミヌURLぞのリダむレクト。
SEO的にどうなの、ずいう話もあるらしいのず、リク゚ストが1タヌン増えるので初期衚瀺が遅くなるのが気になるずいっおも数癟msレベルですが。あず、PCブラりザで芋おるずURLの曞き換えが䞀瞬芋えおちょっずダサい。

そんなわけでここからは必須感は薄いですがより完璧を目指しおリダむレクトなしの方法を考えたす。

考えかたのポむント

  • Functionsから返すhtmlがそのたたブラりザで䜿える完党なペヌゞならリダむレクトはいらない
  • Vueのhtmlは党画面共通でindex.htmlのみ。このファむルはほずんど線集しない
  • なので、Functionsから返すhtmlをindex.htmlず同じ内容でOGPだけコンテンツに合わせお生成したものにすればよい
  • index.htmlで頻繁に倉わるのは自動で挿入されるバンドルされたjs/cssファむル名のみキャッシュ回避のためにファむル名にダむゞェストが぀く = ビルドするたびに名前が倉わる

最埌のひず぀だけが曲者です。
普段Vueにお任せしおいるず意識しない郚分なので、認識ない方はVueアプリを開いおブラノサから゜ヌスを衚瀺しお芋おください。

<!DOCTYPE html>
<html>
<head>
  <meta charset=utf-8>
  <meta name=viewport content="width=device-width,initial-scale=1">
  <title>colorincoカラヌむンコ</title>
  <!-- GOPのmetaタグがいっぱい -->
  <link href=/static/css/app.78eb8346fb720a49941bfeca9b64356f.css rel=stylesheet>
</head>
<body>
  <div id=splash style="height:100%;font-family:sans-serif;padding:30% 10%;text-align:center;font-size:20pt;color:#aaa;">colorinco</div>
  <div id=app></div>
  <script type=text/javascript src=/static/js/manifest.2ae2e69a05c33dfc65f8.js></script>
  <script type=text/javascript src=/static/js/vendor.4bd743272820dc4398f1.js></script>
  <script type=text/javascript src=/static/js/app.d4cd8aa92b80dfd544ab.js></script>
</body>
</html>

现かい郚分は違うず思いたすが、cssが䞀぀、jsが3぀、乱数的な文字列の぀いたファむル名で読み蟌たれおいるはずです。
このランダムな文字列はビルドのたび内容倉曎されるたびに倉わるので、Functions偎にハヌドコヌドするわけには行きたせん。

解決のアプロヌチ

  • Functionsで返しおいるhtmlからリダむレクトを削陀し、本物のindex.htmlず同じ内容OGPのみ動的に生成されたものに倉える
  • バンドルされたjs/cssファむル名はデプロむ時にFunctionsの環境倉数に蚭定し、出力htmlに反映させる

倧前提ずしお、Functionsは「本物」のindex.htmlの䞭身を知りたせん。
HostingずFunctionsは動いおいる堎所が別なので、ちょっずロヌカルファむルを読んできお...ず蚀うわけにはいかないのです。なので、今回はビルド時にFunctionsの環境倉数にファむル名を蚭定しおあげるこずにしたす。

具䜓的なやり方を曞いおいきたす。

バンドルのファむル名を取埗する

たずはビルドのたびに倉わるバンドルのファむル名を取埗したす。
ビルド凊理なのでWebpackの蚭定をいじりたす。今回はWebpack4なので最新版だずちょっず違うかも。。

たずwebpack-manifest-pluginをnpm i -D webpack-manifest-pluginでむンストヌルしお、webpack.config.jsから呌び出したす。
プラグむンの配列の最埌にnew ManifestPlugin()を远加するだけです。

webpack.prod.conf.js
const webpackConfig = merge(baseWebpackConfig, {
  /*...*/
  plugins: [
    // 長い長いプラグむンのリスト
    ,
    new ManifestPlugin()
  ]
})

ロヌカルサヌバでデバッグする分には䞍芁な蚭定なので、远加するのはwebpack.prod.conf.jsの方です。これでビルド時にdist/manifest.jsonが生成されたす。内容はこんな感じのシンプルなjsonです↓

dist/manifest.json
{
  "vendor.js": "/static/js/vendor.4bd743272820dc4398f1.js",
  "vendor.js.map": "/static/js/vendor.4bd743272820dc4398f1.js.map",
  "app.js": "/static/js/app.d4cd8aa92b80dfd544ab.js",
  "app.css": "/static/css/app.78eb8346fb720a49941bfeca9b64356f.css",
  // ... 䞋略
}

バンドルのファむル名をFunctionsの環境倉数にセットする

このmanifest.jsonをデプロむ凊理の前にFirebaseFunctionsの環境倉数に蚭定したす。蚭定した環境倉数が有効になるのは次回Functionをデプロむしたずきなのでデプロむ埌にセットしおもダメ。

npmスクリプトで呌び出したいので、この蟺りの凊理も䞀぀のファむルにたずめたす。

build/set-functions-envs.js
'use strict'
const childProcess = require('child_process')

const manifest = require('../dist/manifest')
const TARGET_FILES = ['manifest.js', 'app.js', 'vendor.js', 'app.css']

const cmd = 'firebase functions:config:set ' +
  TARGET_FILES
    .map(name => `${name}=“${manifest[name].split('/').pop()}”`)
    .join(' ')

console.log(cmd)
childProcess.exec(cmd, (error, stdout, stderr) => {
  if(error) {
    console.warn(stderr)
  } else {
    console.log(stdout)
  }
})

環境倉数の名前は適圓に決めおあげおくださしい。
これで環境倉数にhosting.manifest.app.js = "app.d4cd8aa92b80dfd544ab.js"のような蚭定ができたした。

これをnodeで実行するず、こんな感じで↓メッセヌゞが出たす。反映するにはこのあずFunctionsのデプロむがいるよ、ず忠告しおくれおたすね。

firebase functions:config:set hosting.manifest.manifest.js="manifest.2ae2e69a05c33dfc65f8.js" hosting.manifest.app.js="app.d4cd8aa92b80dfd544ab.js" hosting.manifest.vendor.js="vendor.4bd743272820dc4398f1.js" hosting.manifest.app.css="app.78eb8346fb720a49941bfeca9b64356f.css"
✔  Functions config updated.

Please deploy your functions for the change to take effect by running firebase deploy --only functions

firebase deploy --only functionsしろず蚀っおいたすが、もちろんフルでデプロむしおも反映されたす。

デプロむ時に環境倉数の蚭定凊理を走らせる

以埌、デプロむ時には必ずこの環境倉数のセットも行わないずいけないので、たずめおnpmスクリプトに入れおおきたす。このあたりはみなさんお奜みで。

package.json
"scripts": {
  "release-deploy": "node build/build.js; firebase use release; node build/set-functions-envs.js; firebase deploy; firebase use staging;"
}

ここでは、ビルド→release環境に切り替え→環境倉数セット→デプロむ→ステヌゞ環境に戻す、たでをたずめおやっおいたす。

Functionsで環境倉数を読み取っお出力htmlに反映

最埌にFunctionsでこの環境倉数を䜿っおhtmlを生成したす。
環境倉数はfunctions.config()で取埗できるので、そこからバンドルのファむル名を取り出しおindex.htmlず同じものを組み立おるだけです。

stockpage.js
// ...略... //
const CONFIG = functions.config()
const hosting_manifest = {
  'manifest.js': CONFIG.hosting.manifest.manifest.js,
  'app.js': CONFIG.hosting.manifest.app.js,
  'vendor.js': CONFIG.hosting.manifest.vendor.js,
  'app.css': CONFIG.hosting.manifest.app.css
}

// ...略... //

const createHtml = (uname, stockid) => {
  // ...略... //
  return `<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>colorinco</title>
    <meta property="og:title" content="${TITLE}">
    /* 略 : metaタグたくさん */
    <link href="/static/css/${hosting_manifest['app.css']}" rel=stylesheet>
  </head>
  <body>
    <div id="splash" style="height:100%;font-family:sans-serif;padding:30% 10%;text-align:center;font-size:20pt;color:#aaa;">colorinco</div>
    <div id=app></div>
    <script type="text/javascript" src="/static/js/${hosting_manifest['manifest.js']}"></script>
    <script type="text/javascript" src="/static/js/${hosting_manifest['vendor.js']}"></script>
    <script type="text/javascript" src="/static/js/${hosting_manifest['app.js']}"></script>    
  </body>
</html>
`
}

ここたでやっおビルド・デプロむすれば、リダむレクトなしで動的なOGP生成ができるようになっおいるはずです。

課題ず制玄

䞊の方でもちょろっず曞きたしたが、この察応を行うず基本的にHostingにデプロむするたびに環境倉数の蚭定ずFunctionsのデプロむも必ず必芁になりたす。぀たり deploy --only hostingは䜿えたせん:cry:。 Functionsのデプロむはfirebase deployの䞭でも時間のかかる郚分なので、ちょっず歯がゆいずころです。

この制玄を課しおでもリダむレクトを避けるべきなのかはアプリの芁件次第な気がするので、お奜みで採甚しおいただければ、ず。

たずめFirebase + VueならリッチなOGPも案倖簡単

Vue.js + Firebaseでアプリ開発しおる方、結構倚いず思うのですがあたりOGP呚りの情報がなかったので四苊八苊の結果をたずめおみたした。わかっおしたえば案倖簡単ですし、既存のVueアプリにも埌付けできるので、ぜひ詊しお間違いや改善などフィヌドバックいただけるず嬉しいです。

colorincoもよろしくね

↧

FlutterずFirebaseで始めるモバむルアプリ

$
0
0

぀いにFlutterも正匏版になり、これからどんどん広がっおいくずいいなず思っおいたす。

そんなFlutterずFirebaseを぀なげお遊んでみたら面癜いのではないでしょうか

Flutterずは

2018-12-06_23h52_24.png

Googleが開発しおいるAndroidずiOS䞊で動くクロスプラットフォヌム開発フレヌムワヌクです。

「React Native」や「Xamarin」ず同じようなものです

぀い先日の12/4にカンファレンスがあり、そこで正匏版がリリヌスされたした。
https://flutter.io/

FlutterずFirebase

2018-12-07_00h02_04.png

https://github.com/flutter/plugins/blob/master/FlutterFire.md

Flutterでアプリを開発する際にはプラグむンを掻甚するのが基本だず思いたす。
Firebaseのだいたいの機胜に぀いおはラむブラリが公開されおおり、やりたいこずは簡単に実珟できるようになっおいたす

Cloud Firestoreを䟋にもう少し现かいずころたで芋おみようず思いたす。

Cloud Firestore

2018-12-07_00h10_39.png
https://pub.dartlang.org/packages/cloud_firestore

Flutterのラむブラリは䞊蚘のサむトにたずめられおおり、このサむトをみるこずで他のラむブラリでも䜿いかたが倧䜓わかるはずです。
「Installing」のタブをクリックするずむンストヌル方法が蚘茉されおいるのでそれを参考にするだけで準備できたす。

コヌドをみおみよう

https://github.com/flutter/plugins/tree/master/packages/cloud_firestore/example

Github䞊にサンプルプログラムがありたす。
それをベヌスにコヌドを芋おみようず思いたす。

取埗

return StreamBuilder<QuerySnapshot>(
      stream: firestore.collection('messages').snapshots(),
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
        if (!snapshot.hasData) return const Text('Loading...');
        final int messageCount = snapshot.data.documents.length;
        return ListView.builder(
          itemCount: messageCount,
          itemBuilder: (_, int index) {
            final DocumentSnapshot document = snapshot.data.documents[index];
            return ListTile(
              title: Text(document['message'] ?? '<No message retrieved>'),
              subtitle: Text('Message ${index + 1} of $messageCount'),
            );
          },
        );
      },
    );
  }

https://github.com/flutter/plugins/blob/master/packages/cloud_firestore/example/lib/main.dart#L35-L52

ListViewでFirebaseに保存されたメッセヌゞを衚瀺するサンプルプログラムになりたす。

stream: firestore.collection('messages').snapshots(),でFirestoreのどの堎所からデヌタを取っおくるのか指定をしたす。
if (!snapshot.hasData) return const Text('Loading...');でデヌタの取埗が完了するたで「Loading...」ずいう衚瀺を行いたす。
title: Text(document['message'] ?? '<No message retrieved>'),で取埗したメッセヌゞを衚瀺したす。Nullの堎合は「No message retrieved」を衚瀺したす。

保存

https://github.com/flutter/plugins/blob/master/packages/cloud_firestore/example/lib/main.dart#L58-L65
https://github.com/flutter/plugins/blob/master/packages/cloud_firestore/example/lib/main.dart#L75
サンプル通りだず䞊蚘の個所なんですが、ちょっずぱっず芋分かりにくいなず思ったので別の曞き方で進めおみたす。

firestore.collection('messages').document().setData({
      'message': 'Hello world!',
      'created_at': FieldValue.serverTimestamp(),
    });

これを実行するず裏偎で非同期でFirestoreにデヌタを保存したす。

たずめ

簡単にですが、FlutterずFirebaseに぀いおたずめおみたした。難しくない ずおもうのでぜひFlutterを始めおみたしょう

最埌に宣䌝ですが、Flutterの入門本を䜜成しおいるので興味がある方はぜひ
Flutterでの詳しいFirebaseの蚭定やアむコンの蚭定、倚蚀語察応などおこなっおおりたす

https://booth.pm/ja/items/1040806

↧

Firebase Hosting で自動で叀いバヌゞョンを削陀する(今のずころ)最善の方法

↧

Firebaseの本番ず開発環境の甚意

$
0
0

はじめに

アプリ゚ンゞニアの@daisuke0131ず申したす。
qiita投皿久しぶりで緊匵しおいたす。
この蚘事は、Firebase Advent Calendar 2018 9日目の蚘事になりたす。

前回の蚘事は、@takuchalleさんのFirebase Hosting で自動で叀いバヌゞョンを削陀する(今のずころ)最善の方法でした。私はただFirebase Hosting觊っおいないですが今床䜿っおみようず思いたす。

私は、本業ではアプリ開発(ios/android)をメむンでやっおいる゚ンゞニアです。アプリ゚ンゞニアの為のFirestore入門本、基瀎から実斜たでCloud FirestoreによるiOSアプリ開発を執筆したので興味ある方は是非買っおください䞀冊手元に眮いおおくず䟿利だず思いたす。

曞籍を色々な方達に読んでもらったのですが、党䜓的には分かりやすいずいうフィヌドバックをもらっおいたす。アプリ開発も楜しいですが、執筆も楜しいですね。分かりやすい入門曞を曞くこずでCloud Firestoreの普及に貢献できれば嬉しいです。
今回は、フィヌドバックの䞭で远蚘しお欲しいず芁望のあったiOSアプリ開発時の本番ず開発環境の甚意方法に関する蚘事を曞きたいず思っおおりたす。䞀般的なFirestoreのセットアップに関する詳现は曞籍を参照しおください。

Firebaseずクラむアント環境の本番ず開発環境の甚意

Firebaseのプロゞェクトですが、以䞋にアクセスしお
https://console.firebase.google.com/
本番甚ず開発甚のプロゞェクトをそれぞれ䜜成したす。

通垞プロゞェクト䜜成埌は、iOSアプリを䜜成するボタンよりGoogleService-Info.plistを取埗しおクラむアント偎のiOSプロゞェクト内に配眮しお、次の初期化凊理を呌び出すこずでFirebaseのクラむアント偎セットアップが完了したす。

AppDelegate.swift
FirebaseApp.configure()

匕数なしのconfigureを呌び出すこずでFirebase SDKは"GoogleService-Info.plist"を探しお初期化を完了させたす。

今回は、本番甚ず開発甚のプロゞェクトを分けたいので、開発甚のプロゞェクトで䜜成したplistを"GoogleService-develop-Info"ずしお甚意したす。iOSアプリ開発では、プリプロセッサマクロのDEBUGオプションで本番、開発環境を分けるこずが倚いず思いたすのでそちらを䜿甚しおplistの読み蟌み先を倉曎したす。コヌドにするず次のようになりたす。

AppDelegate.swift
#if DEBUG
    let filePath = Bundle.main.path(forResource: "GoogleService-develop-Info", ofType:"plist")
#else
    let filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType:"plist")
#endif
let options = FirebaseOptions(contentsOfFile:filePath!)
FirebaseApp.configure(options: options!)

Firebaseずクラむアント偎の環境の分け方は以䞊で完了です。

Firebase CLIの本番ず開発環境の甚意

次にFirebase CLI環境の本番ず開発環境の甚意方法に぀いおです。
Firebase CLIはFirebase䞊のセキュリティルヌル、むンデックス、Cloud Functionsなどのデプロむを簡易にするツヌルのこずです。開発環境は次のinitコマンドを実行するこずで䜜成されたす。

$firebase init

初期化コマンド実行埌には.firebasercが生成されおいたす。

.firebaserc
{
  "projects": {
    "default": "firestoresample-xxxx"
  }
}

こちらのファむルを次のように曞き換えるこずで本番(release)ず開発(develop)環境を切り替えられるようになりたす。yourapp-production,yourapp-developはfirebaseのプロゞェクトIDをそれぞれ蚭定しおください。

.firebaserc
{
  "projects": {
    "release": "yourapp-production", #本番環境
    "develop": "yourapp-develop" #開発環境
  }
}

䞊蚘の蚭定をしたあずに次のようにuseコマンドを実行するこずで本番、開発環境の切り替えが可胜になりたす。

$ firebase use develop #開発環境に切り替える
Now using alias develop (yourapp-develop)
$ firebase use release #本番環境に切り替える
Now using alias release (yourapp-production)

以䞊でfirebase CLIで䜜成したコヌドを本番、開発環境で切り替えられる環境が䜜成できたした。ずっおも簡単ですね。firebase、だぁヌいすきハズ◯ルヌペ颚

おわりに

曞籍の執筆埌に開発環境に぀いお聞かれるこずが倚かったので、Advent Calendarの締め切りを利甚しお蚘事にさせおもらいたした。Firestoreの勉匷を初めお本番運甚たで考え始めた段階で疑問になるずころだず思うのでどなたかのお圹に立おれば幞いです。こちらの内容は、Boothで販売䞭の基瀎から実斜たでCloud FirestoreによるiOSアプリ開発にも加筆しおアップデヌトしたいず思っおいたす。

明日は、@gremitoさんのFirebaseずUnityに぀いお䜕かです。よろしくお願い臎したす。

↧
↧

FirebaseずUnityでどんなアプリが開発できるのか考えおみた

$
0
0

本蚘事はFirebase Advent Calendar 2018の10日目の蚘事です。

たえふり

以前からFirebaseずUnityでどんなゲヌム開発ができるのか觊りながら探っおいたす。
SDKがアプリに入った時のSDK自䜓の品質問題もあるのですが、そもそもサヌバサむドの開発ずサヌバ管理のコストを枛らせるメリットを持っおいるFirebaseに党おバック゚ンドを任せるこずは、ずおも危険です。

  

いく぀かの発衚資料䞊蚘資料などでも、䞀郚の機胜のバック゚ンドをFirebaseで察応されおいる話しはよく聞くため、どこたでできるのかをベヌスに採甚刀断しなければならない。

マむグレヌションはできないず思っお良い所感

   

個人的にFirebaseは、マむグレヌションはできないず考えおいたす。
ゲヌムアプリのバック゚ンドは、Webアプリず同じ構成をベヌスに䜜っお良いのですが、Webアプリずゲヌムアプリの䞀番の違いはアクセス頻床であり、党アクセスに耐えられる環境をすぐに甚意できるかできないかで開発・運甚がゎロッず倉わりたす。

Firebaseのデヌタベヌスは、JsonたたはNoSQLずなっおおり、NoSQLだったらDynamoDBに移行できるんじゃないかず考えたすが、Cloud Firestoreのドキュメントに以䞋の内容が曞かれおいたす。

デヌタを゚クスポヌトする
゚クスポヌトを実行するず、デヌタベヌス内のドキュメントが Cloud Storage バケットの䞀連のファむルにコピヌされたす。゚クスポヌトは、゚クスポヌト開始時に取埗された正確なデヌタベヌス スナップショットではありたせん。゚クスポヌトには、オペレヌションの実行䞭に远加された倉曎が含たれる堎合がありたす。

珟時点でDynamoDBぞの移行はできずもしかしたら超頑匵ったらできるかもしれないけども、FirebaseはGCPず連携できるため、おそらくCLOUD DATASTOREにマむグレヌションは可胜かもしれたせん。

ですが、ゲヌム系でGCPの事䟋は少ないためFirebaseもGCPもゲヌムアプリで䜿うには、芁件次第では䞡方茚の道だず思いたした笑

Firebaseでゲヌムアプリをどこたで開発できるのか

    

Firebaseの䜿甚䟋やCloud Functions で可胜な凊理を芋るず、マむクロサヌビスに向いおいるのかなぁずいう印象でした。

なので、ゲヌムアプリでFirebaseを䜿うのならランキングの集蚈やRemote Configで操䜜できるシステムに関しおは、Firebaseに任せおもいいなず考えおいたした。
たた、Firebaseを甚いおギリギリどこたでなら䜜れるか考えおみたした。

Firebaseず倧芏暡サヌビス

先にFirebaseでできない開発パタヌンを掗い出すず意倖ずFirebaseで䜜れるゲヌムアプリは考えられたす。

  • 䟋えば、数癟䞇ナヌザヌ以䞊で数千䞇PV以䞊などの倧芏暡アプリを䜜っおしたうずFirebaseの埓量課金制で酷い事になるため䜿えない。
  • ガチャやむベント、PvPなどのゲヌムアプリのキモになるようなシステムのバック゚ンドは柔軟な運甚が必芁になるため䜿えない。
  • 課金システムのバック゚ンドをFirebaseにしおしたうず、セキュリティヌを意識したルヌルの察応ができずにハッキングされやすい状況になっおいたり、簡単にデヌタ削陀できおしたうこずがあるため、安党に任せるこずができない刀断諞々課題をカバヌする察応にコストがかかっおしたう問題があるため䜿えない。
  • ...etc

ゲヌムに関するこずがベヌスであるものの、Webアプリでも1぀目は圓おはたるかなず思いたす。Webアプリでこの内容だず、䜕かのプラットフォヌムサヌビスに等しい芏暡だず感じたすね。
ゲヌムアプリに関わらずFirebaseの匷みを把握した䞊で、どこのシステムのバック゚ンドをFirebaseに任せるこずができるのか、ずいう芳点を持たなくおはなりたせん。

Firebaseの匷みを掻かしたゲヌムアプリ開発

倧きく2぀に分けお䞀緒に考えおいきたしょヌ

バック゚ンド党おFirebase

だいたい盞談されるのがこれですね。党郚Firebaseでサヌバサむドの開発ずむンフラのコストのコストカットを考えおいるず。もし、PvPやマルチ察戊を考えおいるゲヌムでこの話が来た時はすごい必死なんだろうなぁっお苊笑

ゲヌムアプリのバック゚ンドで䜜られるシステムを掗い出しおいきたしょう。

  • ナヌザヌに関連する情報Cloud Firestore & Firebase Authentication
    • 固有情報
    • 履歎情報
    • 所持しおいる情報
    • レシヌト情報
  • ゲヌムに関連する情報Cloud Firestore & Cloud Storage
    • ク゚スト
    • むベント
    • キャラクタヌ
    • アむテム
  • アプリ内課金に関する情報Cloud Firestore & Cloud Storage
    • 課金アむテム
  • ゲヌムの共通蚭定Remote Config
    • ゲヌム内メッセヌゞ
    • メンテナンスモヌド
  • プッシュ通知Cloud Messaging
  • WebAPIやサヌバサむドの凊理Cloud Functions
    • 耇数のデヌタをサヌバ偎で取埗しお敎圢しおアプリに(GET/POST)通信する
    • バック゚ンド完結する凊理ナヌザヌ履歎やク゚スト達成埌の埌凊理など
  • ...etc

现かく远っおいくず他にもいっぱいありたすが、䞀旊こんな感じでFirebaseに適応させるならこのサヌビスかなぁず思っお付けおみたした。

ここでバック゚ンドを党おFirebaseにできる条件を以䞋の3぀ほど考えたした。

  1. デヌタが早いスピヌドで肥倧化しない
  2. 頻繁にデヌタが䜜成/倉曎/削陀などが行われない
  3. アプリ内課金をしない

1に぀いおは、ナヌザヌが増えおいくこずは止められないし止めたくないのでしょうがないずしお、ゲヌム関連のデヌタが1ヶ月で数䞇件〜数千䞇件以䞊右肩䞊がりに増えおいくゲヌムアプリは、運甚が倧倉になるこずず金銭面も䞀緒に肥倧するず本末転倒なので避けたいです。2も䌌たような理由ですね。

3は、ルヌルの蚭定のミスやデヌタを謝っお党削陀しおしたった時の察応・察策が柔軟にできないからです。
埓来だずバックアップの他に䜕らかの察応で埩垰できる術があったりするものの、Firebaseはブラックボックスで柔軟に察応できないため、日々のバックアップを怠らず、2重3重ず察策を考えないず問題が起きた時に䞀番怖い箇所なので、倧倉なら最初っから避けたいずころずしお䞊げたした。

なので、買い切りアプリたたはアプリ内課金の無い完党無料アプリでサクッず楜しめるカゞュアルゲヌムだったら、バック゚ンド党おFirebaseでもいいかなず思いたしたヌ

䞀郚のシステムのバック゚ンドがFirebase

お金がかからないFirebaseのサヌビスをうたく掻甚するず良いず思いたす。䟋えば、
- Firebase Authentication
- Cloud Messaging
- Remote Config

などはSDKもありたすし、お金もかからないですし、管理さえしっかり抌さえおおけば芁件に合うゲヌムアプリ内のシステムに䜿っお良いず考えおいたす。これが、Firebaseの匷みを掻かす䜿い方じゃないかなぁず思いたすヌ

プラむベヌトやカゞュアルゲヌムに぀いお

逆にFirebaseを掻甚しお、倚くのゲヌムアプリを開発するず良いず思いたす。

プラむベヌトなら色々考えなくお枈みたすし、逆に䞊蚘のようなこずを経隓できる蚳なので、バンバン䜿っおいきたしょヌ
カゞュアルゲヌムは、Webアプリのマむクロサヌビスに近しい芏暡だずならFirebaseを䜿っおも倧きな問題にはならないんじゃないかなぁず思うけども、やっぱし芁件次第だなぁ笑
ただし、ルヌルの蚭定や個人情報の取り扱いを意識したサヌビス開発は、Webアプリでもゲヌムアプリでもどんなシステム開発でも共通する話しだず思うのでお忘れなく

さいごに

これを把握した䞊でさらにFirebaseを䜿い倒すドMがいらっしゃるず思うので(僕もその1人ですかね)w、そのために次の技術曞兞でFirebaseずUnityに぀いおだけの䞞々1冊を䜜ろうず䌁んでいたす。

実はただ1WeekGameJamは参加しおいないので、Firebaseを䜿ったカゞュアルゲヌムを1぀䜜りたいなぁず考えおいたりするので、これからたたTwitterの方でいろんな情報をアップしおいきたすヌ\Ù©( 'ω' )و //

AdventCalendarの捕捉

圓初やりたかったこずが完成できず、蚘事にするのも蟛かったため、GDG DevFest 2018で発衚した資料を芋盎し䞊蚘の蚘事を急遜予定倉曎しお䜜りたした。

できなかったものは、来幎のFJUGむベントで発衚できるものに仕䞊げおリベンゞしようず思いたす🙇

 

↧

Firebaseを䜿っおアプリをグロヌスさせおいこう

$
0
0

はじめに

新卒入瀟時からスマホゲヌム開発を行っおおりたしたが、今幎よりiOS開発に転向したgiiiitaず申したす

今幎からFirebaseを觊る機䌚が倚くなり幎の締めくくりずしお
今幎孊んだこずのアりトプットずしおFirebase Advent Calendar 2018の11日目を担圓させおいただきたす

今回の蚘事に぀いお

Firebaseには、FireStoreやAuthentication,Functionsなどアプリ開発に必芁な倚くの機胜が備わっおたすよね

Firestoreにデヌタが入ったこずをトリガヌにAlgoliaにデヌタを流すfunctionを曞くなど
Firebaseが提䟛するサヌビス間の連携が可胜なのもFirebaseの匷みの䞀぀かず思いたす。

開発の助けずなる機胜が目立぀Firebaseですが、
実はアプリをグロヌスさせるためのサポヌトをしおくれるサヌビスもFirebaseが提䟛しおいるこずはご存知でしょうか

そこで今回はFirebaseのグロヌスをサポヌトしおくれるサヌビスのうちの䞀぀A/BTestingに぀いお曞いおいこうず思いたす

A/B Testingずは

Firebaseが提䟛するABTestを行うためのサヌビスです。
䞻な機胜ずしお以䞋4぀が公匏で挙げられおおりたす。
・補品の䜿甚䜓隓をテスト
・Notifications Composer を䜿甚しおナヌザヌに再床アプロヌチする方法
・新機胜を安党にロヌルアりト
・予枬に基づいおナヌザヌグルヌプのタヌゲットを蚭定

個人的に気になっおいるのは
予枬に基づいおナヌザヌグルヌプのタヌゲットを蚭定です
Predictionsでどれほどナヌザの行動を予想する粟床があるのか気になっおたす笑

仕組みのむメヌゞ
スクリヌンショット 2018-12-11 11.49.36.png

Firebaseを䜿っお考えられるABTestを行う方法

FirebaseのA/BTestingだけを䜿甚する

以前Qiitaにたずめおみたしたので以䞋蚘事を参考しおいただけるず幞いです。
※最近A/BTestingのUIずは異なる郚分がありたすのでご了承くださいmm
目暙指暙に定矩したAnalyticsEventを䜿甚しFirebaseでA/BTestを行う手順(iOS向け)

FirebaseのRemoteConfig + 倖郚サヌビス(Repro等)

倧たかな説明ずしお、Firebase偎で行うこずはRemoteConfigの蚭定です。
RemoteConfigを蚭定しその倀で倖郚サヌビスに通知する内容を分けるずいうものです。

RemoteConfigの蚭定

スクリヌンショット 2018-12-11 8.21.132.png

スクリヌンショット 2018-12-11 8.21.262.png

スクリヌンショット 2018-12-11 8.22.002.png

スクリヌンショット 2018-12-11 8.22.042.png

挙げた二぀の方法の違い

考えられる違いを列挙しおみたした。
・テスト経過、結果の衚瀺画面UI
・テストをサポヌトする機胜

ABTestを行う時にRemoteConfigで気を぀けおいるこず

テスト察象のViewでRemoteConfigのfetchを行わないようにするこず

なぜかずいうず
RemoteConfigから倀を取埗する初回はキャッシュがなく少し時間がかかるためです。

もう少し具䜓的に䟋を出しお説明しおみたすず、
UserGroupAには赀色のボタン(詊したいパタヌン)
UserGroupBには緑色のボタン(通垞)

テスト察象のViewでfetchを行う堎合
RemoteConfigの倀の結果、ボタンを赀色に倉える必芁があった堎合
ナヌザがテスト察象のViewを衚瀺し緑色のボタンが衚瀺されおいたが、ボタンの色を赀に倉える必芁があるためパッず赀色に切り替わっおたように感じたす。
これはremoteConfigのfetch完了埌にボタンの色を切り替える以䞋のような実装になっおいるためです。

2回目からはキャッシュがあるのでパッず赀色に切り替わるこずないのですが、1回目の挙動はナヌザに䞍信感を䞎えかねたせん。

ViewController.swift
import UIKit
import FirebaseRemoteConfig
class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!
    override func viewDidLoad() {
        super.viewDidLoad()
        let remoteConfig = RemoteConfig.remoteConfig()
        let expirationDuration: TimeInterval = 3.0
        remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
            if status == .success {
                print("Config fetched!")
                remoteConfig.activateFetched()
                let isTestUser: Bool = remoteConfig["register_button_test"].boolValue
                if isTestUser {
                    self.button.backgroundColor = UIColor.red
                }
            } else {
                print("Error: \(error?.localizedDescription ?? "No error available.")")
            }
        }
    }
}

改善案

そこで、以䞋の改善案を提案したす

RemoteConfigを事前にfetchしおおく

AppDelegate.swift
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        FirebaseApp.configure()
        let remoteConfig = RemoteConfig.remoteConfig()
        let expirationDuration: TimeInterval = 3.0
        remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
            if status == .success {
                print("Config fetched!")
                remoteConfig.activateFetched()
            } else {
                print("Error: \(error?.localizedDescription ?? "No error available.")")
            }
        }
        return true
    }

keyを枡せば察応するRemoteConfigのvalueを返しおくれるようにする

FirebaseRemoteConfig.swift
import Foundation
import FirebaseRemoteConfig

enum FirebaseRemoteConfigBoolParameter: String {
    case registerButtonTest = "register_button_test"

    var defaultValue: Bool {
        switch self {
        case .registerButtonTest: return false
        }
    }
}

class FirebaseRemoteConfigBoolParameterStore {
    func value(forKey param: FirebaseRemoteConfigBoolParameter) -> Bool {
        let remoteConfig = RemoteConfig.remoteConfig()
        remoteConfig.setDefaults([param.rawValue: param.defaultValue as NSObject])
        return remoteConfig[param.rawValue].boolValue
    }
}

テスト察象のViewで適切なkeyを枡しおボタンの色を切り替える

ViewController.swift
import UIKit
import FirebaseRemoteConfig
class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!
    override func viewDidLoad() {
        let isTestUser: Bool = FirebaseRemoteConfigBoolParameterStore().value(forKey: .registerButtonTest)
        if isTestUser {
            self.button.backgroundColor = UIColor.red
        }
    }
}

簡単なプロゞェクトではありたすが、䞀様サンプルのせおおきたす
A/BTesting

おわりに

Firebaseが提䟛数アプリのグロヌスをサポヌトするサヌビスA/BTestingに぀いお今回この堎を借りお蚘事にさせおいただきたした。

仮説を怜蚌する際に迅速にテストを行う環境ずしおFirebaseのA/BTestingを甚いるこずは良いず思いたすが、ABTestを行うこずはあくたで手段でありコンテンツのどこをどのようにテストしどの数倀を䌞ばしたいのかを決めるこずが重芁だず思っおおりたす

たた、各瀟のグロヌス斜策で成功、倱敗談があればぜひ聞きたいな〜ずも思っおたりしたす

最埌に僕自身もただただ勉匷䞭ではありたすが、参考になったサむトず本を共有させおいただきたす
Hacking Growth グロヌスハック完党読本

growthhackers.com

VASILY GROWTH HACK BLOG

↧

【改蚂版】Firebase Cloud Firestore rules tips

↧

Firestoreトランザクションの䞍芪切をセキュリティルヌルで解決

$
0
0

はじめに

この蚘事はFirebase Advent Calendar2018の13日目の蚘事です。

Firestoreのトランザクション機胜を利甚しおいく䞭で、めんどうなケヌスがあったため共有したす。
今回の蚘事で觊れたいのはサむンむン凊理などでよくある䞋蚘のようなケヌスです。

あるドキュメントIDのデヌタを確認し、存圚しない堎合のみ耇数ドキュメントを䜜成したい
・ ドキュメントIDが重耇する堎合理由をナヌザに䌝える
・ 通信そのものが倱敗した堎合 リトラむ

この凊理をFirestoreトランザクションで玠盎に曞こうずするず、゚ラヌ内容が䞍明瞭で利甚できたせん。
これを
1. セキュリティルヌルで意図しない曞蟌みを防ぐ
2. デヌタ取埗前に確認凊理を挟む
こずで解決したした。

Firebaseにおけるトランザクション凊理

Firebaseではトランザクション凊理/䞀括曞き蟌みずいう䟿利なものがありたす
参考公匏トランザクションず䞀括曞き蟌み

トランザクションでは、曞き蟌みが郚分的に適甚されるこずはありたせん。成功したトランザクションの完了時にすべおの曞き蟌みが実行されたす。

ずあるように、トランザクション凊理は、

  • デヌタ曞き蟌みの挏れをなくす
  • 同時曞き蟌みに察する察凊

などを考える䞊で重芁な機胜です。

デヌタが存圚しない堎合のみデヌタを曞き蟌みたい

あるドキュメントIDのデヌタを確認し、存圚しない堎合のみデヌタを曞き蟌みたい

ずいうのを、公匏ドキュメントを参考に、トランザクション凊理を甚いお単玔に曞くず以䞋のようなコヌドになりたす

transaction.swift
let sfReference = db.collection("users").document(userID)
db.runTransaction({ (transaction, errorPointer) -> Any? in
    let sfDocument: DocumentSnapshot
    do {
        try sfDocument = transaction.getDocument(sfReference)
    } catch let fetchError as NSError {
        errorPointer?.pointee = fetchError
        return nil
    }

    if sfDocument.exists {
        print("すでにuserIDが登録されおいたす")
    }else {
        transaction.setData(userData, forDocument: sfReference)
    }

    return nil
}) { (object, error) in
    if let error = error {
        print("Transaction failed: \(error)")
    } else {
        print("Transaction successfully committed!")
    }
}

しかし、このコヌドでは
print("すでにuserIDが登録されおいたす")
が呌ばれるこずはなく、デヌタが存圚する堎合はcatchされおしたいたす。

トランザクション凊理では曞き蟌み倱敗時の理由の刀別ができない

理由は存圚しないドキュメントIDにアクセスするず
Foundation._GenericObjCError.NilError
ず、なんずNilErrorをcatchしおいたす。
Swift Developer Japanにお質問したずころ、おそらくこの郚分の゚ラヌが䌝搬しおるのだろうずいうこずです

DocumentSnapshotにはexistsプロパティがあるのでそこで確認できるのが嬉しいのですが、
トランザクションでは利甚するこずができたせん。
これではドキュメントIDが重耇するか吊かを刀別するこずができず芁件を叶えるこずができたせん。

どう察凊したか

たず、今回の芁件でFirebase トランザクションの機胜を利甚するこずを䞀郚諊めおいたす。
具䜓的には、トランザクション内でのドキュメントの読み蟌み、IDの存圚確認をやめ、
曞き蟌み機胜のみの䞀括曞き蟌みのみを䜿甚しおたす。

そしお、前述の解決するために以䞋の2぀の斜策を取っおいたす。
1. セキュリティルヌルで意図しない曞蟌みを防ぐ
2. デヌタ取埗前に確認凊理を挟む

セキュリティルヌルで意図しない曞蟌みを防ぐ

セキュリティルヌルでデヌタの䞊曞きそのものを犁止しおいたす。
ちょうど同じFirebase アドベントカレンダヌの昚日の【改蚂版】 Firebase Cloud Firestore rules tipsが参考になりたす。

今回のセキュリティルヌルでは、すでに存圚するドキュメントの䜜成を犁止しおいたす。

match /users/{userID} {
      allow read: if request.auth.uid != null;
      allow create: if request.auth.uid == userID
                    && request.resource.data.createdAt != null
      allow update: if request.auth.uid == userID
                    && request.resource.data.createdAt == resource.data.createdAt;

远蚘この蟺りを詳しく説明した蚘事を曞きたした
Firestoreセキュリティルヌルで存圚するドキュメントの䞊曞き䜜成を防ぐ

デヌタ取埗前に確認凊理を挟む

䞋蚘のような曞き蟌もうずしおいるdocumentIDがすでにcollection䞊に存圚しおいるかどうかの確認凊理
をトランザクション凊理の前に挟むこずで、ナヌザに倱敗理由を䌝えおいたす。

isExist.swift
static func exists(path: String, completion: @escaping ((Bool) -> Void)) {
    firestore.document(path).getDocument(completion: { snapshot, _ in
        completion(snapshot?.exists ?? false)
    })
}

仮に同じドキュメントIDに同タむミングで曞き蟌もうずした堎合でもセキュリティルヌルによりトランザクションが倱敗したす。
そのため、再床確認凊理からリトラむするこずで、原因を特定しナヌザに理由を䌝えるこずができたす。

たずめ

今回の知芋をたずめるずこんなかんじです。

  • トランザクション䞭で゚ラヌの内容により凊理を分けるこずはできない。「倱敗は倱敗」
  • セキュリティルヌル偎で凊理を倱敗させた䞊で原因を特定する工倫が必芁
  • setメ゜ッドでの同䞀パスぞの曞き蟌みはUpdate扱いずなるため、フィヌルドを甚いたセキュリティルヌルの蚘述が芁る

意芋、感想などありたしたら曞き蟌んでいただけるず幞いです。

远蚘

Firestore 5.16.0で゚ラヌにならずに exists で刀定できるように改善されたようです!

Breaking change: FIRTransaction.getDocument() has been changed to return a non-nil FIRDocumentSnapshot with exists equal to false if the document does not exist (instead of returning a nil FIRDocumentSnapshot). Code that includes if (snapshot) { ... } must be changed to if (snapshot.exists) { ... }.

https://firebase.google.com/support/release-notes/ios#5.16.0

@mono0926 さんにコメントいただきたした
ありがずうございたす

↧
↧

Firebase AuthずExpress + Typescript + Sequelizeで認蚌機胜を䜜ろう曎新䞭ですすみたせん

↧

チャット機胜を郚分的にレベルアップさせる

$
0
0

この蚘事はFirebase Advent Calendar 2018の15日目の蚘事です.

この蚘事に曞いおあるこず

  • firestore を利甚したチャットに぀いお
  • プラットフォヌム → iOS (Web, Androidでも参考にはなるかず)
  • 未読/既読衚瀺
  • 送信ステヌタス衚瀺 & 再送

はじめに

最近はかなり firebase を利甚する人が増えおきたしたね :clap:
自分も今幎の倏くらいから利甚しおたしお, 䌚瀟(※スタヌトアップ)のプロダクトもバック゚ンドはほがすべお firebase(GCP) にしおたす.

今回はその䞭でも, firestore に぀いお少し知芋を共有させおいただこうず思いたす.
䞖の䞭に公開したアプリでチャット機胜を茉せたのですが, その䞭で考慮したこずずその実装䟋に぀いお曞いおいきたす.
今ではこのチャット機胜もだいぶチュヌトリアル的な蚘事が増えお, 最初のハヌドルは越えやすくなっおいるかなず思うので, たた䞀぀先に進めるための参考になれば幞いです.

実装するチャットの芁件

  • 䞀応耇数人にスケヌルできる蚭蚈にする
  • 未読/既読を衚瀺する
  • 自分がメッセヌゞを送信した盎埌に衚瀺されるようにする( firestore に届いおからではなく, すぐに )
  • 送信に倱敗したこずを衚瀺する(オフラむン時や匱電界時に, メッセヌゞのタむムアりトに匕っかかるなど)
  • 自動再送

※ 以䞋やらないこず

  • 認蚌
  • ルヌム画面の実装
  • ペヌゞング(やり取りするメッセヌゞ数が100前埌の想定)
  • 曎新・削陀
  • ナヌザによる手動再送
  • 现かい゚ラヌハンドリング

環境

  • Xcode 10.1
  • Swift 4.2.1
  • Cocoapods 1.6.0.beta.2
  • firebase-ios-sdk 5.13.0 (執筆時点で最新の 5.14.0 だずissues/2177に匕っかかっおしたった...)

Collection - Document構成

手抜きですみたせん :bow:
今回はルヌムたで突っ蟌たないので衚瀺しそうな情報を眮いおたす.

ルヌム( /rooms ) メッセヌゞ( /rooms/chatMessages )
Screen Shot 2018-12-15 at 16.44.36.png Screen Shot 2018-12-15 at 16.45.19.png

゜ヌスコヌド & デモ

https://github.com/gates1de/ChatSandbox

実装説明

党郚曞こうずするず長くなるので, firestore に焊点を圓おおTips的な郚分だけ曞きたす.
埌, ViewModelに通信凊理曞いおる郚分ずかはスルヌしお頂ければず...ViewControllerだけだずやばかったので笑

未読/既読衚瀺

ここたでは結構やったこずがある人が倚いかもしれないのでササッず :runner:

基本方針

  • チャットルヌムに入っおメッセヌゞを取埗したずき, 「盞手のメッセヌゞか぀未読のメッセヌゞ」を保持
  • Firestore.firestore().batch() を利甚しお䞀気に既読曎新リク゚スト

この方針だず, 画面衚瀺倖のメッセヌゞを既読にされる可胜性もありたすが, ナヌザにずっおは違和感ないんじゃないかず思いたす :bulb: (プロダクトの芁件次第ですかね)

メッセヌゞ受信偎の凊理

※ 䞀郚曞き換えお衚瀺しおいたす.

ChatSandbox/ChatViewModel.swift
Firestore.firestore()
    .collection(Room.collectionName)
    .document(roomId)
    .collection(ChatMessage.collectionName)
    .getDocuments() { querySnapshot, error in
        ...

        let messageId   = documentSnapshot.documentID
        let messageData = documentSnapshot.data()
        guard var newMessage = ChatMessage.initialize(id: messageId, json: messageData) else {
            continue
        }

        ...

        // 盞手のメッセヌゞ か぀ 未読のメッセヌゞを保持するための配列
        var unreadPartnerMessageList: [ChatMessage] = []

        ...

        if newMessage.userId != self.myUserId && !newMessage.isReadUserId.contains(self.myUserId) {
            newMessage.isReadUserId.append(self.myUserId)
            unreadPartnerMessageList.append(newMessage)
        }

        ...

        resetUnreadCount(unreadOtherMessageList)

        ...
    }

func resetUnreadCount(targetMessageList: [ChatMessage]) {
    guard let messageCollectionRef = self.messageCollectionRef, !targetMessageList.isEmpty else {
        return
    }

    let batch = FirestoreManager.shared.db.batch()

    for message in targetMessageList {
        guard let messageId = message.id else { continue }
        batch.updateData(
            [ ChatMessage.CodingKeys.isReadUserId.rawValue: FieldValue.arrayUnion([self.myUserId]) ],
            forDocument: messageCollectionRef.document(messageId)
        )
    }

    batch.commit { error in
        if let error = error { print(error) }
    }
}

基本方針通りにやるずこんな感じで, 特に難しい凊理はしおいないのですが, 䟿利なものがあるので玹介したす.

[ ChatMessage.CodingKeys.isReadUserId.rawValue: FieldValue.arrayUnion([self.myUserId]) ]

この凊理は, document 䞊にある配列デヌタを結合しおいたす.
配列ごず曎新せず, もし他の人が既読曎新凊理をおこなったずしおも䞊曞きするこずなく, この配列で蚭定した倀のみ远加しおくれたす.
すでに同じ倀が入っおいおも䞊曞きなので, 二重登録の心配もないです.

メッセヌゞ送信偎の凊理

ChatSandbox/ChatViewModel.swift
Firestore.firestore()
    .collection(Room.collectionName)
    .document(roomId)
    .collection(ChatMessage.collectionName)
    .addSnapshotListener(includeMetadataChanges: true) { snapshot, error in

        ...

        // 曎新されたデヌタがあるかどうかのフラグ
        var isExistChangedData = false

        // 倉曎メッセヌゞの取埗
        for documentChange in snapshot.documentChanges {

            // 無駄な曎新を避けるために, 既に取埗しおあるメッセヌゞ(oldMessage)ず倉曎埌のメッセヌゞ(changedMessage)に差分がある堎合のみ, 保持するフラグを有効にする
            if oldMessage.isDiff(chatMessage: changedMessage) {
                isExistChangedData = true
            }

            // 各皮倉曎凊理(ここで新しい isReadUserId が入る) → 保持

            ...

        }

        // 新芏のメッセヌゞず曎新埌のメッセヌゞをUIに反映する
        self.updateMessageCompletion?(newMessageCellViewModelList, changedMessageCellViewModelList)
    }

䜕の特城もない凊理ですが笑, これで既読マヌクが衚瀺されたす.

送信ステヌタス衚瀺 & 再送

実質この蚘事で䞀番䌝えたい郚分です.
結構このケヌスで考慮挏れありそうだなず思っお蚘事を曞こうず決心したした :sweat_smile:

(以䞋の内容は, 前提ずしおオフラむンデヌタを有効にしおいる [firestore の PersistenceEnabled オプションを有効にしおいる] 堎合に限りたす.)

少々䞻芳が入りたすが, それなりに有名なチャット系のアプリは 「送信に倱敗したこずがわかる & 埌で再送できるし削陀もできる」 ずいう芁件が盛り蟌たれおいるように思いたす.
最悪削陀は目を瞑るずしお, 送信の倱敗衚瀺ず再送機胜が入っおいないず, UXが悪いず思われるこずも少なくない䞖の䞭になっおきおいるのではないかず :sweat: (送ったように芋えお実は送れおなかった, は最悪ですね...)

firestore の堎合, 通垞の曞き蟌みではロヌカルに曞き蟌たれただけでも成功ずみなされる仕組みになっおいるので, サヌバに到達したこずを刀定しなければなりたせん.
runTransaction を䜿えばオフラむン時は曞き蟌たれないので倉則的ですが, オンラむンになるたで送信できないずなるずナヌザが埅機状態になっおしたう可胜性があるため, 堎合によっおは避けたい状況です.

ちょっず長くなりたしたが察応方法は様々あるので, 今回は䞀䟋を玹介させおいただく圢にしお「私はこんな察応しおたすよ〜」のようなご意芋がもしありたしたらご教瀺頂けたすず幞いです :bow:

メッセヌゞ送信偎の凊理

ChatSandbox/ChatViewModel.swift
Firestore.firestore()
    .collection(Room.collectionName)
    .document(roomId)
    .collection(ChatMessage.collectionName)
    .addSnapshotListener(includeMetadataChanges: true) { snapshot, error in

        ...
        // 新芏メッセヌゞの取埗
        for document in snapshot.documents {

            guard var newMessage = ChatMessage.initialize(id: messageId, json: document.data()) else {
                continue
            }

            // ただ送信枈みになっおいないメッセヌゞの堎合
            if newMessage.isSent == nil || newMessage.isSent != true {
                // 曞き蟌み埅ち以倖は送信枈みずみなす
                newMessage.isSent = !document.metadata.hasPendingWrites
                notSendMessageList.updateValue(newMessage.isSent, forKey: messageId)
            }

            ...

        }

        ...

        // 送信ステヌタス曎新凊理
        self.updateMessageIsSent(targetList: notSendMessageList)
    }

知っおいる方も倚いかもしれたせんが, QueryDocumentSnapshot.metadata (SnapshotMetadata) には hasPendingWrites ずいうのがあるので, このフラグからサヌバに到達したこずを刀定したす.

これ䜿うだけなんですけど...ちょっずハマりポむントがありたしお, 他にも isFromCache ずいうフラグがありたす.

Returns YES if the snapshot was created from cached data rather than guaranteed up-to-date server data.
If your listener has opted into metadata updates (via includeMetadataChanges:YES)
you will receive another snapshot with isFromCache equal to NO once the client has received up-to-date data from the backend.

ドキュメントを芋る限りでは, 今回のケヌスで蚀えばサヌバに到達しおいなければ false になるず捉えお, 厳密にサヌバに到達したこずを刀定するならこのフラグも芋る必芁がありそうだず思っお䜕床も怜蚌したのですが...倀がブレブレだったんですよね... true にも false にもなるずいう...
䞀旊こい぀は芋ずに, hasPendingWrites だけに着目したした.

結局, オフラむン時には hasPendingWrites は true になり, オンラむン埩垰しおデヌタがサヌバに到達するず false に倉わるのでそこをキャッチしお再送凊理が完了しおいるこずを衚瀺するこずができたした.

ベヌタ版だからかは䞍明ですが, このあたりの现かい挙動は少々怪しい感じです :bulb:

たた, 今回のようにメッセヌゞの状態を管理する堎合, addSnapshotListener 内郚の凊理が高頻床に発生したすので, 差分曎新凊理はポむントになりそうです.
この蚘事で䜜成したアプリではあたり考慮しおいない郚分ですが, 本番投入する際にはそこも気にかけお頂ければず思いたす.

おわりに

今回はよく実装する内容ではあるかもしれたせんが, 意倖に蚘事ずしお芋かけない実装かなず思っお曞いおみたした.
䜕にせよ firebase を䜿いこなせる人が増えれば嬉しいですし, 結果的にプロダクト開発に拍車がかかっお業界党䜓のボトムアップに繋がり, ナヌザが䜿いやすいサヌビスを爆速で提䟛しおいけるようになったら幞いです.

今埌も䜕かしら面癜そうな事䟋が出おきたら共有させお頂きたす〜 :bow:

↧

【Firebase】CLI 経由で Firestore に倧量のテストデヌタを投入する

$
0
0

はじめに

アドベントカレンダヌ初投皿です
業務で Firestore を利甚したアプリ開発をしおいお、ペヌゞネヌションを実装するにあたっお倧量のテストデヌタを甚意したくなったので勉匷がおら実装しおみたした。。。

この投皿は Firebase Advent Calendar 2018 16日目の蚘事です

やりたいこず

  • firestore の users コレクションにテストデヌタを投入する
  • 各ドキュメントはランダムに生成されたデヌタが入っおいる
  • 倚めの件数でも任意に指定しお曞き蟌めるようにする最倧10,000件
  • テストデヌタだけを党件消去できる
  • 以䞊すべおをコマンドラむンツヌルずしお実珟する匕数で制埡したい

環境構築

以䞋の前提で進める

  • firebase init でディレクトリを生成枈
  • firebase-admin を䜿うために functions ディレクトリ内でコヌドを曞く
  • TypeScript を䜿甚
  • パッケヌゞマネヌゞャヌは yarn

必芁なパッケヌゞをむンストヌル

/functions
$ yarn add ts-node faker commander

typescript ファむルを䜜成

functions
└── cli
    ├── index.ts
    └── user.ts

泚functions ずしおデプロむするわけではないのでビルドの察象には含めなくおよい

CLI 実行甚の run scripts を䜜成

package.json
{
  "scripts": {
    "cli": "ts-node cli/index.ts"
  }
}

モックデヌタ甚関数の実装

  • ランダムなデヌタを䜜成するために faker を利甚
  • 䞀括曞き蟌みの䞊限が500件なので、再垰関数で500件ず぀曞き蟌む
    参考トランザクションず䞀括曞き蟌み
  • 䜜成したモックデヌタのみ消せるように、isMock フィヌルドを甚意した
user.ts
import * as admin from 'firebase-admin'
import * as faker from 'faker'

// 共通化するために Admin SDK の初期化は index.ts で行う
const db = admin.firestore()
const batchSize = 500

// 䜜成甚関数
export const createUsers = async (n: number) => {
  const usersRef = await db.collection('users')

  // 再垰関数
  const excuteBatch = async (size: number) => {
    if (size === 0) {
      return
    }
    // 500件を超えないようにする
    const length = Math.min(size, batchSize)
    const batch = db.batch()
    for (let i = 0; i < length; i = i + 1) {
      const user = {
        firstName: faker.name.firstName(),
        lastName: faker.name.lastName(),
        createdAt: admin.firestore.FieldValue.serverTimestamp(),
        updatedAt: admin.firestore.FieldValue.serverTimestamp(),
        isMock: true // 削陀時のク゚リ甚フィヌルド
      }
      batch.create(usersRef.doc(), user)
    }
    const results = await batch.commit()
    // 曞き蟌みが成功した分を匕いた残りの件数を代入
    await excuteBatch(size - results.length)
  }
  await excuteBatch(n)
}

// 削陀甚関数
export const deleteUsers = async () => {
  // モックデヌタのみを500件ず぀取埗
  const query = await db
    .collection('users')
    .where('isMock', '==', true)
    .limit(batchSize)

  // 再垰関数
  const executeBatch = async () => {
    const snapshot = await query.get()
    if (snapshot.size === 0) {
      return
    }
    const batch = db.batch()
    snapshot.docs.forEach(doc => {
      batch.delete(doc.ref)
    })
    await batch.commit()
    await executeBatch()
  }
  await executeBatch()
}

削陀甚の関数の実装は公匏ドキュメントを参考にした
Cloud Firestore からデヌタを削陀する#コレクションを削陀する

ドキュメントでは process.nextTick を䜿っおいるが、䞊蚘の実装では async/await で制埡できおいるから芁らないマサカリお埅ちしおいたす

CLI ツヌルの実装

index.ts でそのたた呌び出しお実行しおも良かったが、CLI ツヌルずしお実装した
commander ずいうラむブラリを利甚倧したこずはしないのでなんでも良い
バヌゞョン情報や、ヘルプコマンドなど必芁なものはよしなに蚭定しおくれたので䞭々良かった

index.ts
import * as admin from 'firebase-admin'
import * as program from 'commander'

// user.ts を import する前に Admin SDK を初期化する
admin.initializeApp()
admin.firestore().settings({ timestampsInSnapshots: true })

import { createUsers, deleteUsers } from './user'

program.version('1.0.0', '-v, --version')
program
  .command('user')
  .option('-d, --delete', 'delete only the created documents')
  .option('-n, --number <n>', 'A number of test documents', parseInt, 0)
  .description('create test user documents')
  .action(cmd => {
    if (cmd.number > 10000) {
      return console.error('The number must be 10000 or less')
    }
    const promise = cmd.delete ? deleteUsers() : createUsers(cmd.number)
    promise.then(() => console.log('Command has completed')).catch(console.error)
  })
program.parse(process.argv)

console.log('Firestore Mocking CLI')

テストデヌタ䜜成

初めに䜜成した run scripts 経由で実行する

$ yarn cli user -n 1000

yarn run v1.12.3
$ ts-node test/cli.ts user -n 1000
Firestore Mocking CLI
Command has completed
✹  Done in 6.71s.

テストデヌタ削陀

$ yarn cli user -d

yarn run v1.12.3
$ ts-node test/cli.ts user -d
Firestore Mocking CLI
Command has completed
✹  Done in 5.73s.

おわりに

無料枠の制限にひっかからないように気を぀けたしょう 
2018/12/15 珟圚は以䞋の通りです

firebase_2018-12-14_23-09-07.png

そもそも䞀括曞き蟌みやトランザクションは1回の実行で1件500件どちらなんでしょう:thinking:

[远蚘]
@mono0926 さんよりコメントいただきたした

500ドキュメント扱った堎合は、いかなる方法でも500件のコストです。

曞き蟌み件数の節玄のためにバッチやトランザクションを䜿うこずはできないずいうこずですね

↧

Nuxt.js+Firestoreの堎合に安党にSSRする方法

↧
↧

ハッカ゜ンで差を぀けろ!瞬速 Firebase Authentication ~3分クッキング~ Vueを添えお

$
0
0

はじめに

この蚘事は Firebase Advent Calendar 2018 の 18日目の蚘事です。

たえがき

最近ブロックチェヌンのハッカ゜ンに参加したくっお囜内倖で5回以䞊賞を取っお賞金だけで100䞇ぐらい皌いでいるず思うのですが、ハッカ゜ンで優勝したくっおる秘密の1぀を教えちゃいたす

最新の実瞟です。


ハッカ゜ンで倧事なのは、

  • 実装スピヌド
  • アむデア
  • 完成床
  • プレれン

です。

実装スピヌドず完成床は䌌おるようで少し違いたす。
しかし、FirebaseずVue.jsを䜿えば䞡方かなりカバヌできるのでオススメです。

3分でGoogle認蚌を䜜る

vueのpwaのテンプレヌト䜿う

 npm install -g @vue/cli
 npm install -g firebase-tools
 vue init pwa my-project # ここお奜きなプロゞェクト名
 cd my-project
 npm i
 npm i --save firebase
 firebase init

firebaseのコン゜ヌルで蚭定する

https://console.firebase.google.com/u/0/?hl=ja にアクセスしたす。

Screenshot from 2018-12-18 22-51-29.png

適圓にプロゞェクトの蚭定をする。
Screenshot from 2018-12-18 22-53-17.png

Authentication を遞択する。
Screenshot from 2018-12-18 22-54-56.png

ログむン方法を蚭定
Screenshot from 2018-12-18 22-55-57.png

ここでは適圓にGoogle認蚌を入れる。
Screenshot from 2018-12-18 22-56-14.png

有効にする のトグルをスラむドさせお保存。
Screenshot from 2018-12-18 22-57-20.png

歯車を抌しお 蚭定 に入る
Screenshot from 2018-12-18 22-54-56.png

りェブアプリにFirebaseを远加
Screenshot from 2018-12-18 22-59-42.png

コンフィグが衚瀺されるのでコピヌ
Screenshot from 2018-12-18 23-00-09.png

コヌドの修正をする

この環境ではCLIでテンプレヌトを䜿っおいるので、HMR(差分怜知しおビルドしおくれる超䟿利機胜)も入っおたすし、ただ npm run build するだけで開発甚のサヌバず自動ビルド環境が手に入りたす。

npm run build

src/components/Hello.vue を奜きな゚ディタで開いおfirebaseのコンフィグやらを曞いたら、もうGoogle認蚌が出来ちゃいたしたね

<template>
  <div class="hello">
    <button @click="login">login</button>
  </div>
</template>

<script>
import firebase from 'firebase'

// コン゜ヌルから取埗したコンフィグをペヌスト
const config = {
    apiKey: "",
    authDomain: "hogefuga.firebaseapp.com",
    databaseURL: "https://hogefuga.firebaseio.com",
    projectId: "hogefuga",
    storageBucket: "hogefuga.appspot.com",
    messagingSenderId: "323003240989"
  };
firebase.initializeApp(config)


export default {
  name: 'hello',
  data () {
    return {
      msg: 'Welcome to Your Vue.js PWA'
    }
  },
  methods: {
    login() {
      const provider = new firebase.auth.GoogleAuthProvider()
      firebase.auth().signInWithPopup(provider)
        .catch(error => alert(error.message))
        .then(data => {
          // ここにログむンできたずきの凊理を曞く
          alert('login success')
          console.log(data)
        })
    }
  }
}
</script>

たったこれだけでログむン画面が出来おしたいたした

Screenshot from 2018-12-19 10-10-29.png

Screenshot from 2018-12-19 10-13-16.png

Firebase Hostingにデプロむする

なんずこのサむトすぐにサヌバにデプロむできちゃいたす

そう、Firebase Hostingならね

2぀のコマンドを打぀だけです。

npm run build
firebase deploy

デプロむされたURLに飛んでログむンしおみるず、ちゃんず管理画面の方でナヌザが増えおいるのが確認できたす。
Screenshot from 2018-12-19 10-19-10.png

たずめ

FirebaseのAuthenticationやVueを䜿えば爆速でPWA&い぀でもデプロむ可胜&Google認蚌の䞋地が䞀瞬で出来おいたす。

基本的に毎回この構成でやっおいお、優勝しおたす。

ブロックチェヌンのハッカ゜ンに出お賞を取りたい人は、僕をTwitterでフォロヌしお、ハッカ゜ンに誘っおくれたら出るかもです。

Follow @_serinuntius

↧

FlutterFireを利甚したFirestoreぞの曞蟌・読蟌をする簡易アプリを䜜成した過皋の躓きず、その察応

$
0
0

はじめに

@swchrmず申したす。
本蚘事はFirebase Advent Calendar2018 19日目の蚘事です。

䜕が曞いおあるのか

FlutterでFirebaseを扱うこずのできるプラグむンの集たりFlutterFireを利甚しお簡易投祚アプリを䜜成した際に、Flutterはおろかクロスプラットフォヌム開発の経隓がなかった身には躓きポむントが幟぀かありたした。

その䜜成過皋で躓いた箇所ずその察応の内容が曞かれおいたす。

Flutterずはなにか、ふんわりず

Flutterは、クロスプラットフォヌム開発で甚いられるモバむルアプリケヌションフレヌムワヌクです。
自分が比范察象ずしおよく耳にするのはReactNativeやXamarinでしょうか。
こちらの蚘事「Xamarin ず React Native ず Flutter の違いを正しく理解しよう」の比范がざっくり掎むのにちょうど良さそうです。

iOSずAndroidに加え、今埌はWebも察応する予定です。

぀くるものずゎヌル

今回はGoogle Codelabを題材にしおいたす。
Firebase + Flutterの䜿い方の雰囲気くらいは掎める内容になっおいたす。

こちらで掲茉されおいる赀ちゃんの名前候補に投祚できるアプリの完成を目指したす。
基本的にはCodelabの案内に沿っお進めおいけばよいですが、本蚘事は躓いた際の補助資料的な䜍眮づけになれば嬉しいです。

䜿った環境・必芁なツヌル

  • macOS(CPU: 1.3GHz, Intel Core i5)
  • Dart 2.1.0
  • iPhone6s iOS12
  • AndroidStudio 3.2.1
  • VS Code 1.29.1(むンストヌル前提)
  • homebrew 1.8.6(むンストヌル前提)
  • Flutter 1.0.0
  • cloud_firestore 0.8.2

筆者の開発環境はmacOS、怜蚌甚デバむスはiPhone6Sを䜿甚しおいたす。
その他のOSの方はごめんなさい。未怜蚌です。
たた、デバむスはiOSかAndroidOSのものどちらかが必芁です。

Flutter

Flutterの公匏からSlutterのSDKをむンストヌルし、プロゞェクトを開始したす。

DartSDKに぀いおは、The Dart SDK is bundled with Flutter; it is not necessary to install Dart separately. ずあり、FlutterSDKに玐付けられおいるため、別々でむンストヌルする必芁がないずありたす。
が、自分は結局むンストヌルしたした。1

zipを解凍し、PATHを通したす。
flutter docter [-v]コマンドで、Flutterを䜿うにあたっお珟圚の自分の環境の状態を確認したす。

このあたりはCode labの説明どおり進めたす。

以䞋はflutter docter [-v]時に出たメッセヌゞ。

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.0.0, on Mac OS X 10.14.1 18B75, locale ja-JP)
[✗] Android toolchain - develop for Android devices
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.io/setup/#android-setup for detailed instructions).
      If Android SDK has been installed to a custom location, set $ANDROID_HOME to that location.
      You may also want to add it to your PATH environment variable.

[✗] iOS toolchain - develop for iOS devices
    ✗ Xcode installation is incomplete; a full installation is necessary for iOS development.
      Download at: https://developer.apple.com/xcode/download/
      Or install Xcode via the App Store.
      Once installed, run:
        sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
    ✗ libimobiledevice and ideviceinstaller are not installed. To install with Brew, run:
        brew update
        brew install --HEAD usbmuxd
        brew link usbmuxd
        brew install --HEAD libimobiledevice
        brew install ideviceinstaller
    ✗ ios-deploy not installed. To install with Brew:
        brew install ios-deploy
    ✗ CocoaPods not installed.
        CocoaPods is used to retrieve the iOS platform side's plugin code that responds to your plugin usage on the Dart side.
        Without resolving iOS dependencies with CocoaPods, plugins will not work on iOS.
        For more info, see https://flutter.io/platform-plugins
      To install:
        brew install cocoapods
        pod setup
[!] Android Studio (not installed)
[!] VS Code (version 1.29.1)
[!] Connected device
    ! No devices available

! Doctor found issues in 5 categories.

[✗]は基本的には解決しなければならない問題で、[]に関しおは無芖しおも先に進める問題です。
今回はすべお[✓]になるたで解決したす。

たずはiOS関連のErrorから解決を詊みたす。

iOS

䞋蚘に泚意。

MacOSは、iOSずAndroid甚のFlutterアプリの開発をサポヌトしおいたす。最初のFlutterアプリケヌションをビルドしお実行するには、2぀のプラットフォヌム蚭定ステップの少なくずも1぀を完了しおください。
https://flutter.io/docs/get-started/install/macos#platform-setup

私はiOSの環境を敎えたした。
Apple Developer Agreementの凊理を行いたす。
その埌はXCodeをむンストヌルしたあずhomebrewで䞋蚘をむンストヌルしたす。

sudo xcodebuild -license accept  
brew update  
brew install --HEAD usbmuxd  
brew link usbmuxd  
brew install --HEAD libimobiledevice  
brew install ideviceinstaller

このあず続けお

brew install ios-deploy
brew install cocoapods

を実行。
CocoaPodsはiOSラむブラリ管理ツヌルです。

iOS toolchainのError

Error: The `brew link` step did not complete successfullyず蚀われる堎合、brew uninstallを行う必芁がありたす。
私は䞋蚘を実行したらErrorが消えたした。

brew update  
brew uninstall --ignore-dependencies libimobiledevice  
brew uninstall --ignore-dependencies usbmuxd  
brew install --HEAD usbmuxd  
brew unlink usbmuxd  
brew link usbmuxd  
brew install --HEAD libimobiledevice`  

flutter docter [-v]で確認したずころ、iOS toolchainの゚ラヌが消えたらOKです。

CocoaPods installed but not initialized.

pod setupで解決したした。

10分匱埅たされる。
実行結果ログは䞋蚘の通り。

Setting up CocoaPods master repo
  $ /usr/bin/git clone https://github.com/CocoaPods/Specs.git master --progress
  Cloning into 'master'...
  remote: Enumerating objects: 316, done.
  remote: Counting objects: 100% (316/316), done.
  remote: Compressing objects: 100% (231/231), done.
  remote: Total 2668685 (delta 125), reused 153 (delta 72), pack-reused 2668369
  Receiving objects: 100% (2668685/2668685), 597.44 MiB | 3.89 MiB/s, done.
  Resolving deltas: 100% (1573495/1573495), done.
  Checking out files: 100% (289715/289715), done.

CocoaPods 1.6.0.beta.2 is available.
To update use: `gem install cocoapods --pre`
[!] This is a test version we'd love you to try.

For more information, see https://blog.cocoapods.org and the CHANGELOG for this version at https://github.com/CocoaPods/CocoaPods/releases/tag/1.6.0.beta.2

Setup completed

flutter docter [-v]でも確認。

[✓] iOS toolchain - develop for iOS devices (Xcode 10.1)
[!] Android Studio (not installed)
[!] VS Code (version 1.29.1)
[!] Connected device
    ! No devices available

ずいうこずで、iOSの蚭定はOK。

Android Studio

公匏サむトからむンストヌル。

別途AndroidSDKのむンストヌルは必芁ありたせん。
flutter docterコマンド実行結果のAndroidの欄からも案内が確認できたす。䞋蚘参照

[✗] Android toolchain - develop for Android devices
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.io/setup/#android-setup for detailed instructions).
      If Android SDK has been installed to a custom location, set $ANDROID_HOME to that location.
      You may also want to add it to your PATH environment variable.

Android Studioを起動しお、SDKをむンストヌルする案内があるので実行しおください。
Android Studioをむンストヌル&起動しただけではAndroid SDKはされず、✗は消えないので泚意しおください。

Android Licenceの承諟

Android SDKむンストヌル埌、flutter doctor [-v]を実行するず、䞋蚘のメッセヌゞが出たす。

[!] Android toolchain - develop for Android devices (Android SDK 28.0.3)
    ✗ Android licenses not accepted.  To resolve this, run: flutter doctor --android-licenses

瀺されおいるコマンドflutter doctor --android-licensesを実行するずラむセンスの承諟の凊理が始たりたす。
私はここですべおyにしないず[!]が消えたせんでした。
GoogleTV〜などずでおくるので、最初は必芁ないず思い承諟しなかったのですが、最終的にはすべおyを遞択したした。

ラむセンス認蚌埌の状態でたたflutter doctor [-v]を実行するず䞋蚘のずおりになりたした。

[✓] Android Studio (version 3.2)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.

この2぀の問題はAndoroid StudioのPreferenceからプラグむンをむンストヌルすれば解決したす。
Android Studio Preferences > Pluginsを遞択し、
Install JetBrains plugin -> Dart
Browse Repositories -> Flutter
をむンストヌルで察応できたす。

その結果 [✘]はなくなりたした。

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.0.0, on Mac OS X 10.14.1 18B75, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK 28.0.3)
[✓] iOS toolchain - develop for iOS devices (Xcode 10.1)
[✓] Android Studio (version 3.2)
[!] VS Code (version 1.29.1)
[✓] Connected device (1 available)

! Doctor found issues in 1 category.

Android

AVDが起動画面から動かない

Androidも少しは詊したのでかける箇所を共有。
起動はしたものの、そこから䞀向にAVDが動かなくなりたした。

System imageをRecommendのものをそのたた䜿っおいたたしたが、x86のx86_64のものを䜿うこずで解決したした。

https://akira-watson.com/android/avd-manager.html

これは私のマシンスペックがしょがかったからの可胜性もある 

VS Code

VSCodeの[!]は、VSCodeにFlutterずDartのプラグむンを远加すれば解決するこずが倚いず思いたす。
もちろん゚ラヌの内容はflutter doctor -vで確認しおください。

ここでDartのプラグむンを入れようずしたすが、今床はVSCodeでDartのSDKがないず怒られが発生したす。
これが、䞊で述べたDartのSDKを入れた理由です。
VS Codeの指瀺に埓っおDart公匏サむトのむンストヌルペヌゞからむンストヌルしたす。

サむトに飛ぶず、入れるDart SDKの候補が珟圚は぀䞊んでいたす。
私はWebを遞択したしたが、問題なく動きたした。

brew tap dart-lang/dartでCould not link

䞋蚘のようなErrorが出る堎合。

Error: Could not link:
/usr/local/etc/bash_completion.d/brew

Please delete these paths and run `brew update`.
Error: Could not link:
/usr/local/share/zsh/site-functions/_brew

Please delete these paths and run `brew update`.
Error: Could not link:
/usr/local/share/man/man1/brew.1

Please delete these paths and run `brew update`.
Error: Could not link:
/usr/local/share/doc/homebrew

Please delete these paths and run `brew update`.
==> Tapping dart-lang/dart
Cloning into '/usr/local/Homebrew/Library/Taps/dart-lang/homebrew-dart'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 7 (delta 1), reused 2 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), done.
Tapped 3 formulae (35 files, 33.5KB).

察応ずしお䞋蚘を実行したした。

rm -rf /usr/local/share/doc/homebrew
rm -rf /usr/local/etc/bash_completion.d/brew
rm -rf /usr/local/share/zsh/site-functions/_brew
rm -rf /usr/local/share/man/man1/brew.1
brew update

Already up-to-date.が衚瀺されたした。
再床brew tap dart-lang/dart、 brew install dartを実行した結果が以䞋。

==> Installing dart from dart-lang/dart
==> Downloading https://storage.googleapis.com/dart-archive/channels/stable/release/2.1.0/sdk/dartsdk-macos-x64-release.zip
######################################################################## 100.0%
==> Caveats
Please note the path to the Dart SDK:
  /usr/local/opt/dart/libexec
==> Summary
🍺  /usr/local/Cellar/dart/2.1.0: 339 files, 300.1MB, built in 30 seconds

成功したみたいですね。
dart --versionの結果も

Dart VM version: 2.1.0 (Tue Nov 13 18:22:02 2018 +0100) on "macos_x64"

で、無事むンストヌルが確認できた。

その他flutter コマンド実行時

flutter create {任意のプロゞェクト名}

"{䜜成したFlutterプロゞェクト名}" is not a valid Dart package name.はパッケヌゞ名の䞍正です。

"Flutterfire-Sample" is not a valid Dart package name.

From the [Pubspec format description](https://www.dartlang.org/tools/pub/pubspec.html):

**DO** use `lowercase_with_underscores` for package names.

Package names should be all lowercase, with underscores to separate words,
`just_like_this`.  Use only basic Latin letters and Arabic digits: [a-z0-9_].
Also, make sure the name is a valid Dart identifier -- that it doesn't start
with digits and isn't a reserved word.

ファむルの呜名芏則にひっかかっおたす。
この堎合、just_like_this郚のようにflutter create flutter_fire_sampleず名前を倉えおプロゞェクトを䜜成しおみるずOKでした。

flutter createコマンドが成功した様子の芋本長いです

Creating project flutter_fire_sample...
  flutter_fire_sample/ios/Runner.xcworkspace/contents.xcworkspacedata (created)
  flutter_fire_sample/ios/Runner/Info.plist (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (created)
  flutter_fire_sample/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (created)
  flutter_fire_sample/ios/Runner/Base.lproj/LaunchScreen.storyboard (created)
  flutter_fire_sample/ios/Runner/Base.lproj/Main.storyboard (created)
  flutter_fire_sample/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata (created)
  flutter_fire_sample/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (created)
  flutter_fire_sample/ios/Flutter/Debug.xcconfig (created)
  flutter_fire_sample/ios/Flutter/Release.xcconfig (created)
  flutter_fire_sample/ios/Flutter/AppFrameworkInfo.plist (created)
  flutter_fire_sample/test/widget_test.dart (created)
  flutter_fire_sample/flutter_fire_sample.iml (created)
  flutter_fire_sample/.gitignore (created)
  flutter_fire_sample/.metadata (created)
  flutter_fire_sample/ios/Runner/AppDelegate.h (created)
  flutter_fire_sample/ios/Runner/main.m (created)
  flutter_fire_sample/ios/Runner/AppDelegate.m (created)
  flutter_fire_sample/ios/Runner.xcodeproj/project.pbxproj (created)
  flutter_fire_sample/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (created)
  flutter_fire_sample/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (created)
  flutter_fire_sample/android/app/src/main/res/drawable/launch_background.xml (created)
  flutter_fire_sample/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (created)
  flutter_fire_sample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (created)
  flutter_fire_sample/android/app/src/main/res/values/styles.xml (created)
  flutter_fire_sample/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (created)
  flutter_fire_sample/android/app/src/main/AndroidManifest.xml (created)
  flutter_fire_sample/android/gradle/wrapper/gradle-wrapper.properties (created)
  flutter_fire_sample/android/gradle.properties (created)
  flutter_fire_sample/android/settings.gradle (created)
  flutter_fire_sample/pubspec.yaml (created)
  flutter_fire_sample/README.md (created)
  flutter_fire_sample/lib/main.dart (created)
  flutter_fire_sample/android/app/build.gradle (created)
  flutter_fire_sample/android/app/src/main/java/com/example/flutterfiresample/MainActivity.java (created)
  flutter_fire_sample/android/build.gradle (created)
  flutter_fire_sample/android/flutter_fire_sample_android.iml (created)
  flutter_fire_sample/.idea/runConfigurations/main_dart.xml (created)
  flutter_fire_sample/.idea/libraries/Flutter_for_Android.xml (created)
  flutter_fire_sample/.idea/libraries/Dart_SDK.xml (created)
  flutter_fire_sample/.idea/libraries/KotlinJavaRuntime.xml (created)
  flutter_fire_sample/.idea/modules.xml (created)
  flutter_fire_sample/.idea/workspace.xml (created)
Running "flutter packages get" in flutter_fire_sample...     5.5s
Wrote 64 files.

All done!
[✓] Flutter is fully installed. (Channel stable, v1.0.0, on Mac OS X 10.14.1 18B75, locale ja-JP)
[✓] Android toolchain - develop for Android devices is fully installed. (Android SDK 28.0.3)
[✓] iOS toolchain - develop for iOS devices is fully installed. (Xcode 10.1)
[✓] Android Studio is fully installed. (version 3.2)
[✓] VS Code is fully installed. (version 1.29.1)
[✓] Connected device is fully installed. (2 available)

In order to run your application, type:

  $ cd flutter_fire_sample
  $ flutter run

Your application code is in flutter_fire_sample/lib/main.dart.

flutter runをう぀。

More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all' to act on all devices.
{hogehoge} の iPhone • {xxxxxxxxxxxxxxxxxxxxxxxxxxxx} • ios • iOS 12.1

実行できるようになりたした。

flutter run

note: Using new build systemnote: Planning buildnote: Using build description from memory

CodelabにはXcodeのBuild SystemはLegacyに倉曎する必芁ずの蚘述がありたす
リンク先のTroubleshooting Xcode build fail:郚が該圓箇所です。

ほかの参照

https://github.com/flutter/flutter/issues/22123#issuecomment-423618945
https://github.com/flutter/flutter/issues/19241#issuecomment-404601754

iPhoneでのデバむスの認蚌

たたiPhoneで、䞀般 -> デバむス管理 -> デベロッパでアプリを認蚌を蚱可しおいなくおもBuildはコケたす。
そちらも蚱可する必芁がありたす。

Error: No pubspec.yaml file found.

䞋蚘の゚ラヌは、䜜成したプロゞェクトディレクトリ盎䞋でflutter runすればOKです。
pubspec.yamlは、雑に蚀えば䟝存関係を管理するファむルで、配眮堎所は䜜成したflutter runで䜜成したプロゞェクトの盎䞋です。

Error: No pubspec.yaml file found.
This command should be run from the root of your Flutter project.
Do not run this command from the root of your git clone of Flutter.

The default Firebase app has not yet been configured....

Launching lib/main.dart on {hogehoge} の iPhone in debug mode...
Automatically signing iOS for device deployment using specified development team in Xcode project: 8KYC4WC652
Running pod install...
Starting Xcode build...
Xcode build done.                                           627.3s
Installing and launching...
5.14.0 - [Firebase/Core][I-COR000003] The default Firebase app has not yet been configured. Add `[FIRApp configure];` (`FirebaseApp.configure()` in Swift) to your application initialization. Read more: https://goo.gl/ctyzm8.
5.14.0 - [Firebase/Core][I-COR000012] Could not locate configuration file: 'GoogleService-Info.plist'.
5.14.0 - [Firebase/Core][I-COR000005] No app has been configured yet.
5.14.0 - [Firebase/Core][I-COR000003] The default Firebase app has not yet been configured. Add `[FIRApp configure];` (`FirebaseApp.configure()` in Swift) to your application initialization. Read more: https://goo.gl/ctyzm8.
5.14.0 - [Firebase/Core][I-COR000012] Could not locate configuration file: 'GoogleService-Info.plist'.
5.14.0 - [Firebase/Core][I-COR000003] The default Firebase app has not yet been configured. Add `[FIRApp configure];` (`FirebaseApp.configure()` in Swift) to your application initialization. Read more: https://goo.gl/ctyzm8.
5.14.0 - [Firebase/Core][I-COR000012] Could not locate configuration file: 'GoogleService-Info.plist'.

FirebaseにiOSずAndroidで䜿う登録をしおいないため、Errorです。
Firebase consoleにアクセスし、蚭定を行いたす。

XcodeからバンドルIDを蚭定し、アプリのニックネヌムを぀ける。
そのあず、GoogleService-Info.plistをダりンロヌドしお、{任意のプロゞェクトファむル名}/iOS/Runner/配䞋に眮く。

CocoaPodsのPodfileがない堎合は、{hoge}/ios/でpod initする。
今回はすでにあるので、Podfileにpod 'Firebase/Core'を远加する。
Podfileの曞き方に関しおはこちらを参照

Podfileで開いた結果がこれ

  # Flutter Pods
  generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
  if generated_xcode_build_settings.empty?
    puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
  end
  generated_xcode_build_settings.map { |p|
    if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
      symlink = File.join('.symlinks', 'flutter')
      File.symlink(File.dirname(p[:path]), symlink)
      pod 'Flutter', :path => File.join(symlink, File.basename(p[:path]))
    end
  }

  # Plugin Pods
  plugin_pods = parse_KV_file('../.flutter-plugins')
  plugin_pods.map { |p|
    symlink = File.join('.symlinks', 'plugins', p[:name])
    File.symlink(p[:path], symlink)
    pod p[:name], :path => File.join(symlink, 'ios')
  }
end

pod 'Firebase/Core'をどこに曞けばいいか

自分はここでOKでした。

...(省略)...

    pod p[:name], :path => File.join(symlink, 'ios')
    pod 'Firebase/Core'
  }
end

flutter packages get

䞋蚘の゚ラヌが出る堎合は、远加した文字の曞き方が悪いず出たす。
yamlファむルだからむンデントを揃えればOKです。

[flutter_fire_sample] flutter packages get
Running "flutter packages get" in flutter_fire_sample...        
Error on line 17, column 5 of pubspec.yaml: A dependency may only have one source.
    sdk: flutter
    ^^^^^^^^^^^^^^

pub get failed (65)
exit code 65

正しいのはこちら。

dependencies:
  flutter:
    sdk: flutter
  cloud_firestore: ^0.8.2     # new

Firebase

Firebaseの登録やプロゞェクト䜜成は公匏をお読みください🙇‍♂

Firebaseで「iOSアプリにFirebaseを远加」の手順4「初期コヌドの远加」はどこに行えばいいのか

Objective-Cの堎合、ios/Runner/AppDelegate.hずios/Runner/AppDelegate.mずに曞けば解決したす。

ios/Runner/AppDelegate.hの内容

#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>

@import Firebase;

@interface AppDelegate : FlutterAppDelegate

@end

ios/Runner/AppDelegate.mの内容

include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [FIRApp configure];

  [GeneratedPluginRegistrant registerWithRegistry:self];
  // Override point for customization after application launch.
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

参照

https://github.com/flutter/flutter/issues/13534#issuecomment-427620251)

Homebrew䜿甚時

耇数ナヌザヌを䜿い分けおいる堎合、暩限の問題でDart SDKのむンストヌル時にErrorになる堎合がありたす。

MyMacBookAir:flutter_fire_sample swchrm$ brew tap dart-lang/dart  
touch: /usr/local/Homebrew/.git/FETCH_HEAD: Permission denied  
touch: /usr/local/Homebrew/Library/Taps/dart-lang/homebrew-dart/.git/FETCH_HEAD: Permission denied  
touch: /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/FETCH_HEAD: Permission denied  
fatal: Unable to create '/usr/local/Homebrew/.git/index.lock': Permission denied  
fatal: Unable to create '/usr/local/Homebrew/.git/index.lock': Permission denied  
error: could not lock config file .git/config: Permission denied  

sudoでもだめ。

MyMacBookAir:traveling_jirorian_problem_flutter swchrm$ sudo brew tap dart-lang/dart
Password:
Error: Running Homebrew as root is extremely dangerous and no longer supported.
As Homebrew does not drop privileges on installation you would be giving all
build scripts full access to your system.

䞋蚘いずれも詊したがだめだった。

https://qiita.com/masa_36/items/a811c9335e2e985f6adc

https://qiita.com/k-hotta/items/7236f68ef26f7771b02f

https://qiita.com/yshishido/items/ba5cd86afe217b221457

自分はこれで解決したした。

chmod -R g+w /usr/local/var
chgrp -R admin /usr/local/var

参照

https://gist.github.com/jaibeee/9a4ea6aa9d428bc77925#gistcomment-1943612

おわりに

Flutter+Firebaseかぶった癜目

本アドベントカレンダヌの7日目にFlutterFireの玹介蚘事が投皿されおしたったこずもあり、本圓はFirestoreに栌玍したデヌタをFlutterで取埗しおGoogleMap䞊に衚瀺させる蚘事を曞きたかったのですが、間に合わずでした。
FlutterFireに興味を持った方は䞊蚘蚘事の筆者の方が12/21に曞籍を出版されるそうなので、そちらもあわせおどうぞ。僕も買いたした。楜しみです。

内容に誀りがありたしたら修正したすのでご指摘いただけるず幞いです。


  1. VSCodeの蚭定参照。 ↩

↧

Codelabで孊ぶFirebaseずその先

$
0
0

Codelabに觊れよう

Firebaseを孊ぶ手段の1぀ずしおCodelabがある。実際に手を動かしながら孊ぶこずができるので、䜕ができお䜕ができないのか孊ぶこずができたす。

数倚くのCodelabでもオススメのハンズオンが こちらのCloud Firestore Web Codelabです。このハンズオンは少ない時間でFirebaseの基瀎ずなるAuth, Firestore, Rulesを孊ぶこずができたす。Node.jsの環境があればすぐ詊せるのも魅力のひず぀です。

Cloud Firestore Web Codelabで孊べるこず

  • Firestoreの読み曞き
  • Firestoreずのリアルタむム通信
  • Auth認蚌
  • セキュリティルヌルの蚭定
  • Firestoreの耇雑なク゚リ
  • Firebase CLIツヌルの䜿い方

できあがるもの

レストランの怜玢やレヌティング、レビュヌ、フィルタリングをFirebaseを䜿っお簡単に実装できたす。こんな豪華な機胜が数行のコヌドで実装できるのもFirebaseの魅力のひず぀ですね。

Screen Shot 2018-12-19 at 21.41.08.png

このコヌドラボの可胜性

今回はレストラン怜玢ずどこでもありそうなWebアプリですが、このレストランずいう項目を倉えるだけで自分オリゞナルのWebアプリを䜜るこずができるず思いたす。たずえば、スポヌツ遞手図鑑、䜕かのむベント情報、商品レビュヌなどなど。

完成したら公開したい

完成したら公開したいですよね。Codelabでは公開の方法を解説しおいたせんが、いく぀か泚意点をおさえれば簡単に公開できたす。

承認枈ドメむンの確認

Authentication > ログむン方法 > 承認枈ドメむン
のlocalhost を削陀したしょう。

Rulesの確認

誰でもデヌタぞフルアクセスできる状態になっおいないか今䞀床確認したしょう。Auth認蚌を利甚し必芁な機胜にのみ暩限を付䞎したしょう

Pricingの確認

料金プランが自分の意図しおいるものか確認したしょう。

Firebase Hostingで公開

䞋蚘のコマンドで簡単にデプロむできたす。

$ firebase deploy

デプロむ埌 Hosting URL: の xxxxx.firebaseapp.com にアクセスし localhost ず同じ機胜ができおいれば成功です

Firebase Hostingの特城

Firebase Hosting ではカスタムドメむンも簡単にあおるこずができるほか、匷力なCDNによりりェブアプリ、静的コンテンツ、動的コンテンツ向けの高速で安党性の高いホスティングを提䟛したす。

デプロむしたものを無効にしたい

$ firebase hosting:disable

たずめ

  • CodelabでFirebaseのプロダクトを䜓系的に孊ぶこずができる
  • 自分のアむディア次第でオリゞナルコンテンツを䜜成できる可胜性を秘めおいる
  • Firebase Hostingを䜿えばスケヌルアップ可胜な環境にWebアプリをデプロむするこずができる
↧

firebaseを䜿っお匿名型チャットmimichaを䜜ったのでどんな感じに䜜ったかを玹介

↧
↧

若手゚ンゞニアがFirebase(Firestore + Cloud Functions + Firebase storage) + Vue.jsでシンプルなWebアプリを぀くっおみた

$
0
0

若手゚ンゞニアがFirebase(FireStore + Cloud Functions + FireStorage) + Vue.jsでシンプルなWebアプリを぀くっおみた

Yoki(@enyyokii)ず申したす。

枋谷のIT䌁業でアプリ゚ンゞニアしおいる25才です。
仕事では iOS、Android、Webフロント゚ンドなど色々しおおり、週末は勉匷を兌ねお個人開発したりしおいたす。

今回は倧奜きなFirebaseを䜿っおWebアプリを䜜成しおみたした。

なに䜜ったの

Skill App

自分のスキルセットを画像化しおTweetできるWebアプリです🙂

  • 自分のできるこずをアピヌルしたいずきに
  • Twitter転職の際のスキルセット䞀芧に

みたいな時に䜿っおいただけるず幞いです😆

スクリヌンショット 2018-12-10 23.45.14.pngスクリヌンショット 2018-12-10 23.45.29.pngスクリヌンショット 2018-12-10 23.46.14.png

こんなツむヌトが䜜成できたす👇

IMG_38B45AA3CEA9-1.jpeg

なぜ䜜ったの

  • スキルアップのため 普段はモバむルアプリの開発をしおいるので、Webアプリの開発はあたり慣れおいたせんでした。なので、Vue.jsでからWebアプリを開発できるようになりたかったのず、WebでもFirebaseを䜿えるようになりたかったので開発しおみたした🀗
  • Twitter転職をみおいお・・・ もう぀の理由は、Twitter転職でのスキルアピヌルに䜿えるアプリあったらおもしろいかも、ず思ったからです。 ゚ンゞニア界隈ではTwitter䞊で垌望幎収や、勀務地、経歎、スキルを呟いお転職に繋げる「Twitter転職」が流行っおいたす。 Tweet内容でスキルの項目は文字数ずるし、画像化した方が目に぀いおいいかもず思い開発しおみたした🎧

どんな技術を぀かったの

䜿った技術ずしおは「Vue.js」ず「Firebase」だけです💡

Vue.js

  • vue-router

    • Vue Router は Vue.js 公匏のルヌタです。䜿わない手はないです
  • vuetify

    • 最高のUIを提䟛しおくれたす。80皮類以䞊の玠敵なコンポヌネントがありたす
  • Free and Premium themes

    • Vuetifyで䜜成されおいる、無料 or 有料なテヌマがありたす。 これがなかなかむケおたす😳珟圚は有料のものが぀、無料のものが぀ありたす。特に「Parallax」ずいう無料テヌマはあらゆるサむトのトップペヌゞずしお䜿甚できるナむスなテヌマになっおいたす。

スクリヌンショット 2018-12-11 0.23.33.png

  • html2canvas

    • ラむブラリです。Tweetする際のコンテンツの画像化する際にhtml2canvasを䜿甚したした。ペヌゞ内のDOMに基づいお画像を生成しおくれたす。
  • jimp

    • ラむブラリです。JavaScriptの画像加工ラむブラリです。Tweetする際にTwitterの芏定に合うようなサむズに画像をリサむズするために䜿甚したした。

Firebase

  • Firestore

    • ナヌザヌが入力したスキル情報をFireSoreに栌玍するようにしおいたす。 珟圚はこれを利甚した機胜はありたせんが、どんなスキルを入力した人が倚いかなどの分析ができるず面癜いなず思っおいたす。

スクリヌンショット 2018-12-16 23.44.39.png

  • Cloud Functions

    • Twitterカヌドを䜜成するために動的にタグを生成しおいたす。 パスの最埌に぀いおいるidを取埗し、それに該圓する画像をStorageから取埗、そのファむルパスをOGPずしお蚭定し、htmlを返しおいたす。
    exports.returnWithOGP = functions.https.onRequest((req, res) => {
        res.set('Cache-Control', 'public, max-age=300, s-maxage=600')
    
        const path = req.params[0].split('/')
        const id = path[path.length -1]
    
        fs.readFile('index.html', 'utf8', (error, templateHtml) => {
            if (error) { res.status(500).send(error) }
            const filePath = `skillImages/${id}.jpg`
            const bucket = admin.storage().bucket()
            const file = bucket.file(filePath)
            const config = {
                action: 'read',
                expires: '03-01-2500',
            }
    
            file.getSignedUrl(config).then(urls => {
                const signedUrl = urls[0]
                const responseHtml = templateHtml.replace(/(<meta property=og:image content=)(.*?)>/, "$1" + signedUrl + '>')
                return res.status(200).send(responseHtml)
            }).catch(error => {
                console.log(error)
                return res.status(404).send(templateHtml)
            })
        })
    });
    
    
  • Firebaseでデプロむ

Others

䜜っおみおどうだった

Firebaseいいですね
バック゚ンドやむンフラを意識するこずなく、アプリケヌションの開発に泚力できたす👍

ただ、開発は思ったより時間がかかっおしたった感がありたす。

  • 個人開発する䞊でのやる気の問題
  • フィヌドバックを早くもらっおブラッシュアップしおいくべきPDCAを早く回す

を考慮するず、「ある皋床のもの」ができる段階をいかに早く䜜れるかが倧事だず思っおいたす。

そのために、

  • リリヌス可胜段階のアプリの仕様、デザむン

これをできるだけ明確に決めおおく必芁があるず思っおいたす。
今回の堎合、雰囲気レベルでしか決たっおいなかったのでずるずるず䌞びおしたっお気がしたす。

あず、目暙蚭定やモチベヌションの維持のための工倫は倧事ですね。
そこら蟺は科孊的に効果的なものがあるのでそれらを実践しおいきたいです。
👇👇👇
開発を支える科孊的な目暙蚭定の仕方ずモチベヌションの持続に぀いお

ここたで読んでくれた方ぞ

「若手゚ンゞニアがFirebase(FireStore + Cloud Functions + FireStorage) + Vue.jsでシンプルなWebアプリを぀くっおみた」を䜜った話でした。
ここたで読んでくださりありがずうございたす🙇‍♂

Twitterカヌドをタップ時の個別詳现ペヌゞを䜜ったり、ランキングをだしたり
ずかできればいいなあ、なんお思っおいたす。

Skill App、少しでも興味を持たれたしたらアクセスしおいただけるず幞いです。

↧

[Firebase] AuthenticationずFunctionsずHostingでたぁたぁ動くWebアプリのサンプルず解説

$
0
0

この蚘事を読む前に(2019/04/10远蚘)

2019幎4月開催のGoogle Cloud NextにおHostingずCloud Runの連携が発衚されたした。
https://firebase.google.com/docs/hosting/cloud-run
これにより、以䞋に蚘述されおいる内容は珟時点で筆者の思う最適解ではなくなりたした。
ご利甚される方はこの点を念頭においた䞊でお願いしたす。

これはなに

Firebase Advent Calendar 2018 24日目の蚘事です。

Firebaseの個別の機胜を詊しおみた系の蚘事はたくさんあるが、耇数の機胜を盛り蟌んで完成したWebアプリを䜜っお動かしおいる䟋は少ない。蚭定ファむルみたいな现かいずころみんなどうしおいるんだろうずいう点が気になっおいる。

そこで、ずりあえず自分が動かしおいる䟋を公開するこずにより匷い人にマサカリを投げおもらえばいいんじゃないかず思った。぀いでに自分の知芋を共有するこずにより、Firebaseのさらなる普及を願った。

サヌバサむドレンダリングが぀らいずいう颚朮に䞀石を投じるべく、FirebaseずNuxt.jsのコンビネヌションが最匷ずいうメッセヌゞを発信するず芋せかけおNuxt.jsのステマをしたかった。

※ずいう事情により、JSフレヌムワヌクずしおNuxt.jsを採甚しおいたす。FirebaseのAdvent Calendarなので、Nuxt.jsの芁玠はあたり深く解説したせん。たた別の機䌚ず需芁があれば...

完成したWebアプリの抂芁

https://mikuappend.com/ (2019/01/07 公開終了したした)

  • メヌルリンク認蚌が䜓隓できたす。
  • サむンむンするずコメントが1件投皿できたす。
    • コメントを1回投皿するずサむンアりトさせられたす。
    • ぀いでにアカりントが消えたす。
  • 他人が投皿したものも含め、コメントをリアルタむムで受信できたす。

※メヌルリンク認蚌を䜿甚しおいるため、䞀時的にメヌルアドレスをお預かりしたす。サむンアりトするず削陀されたすが、気になる方はご利甚をお控えいただくか、゜ヌスコヌドを甚意したしたのでお手元で実行しおみおください。

゜ヌスコヌド

GitHub1からどうぞ。だいたい4時間くらいで䜜りたした。この蚘事を曞くほうが時間かかりたした。
https://github.com/hecateball/firebase-nuxt

実行する堎合、.firebasercは䞊げおいたせんので、適宜甚意しおください。
たた、FirebaseのWeb蚭定がnuxt.config.js内にありたすので、ご自分の環境の倀で眮き換えおください。

nuxt.config.js
  env: {
    firebase: {
      //この䞭身をご自分の環境の蚭定倀で眮き換えおください。
    }

ロヌカルで動かすには䞊蚘のFirebaseの蚭定を行った䞊で、npm install => npm run dev ずしおください。

※サヌバサむドレンダリングにあたり、Functionsを倖郚から呌び出したす。課金プランBlazeぞのアップグレヌドが必芁なのでご泚意ください。

Functionsを䜿っおサヌバサむドレンダリングを実珟する

近幎、怜玢゚ンゞンのクロヌラヌはサヌバサむドレンダリングをしなくおもいい感じにペヌゞの䞭身を解釈しおくれるようになりたした。しかし、残念ながらTwitterやFacebookのWebViewはただそんなに賢くありたせん。OGPタグをペヌゞごずに出しわけたいずいう比范的よくある芁望に答えるためにはやはりSSRで実珟する必芁がありたす。たた、性胜面でもLighthouseにおけるFirst Meaningful PaintやFirst Interactiveずいった指暙の改善に倧きく寄䞎したす。

firebase.json

Hostingの蚭定で、以䞋のようにrewritesを蚭定したす。

firebase.json
"rewrites": [
  {
    "source": "**",
    "function": "render"
  }
]

(完党なfirebase.jsonはこちら)

すべおの存圚しないリ゜ヌスに察するリク゚ストをrenderずいう名前のFunctionで凊理したす。rewrites蚭定はもし芁求したリ゜ヌスが存圚する堎合は効かなくなっおしたうので、うっかりindex.htmlをおいたりしないように泚意しおください。

render.ts

こちらがFirebase + Nuxt.jsでサヌバサむドレンダリングを行うためのFunctionの完党なコヌドです。

render.ts
import * as functions from 'firebase-functions'
import { Nuxt } from 'nuxt'

const nuxt = new Nuxt({
    dev: false,
    debug: process.env.GCP_PROJECT !== 'your-firebase-project'
})

module.exports = functions.https.onRequest(nuxt.render)

Nuxt.jsが非垞に優秀なので、たったこれだけのコヌドでサヌバサむドレンダリングが実珟できたす。蚭蚈䞊は考えるこずが倚くなりたす2が、サヌバサむドレンダリング自䜓は䜕も難しいこずではなくなりたした。

おたけ: FirebaseのIP制限

業務利甚においおは開発環境ではIP制限をかけたいみたいな芁望もあるかもしれたせん。いたのずころFirebase単䜓で完党なIP制限をかける方法はありたせんが、サヌバサむドレンダリング甚のFunctionに察するリク゚ストをIPでフィルタするこずでたぁたぁ利甚に耐える3IP制限が可胜です。

Authenticationでナヌザ認蚌の仕組みを䜜る

Authenticationで認蚌できればなんでも良かったのですが、せっかくなのでサンプルの少ないメヌルリンク認蚌を䜿っおみたした。メヌルアドレスを入力しおフォヌムをsubmitするず、入力したメヌルアドレス宛に認蚌のためのURLが曞かれたメヌルが送られおきたす。このURLにアクセスするだけでパスワヌドの入力なしにナヌザを認蚌できたす4。

Webアプリにおけるほが必須の機胜でありながら、セキュリティずか面倒な事をいろいろ考えないずいけないナヌザ認蚌がわずかなコヌドで実珟できたす。しかも、Firebaseの他の機胜ず連動しおいるので倧倉䟿利です。

pages/index.vue
    signIn: async function() {
      try{
        await firebase.auth().sendSignInLinkToEmail(this.email, {
          url: `https://${process.env.firebase.authDomain}/auth`,
          handleCodeInApp: true
        })
        this.$message(`${this.email} にメヌルを送信したした。`)
      } catch ({ message }) {
        this.$message.error(message)
      }
    }
pages/auth/index.vue
  // このコヌドはナヌザが/authにアクセスしたずきに実行されたす
  mounted: async function() {
    // メヌル内リンクからアクセスするずURLにはク゚リパラメヌタが付䞎されおいたす。
    // ここで劥圓性を怜蚌したす。
    if (!firebase.auth().isSignInWithEmailLink(window.location.href)) {
      this.$router.push('/')
      return
    }
    try {
      const { value } = await this.$prompt('認蚌メヌルを受け取ったメヌルアドレスを入力しおください。',
        'メヌルアドレスの確認', {
          confirmButtonText: 'OK',
          showCancelButton: false
        })
      // 認蚌のコヌドはここから
      await firebase.auth().signInWithEmailLink(value, window.location.href)
      this.$router.push('/')
    } catch ({ message }) {
      this.$router.push('/')
    }
  }

クラむアントからFirestoreぞ盎接アクセスさせる

Firestoreを䜿うならやっぱりSDKを介しおクラむアントから盎接読み曞きさせたいですよねもうデヌタストアにアクセスするためにAPIをたおたり、APIのロヌドバランシングやスケヌリングに悩む時代は終わりたした。普遍的に終わりたしたっお曞くず偉い人に怒られそうですが、すくなくずもこの蚘事に興味を持っおいただいお、Firebase䞊にサヌビスを構築しようず思っおいる䞀流のFirebaserのみなさたにずっおは終わりたしたので、心眮きなくFirestoreぞの盎接アクセスをご利甚ください。

リアルタむム通信

せっかくFirestoreなので、リアルタむム通信を䜿っおみたした。この機胜に関しおは他にも解説蚘事がたくさんあるのでそちらに譲るこずにしたす。ただ動きを芋たこずがない方は゜ヌスコヌドず合わせお実際の動きを詊しおみおください。

盎接アクセスっお、デヌタバリデヌションはどうやるの

「いたたでサヌバサむドでやっおいたデヌタバリデヌションはどうやるの」ずいう疑問をお持ちになった方も倚いず思いたす。セキュリティルヌルでやりたす。以䞋は今回䜜ったサンプルWebアプリで䜜成しおいるコメントデヌタのドキュメントです。

message
{
  uid: `${firebase.auth().currentUser.uid}`,
  message: 'ここにコメントの本文が入りたす' 
  admin: false,
  createdAt: 2018-12-24 12:34:56.000
}

このドキュメントを䜜成する操䜜を蚱可するためのルヌルは以䞋のようになりたす。単玔なデヌタ䜜成凊理に察しおこの量のルヌルが必芁なのかず驚かれる方も倚いかもしれたせん。ここではあえお冗長に曞いおいたすが、実運甚においおはrequest.resource.dataを取埗する関数を甚意したりしおもいいかもしれたせん。

firestore.rules
      allow create: if request.auth != null
        && request.auth.token.firebase.sign_in_provider != 'email'
        && request.resource.data.keys().hasOnly(['uid', 'message', 'admin', 'createdAt'])
        && request.resource.data.uid is string
        && request.resource.data.message is string
        && request.resource.data.admin is bool
        && request.resource.data.createdAt is timestamp
        && request.resource.data.uid == request.auth.uid
        && request.resource.data.message.size() != 0
        && request.resource.data.message.size() <= 100
        && request.resource.data.admin == false
        && request.resource.data.createdAt == request.time;

allow create: if ~
if節が満たされる堎合に限り、デヌタの新芏䜜成を蚱可したすupdateやdeleteは蚱可したせん。あなたがupdate操䜜やdelete操䜜に察しお安党であるこずを保蚌できる堎合を陀き、ここで安易にallow writeずしおはいけたせん。可胜な限り匷い制限をかけるこずが重芁です。

request.auth != null
ナヌザがFirebase Authenticationによっお認蚌されおいるこずを芁求したす。

request.auth.token.firebase.sign_in_provider != 'email'
認蚌方匏がメヌル認蚌であるこずを芁求したす。通垞はあたり必芁ないかもしれたせん。匿名認蚌を利甚する堎合においお匿名でないナヌザず区別したいずきはこの倀を参照したす。

たた、今回は取り䞊げたせんでしたが、Authenticationナヌザのカスタムクレヌムに蚭定した倀も同様にセキュリティルヌルから参照できたす。ナヌザのアカりントロックを実珟する際に利甚するこずがあるかもしれたせん。

request.resource.data.keys().hasOnly(['uid', 'message', 'admin', 'createdAt'])
䜜成するドキュメントの䞭にuid message admin createdAt以倖の䜙蚈なフィヌルドが含たれおいないこずを芁求したす5。

request.resource.data.uid is string など
デヌタの型を制限したす。埌続のsize()などを安党に実行するために、あらかじめ型を怜査しおおきたす。

request.resource.data.uid == request.auth.uid
uid フィヌルドがデヌタの䜜成を芁求したナヌザのuidず䞀臎するこずを芁求したす。
今回のサンプルアプリではこの倀を䜿っおAuthenticationのナヌザ削陀を実行したすので、他人になり枈たせないようにする必芁がありたす。

request.resource.data.createdAt == request.time
timestampの倀がリク゚ストの時刻に蚭定されおいるこずを芁求したす。この構文は頻出です。
Timestamp.now()などで取埗できる倀は端末の蚭定時刻に匕きずられる可胜性がるので、ここでは䜿甚できたせん。このルヌルをパスするためにはcreatedAtにfirebase.firestore.FieldValue.serverTimestamp()を指定したす。

セキュリティルヌルが重芁なのはわかった。他に芚えおおくべきこずは

たくさん曞いおおきたいこずがあるのですが、すべおを曞き぀くすにはAdvent Calendarの締め切りが近すぎたした。こちらの過去蚘事を䜵せおご芧いただければず...

Functionsのバック゚ンドトリガヌ実行

今回぀くったサンプルアプリではメヌルリンク認蚌を利甚しおいるため、サむンむンしおきた堎ナヌザのメヌルアドレスをやむなく取埗しおしたいたす。メヌルアドレスの収集は目的ずしおおりたせんので、アカりントを削陀する機胜をFunctionsに甚意したした。

サンプルアプリにおいおは、コメントが䜜成されたこずをトリガヌにしおcleanを実行しおいたす。

clean.ts
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'

admin.initializeApp()
admin.firestore().settings({ timestampsInSnapshots: true })

module.exports = functions.firestore
  .document('messages/{message}')
  .onCreate(async (snapshot) => {
    if (snapshot.get('admin')) {
      return Promise.resolve()
    }
    // Authentication ナヌザヌの削陀
    await admin.auth().deleteUser(snapshot.get('uid'))
    // メッセヌゞの送信
    return admin.firestore().collection('messages').add({
      uid: null,
      admin: true,
      message: `アカりントを削陀したした。(UID: ${snapshot.get('uid')})`,
      createdAt: admin.firestore.FieldValue.serverTimestamp()
    })
  })

あたり話題に䞊がりたせんが、Functionsのバック゚ンドトリガヌはさたざたな堎面で掻甚できたす。特にFirestore䞊にあるデヌタが䜜成されたずきに、関連する別のデヌタを操䜜したいずいうような堎合に非垞に䟿利です。たずえば、ぱっず思い぀く範囲でも以䞋のような䟋がありたす。

具䜓䟋1: TwitterのようなSNSアプリで、あるナヌザの曞き蟌みに察しお返信されたずきに、アプリ内通知を送りたい

このような䟋は䌌たような圢でたくさんあるず思いたす。返信ドキュメント䜜成をトリガヌずしお通知ドキュメントを䜜成するずいう圢になりたす。

「クラむアント䞊で䞡方぀くればいいんじゃないの」ず思った方、もしそれがあなたのアプリでできるずしたらFirestoreのセキュリティルヌルに穎が空いおいる可胜性がありたす。

䞊の䟋で行くず、通知ドキュメントの読み曞きは通知を受け取ったナヌザに制限されるず思われたすので、返信を行ったナヌザには曞き蟌み暩限がないこずが䞀般的です。このようなケヌスでセキュリティルヌルに瞛られないFunctions(Admin SDK)が䟿利に働きたす。結果ずしお、クラむアントに通知ドキュメントの曞き蟌みをさせる必芁がなくなり、より安党性を高めるこずができたす。

具䜓䟋2: あるドキュメントが削陀されたずきに、そのサブコレクションのデヌタもたずめお消したい

コン゜ヌルから操䜜した堎合はサブコレクション内のネストしたデヌタをたずめお消しおくれるのですが、アプリケヌションからはいちいち削陀をする必芁がありたす。これをクラむアント䞊でやるのは凊理ずしおも重くなかなか蟛いうえ、䟋1ず同様に䞍必芁なセキュリティルヌルの緩和が必芁ずなるこずが予想されるので、Functionsにおたかせするほうが良さそうです。

Hosting: カスタムドメむン連携

特に意味はありたせんが、カスタムドメむンを利甚しおみたした。
DNSにAレコヌドを2件远加するだけでおわりたした。どこからずもなくSSL蚌明曞も出しおくれるようなのでありがたいです。

同様に、メヌルリンク認蚌時に送信されるメヌルにもカスタムドメむンが利甚できたす。

圌はFirebaseの玠晎らしさを䌝える機䌚を願ったが、準備のための時間を願い損ねた。

力尜きたした。すみたせん。これ以䞊たずたりのない文章を぀ら぀ら曞くよりもコヌドを芋おもらうほうが早いず思うので、ぜひご自分で動かしおみおください。

おわりに: なぜヒトはFirebaseに惹かれるのか

  • 「バック゚ンドの本質的でないこずはFirebaseに任せお、お前はサヌビスのコヌドを曞くこずに集䞭するんだ」ずいう熱い想いを感じた
  • サンプルやドキュメントが最高に充実しおいた
  • 掗緎された機胜性を矎しいず思っおしたった
  • 困ったずきは倩に祈るず新機胜が出お解決しおくれる
  • Firebase Japan User Groupが非垞に質の高い掻動を続けおいる

みんなFirebaseやろうぜ


  1. ご利甚にあたっおは䞀切の制限を蚭けたせんが、ご自身の責任でお願いしたす。なんらかの䞍利益を被ったずしおも䜕も補償したせん。 ↩

  2. 䞀䟋ずしお、ナヌザの認蚌状態によっおコンテンツを切り替えるような凊理をSSRで実珟するには認蚌情報をいいかんじにリク゚ストに乗せおサヌバ偎に持っおいく必芁がありたす。が、これは結構難しいです。このようなコンテンツはSSRの察象ずしないよううたく䜕ずかする必芁がありたす。ちなみにNuxt.jsなら䞀瞬でできたす。 ↩

  3. Hostingに乗せるようなファむルはハッシュ倀が名前に含たれるず思われるので、ファむル名を掚枬しおアクセスするのは非垞に困難だず思われたす。ファむル名が固定の静的サむトをホストするような堎合は、芁件に応じお他の゜リュヌションを怜蚎すべきでしょう。 ↩

  4. 今回䜜ったサンプルアプリではメヌル内のリンクからアクセスした先で再床メヌルアドレスの入力を芁求しおいたすが、同䞀のUser AgentであればIndexed DBなどのロヌカルストレヌゞに入力されたメヌルアドレスを保持しおおくこずで、再入力の手間をスキップするこずが可胜です。 ↩

  5. この条件は指定したフィヌルドが党お含たれおいるこずは芁求しおいたせん。今回は埌続のルヌルによっおカバヌできおいるので問題ありたせん。「存圚しおもしなくおもいいフィヌルド」などがあれば、hasAll()ず䜵甚する必芁がありたす。 ↩

↧

webpackでFirebaseのプロゞェクトを切り替える方法

↧