Open9

Firebase

さしもんさしもん

FirebaseはAppExtensionを公式にはサポートしてないみたいだからAppExtensionで使うときは気をつけたほうがいい

さしもんさしもん

AppExtensionじゃないけどEmbeddedFrameworkとして切り出されているTargetとMainTargetの両方でFirebaseを利用する場合
Main targetは切り出したTargetのサブターゲットとして、inherit! :search_pathsでMainTargetは切り出したTargetを継承するようにする(まあ普通に考えたらそうだし、これはcocoapods使う場合に共通して言えることなんだけど

https://qiita.com/takasek/items/39c28042c4ecef4685ee

さしもんさしもん

FirestoreをつかったCRUD

さしもんさしもん

データ追加

ドキュメントを追加・作成するには

  • setData
  • addDocumen
    の2種類がある
    自分はaddDocumentを使っていたけど2つに明確に使い方の違いがある

set() を使用してドキュメントを作成する場合、作成するドキュメントの ID を指定する必要があります
ドキュメントに有効な ID がなく、Cloud Firestore が ID を自動的に生成するように設定したほうが都合のよい場合もあります。この設定を行うには、add() を呼び出します。

setData

  • 指定したIDのドキュメントが存在しない場合は、ドキュメントが新規に作成されます
  • 指定したIDのドキュメントがすでに存在する場合は、その既存のドキュメントにデータを統合することもできるし、データを統合しないようにすることで、新しく提供されたデータでコンテンツを上書くこともできる

merge:trueとすることでsetDataに渡したDictionary型の値がドキュメントに追加される。すでに該当するkey名がある場合はそのvalueを更新する
merge:falseとすることで指定ドキュメントのフィールドがすべて削除され、setDataに渡したDictionary型の値
だけになる

// Update one field, creating the document if it does not exist.
db.collection("cities").document("BJ").setData([ "capital": true ], merge: true)
ViewController.swift

なので基本的に作成する場合にはmerge:true をつけたほうが予想外のことが起きなくていいかも

Dictionaryではなくてカスタムオブジェクトを使う

Firestoreはいちいちdictionaryで追加したいデータを定義してsetData()メソッドで実際にFirestoreへ書き込むのだと思ってたけど、setData(from: )を使ってカスタムオブジェクトをそのまま追加することもできるみたい

公式ドキュメントをきちんと読み込むまで知らなかった...

addDocument

ドキュメントに有効な ID がなく、Cloud Firestore が ID を自動的に生成するように設定したほうが都合がよい場合に使う
*setData(from:)の方ではEncodableのカスタムオブジェクトを渡せば自動でエンコードしてくれたけど、こちらはDictionary型で直接データを表す他ない

データ追加まとめ

setDataとaddDocumentの違い

  • データを追加するという点では2つとも同等
  • addDocumentは追加したいデータはDictionary型やmapで表す他ないが、setDataはDictionary型やマップ型だけでなくカスタムオブジェクトを直接渡すこともできるので楽
  • 一方でドキュメントに使いたいようなIDがない場合、setDataはデータを追加する時点でドキュメントIDを指定することが必須である一方で、addDocumentは必要なくfirestoreが自動で設定してくれる

なのでドキュメントに使いたいIDがない場合は、addDocumentとsetDataを組み合わせて let newCityRef = db.collection("cities").document()でDocumentRefを作りそれに対して newCityRef.setData でデータを追加するやり方が良さそう

さしもんさしもん

データ更新

ドキュメント全体を上書きせずにドキュメントの一部のフィールドを更新する場合には、update()メソッドを使用するらしい

ネストされてるオブジェクトのフィールドを更新する場合

またネストされてるオブジェクトのフィールドを更新する場合、つまりネストされたオブジェクトがドキュメントに含まれている場合は、update()を呼び出すときにドッド表記を使用するとドキュメント内のネストされたフィールドを参照できるらしい
以下で言えばこれまでのやり方だと "favorites": ["color": "Red"] としていたところが "favorites.color":"Red"で住むようになっている
これまでの方法はどうやらFirestoreの文脈でいうマップ(配列ではないけどネストされてるやつ)だけを対象にしているみたい。配列を要素に持つフィールドを更新する場合はこの後に書く

配列内の要素更新する場合

上記の方法はどうやらFirestoreの文脈でいうマップ(配列ではないけどネストされてるやつ)だけを対象にしている
ではドキュメントに配列フィールドがある場合はどうやって更新するのか
arrayUnion()とarrayRemove()を使用して要素を追加および削除を行うらしい
arrayUnion(は要素を配列に追加するけど、追加されるのはまだ存在しない要素のみ。
arrayRemove()は指定した各要素のすべてのインスタンスを削除

setData(from:mergeFields:)を使っても更新できる?

APIリファレンスを見てみたところsetDdata(from:mergeFields)というメソッドがあった
これの説明を読むと

Encodes an instance of Encodable and writes the encoded data to the document referred by this DocumentReference by only replacing the fields specified under mergeFields.
Any field that is not specified in mergeFields is ignored and remains untouched. If the document doesn’t yet exist, this method creates it and then sets the data.
と書かれていた
注目するのは only replacing the fields specified under mergeFields. Any field that is not specified in mergeFields is ignored and remains untouchedの箇所
どうやらmergeFields以下のものだけが置き換えられ、それ以外のものに変更は加えられないらしい

実際に試してみたところ、mergeFieldsで指定したフィールドだけが更新された

さしもんさしもん

個人的には更新はsetData(from:mergeFields:)がやりやすかったな
arrayUnionは自分のコードが間違っていなければ
["member_list": FieldValue.arrayUnion([data])]みたいな感じでdictionaryにしないといけなくてカスタムオブジェクトで使えずに、いちいちエンコード処理を書くのがだるい

さしもんさしもん

Firestoreはオフラインで読み込み書き込みできるのか
DBしか頭になかったけどこれができるならFirestoreもいいな