はじめに
Firestoreから取得したドキュメントをカスタムクラスに入れていくときに紹介したflatMap
でスマートに進める話の続きです。Google本家のサンプルコードでは1つのドキュメントを処理する例だけしか見つけられませんでした。
複数のドキュメントを取ってきたときはどうするのか?試行錯誤をしてみました。
元のコード
今回いじるコードはこれです。
docRef.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error: \(err)")
} else {
for document in querySnapshot!.documents {
var customClass = CustomClass()
customClass.name = document.data()["name"] as! String
customClass.address = document.data()["address"] as! Int
self.data.append(customClass)//カスタムクラスを配列に追加していっています
}
}
}
このコード、for
ループが頂けないです。
何が頂けないって、文字列(“name”や”address”)が居る点と、as! Stringのようなキャスト(的なこと)を行っている点です。逐一カスタムクラスの型やプロパティを確認する必要がありますよね…… 下手をすればセツケイシヨという名の古文書を紐解くことになります。
スッキリさせたいのと、できればカスタムクラス(CustomClass
)に取得したデータをそのまま渡して初期化させたいです。
カスタムクラスにデータの塊を渡して初期化させる
カスタムクラスのプロパティに1つずつ値を割り当てていく作業、これは面倒。
この方法だと、DBに保存する項目が変わる度に割り当てる作業を追加したり削除したりしないといけません。これはミスの元。
Firestore側から渡されたデータの処理は極力1か所にまとめておきましょう。
というわけで、カスタムクラス内に収めてしまいます。
こうすることで、Firestore側から渡されたデータの中身がどうであれ、それをどう処理するかはカスタムクラスの中だけで完結します。
struct CustomClass: {
var name: String = ""
var address: String = ""
init(_ dictionary: [String: Any]) {
self.name = dictionary["name"] as! String
self.address = dictionary["address"] as! String
}
func getArray() -> [String: Any] {
return [
"name": self.name,
"address": self.address
]
}
}
ついでと言ってはなんですが、Firestoreへ保存するときのドキュメントを作るメソッドも追加しておきました。名称getArray
は皆様のセンスで何とかしてください。
mapを使ってスッキリさせる
ごめんなさい。flatmap
は使いこなしきれませんでした。といいますか、今回はmap
で良いような気がしました。
querySnapshot!.documents
でドキュメントを1つずつ取り出して、カスタムクラスに納めていくという処理なので、結局こうなります。先ほどのカスタムクラスの初期化も早速使いました。
self.data = querySnapshot!.documents.map { CustomClass($0.data()) }
ねっ?簡単でしょ?というノリですがこれで終了です。5〜6行がスッキリ1行になりました。また、プロパティ名やそれぞれの型が何かを意識しなくても良くなりました。
ちなみに、map
の代わりにcompactMap
も使えます。ただし、querySnapshot!.documents
の中にnil
が混じっていたり、違う型が入り混じっているとは考えにくいので、map
で十分かと思います。
また、!
でアンラップしているのが気持ち悪いという方はif let
やguard
でほどいてあげると幸せになれると思います。