我是Google Firestore的新手,并且通常都很敏捷,并且想知道如何正确设置onSnapshot()方法,以便我的视图控制器可以自动接收更新并适合所有情况。
我有一个非常简单的结构(Llama),可以用来对数据进行建模。通过遵循以下出色的教程,我可以构建一个基本的Firestore设置:(https://youtu.be/XwXEsKRYUXU)
但是,我发现在实现自己的代码版本时,我只知道如何在服务器上创建文档。如果服务器上发生更改,我希望能够更新我的应用程序中的对象,并且我也想删除一个对象。目前,我对如何完成此任务一无所知。
此外,在更新我的应用程序中的对象时,还有一种方法可以只更新已更改的字段,而不是覆盖整个对象。
我想完成的是一个数据库模型,该模型在后台在托管服务器和用户设备之间无缝同步,如果有冲突,则将任何人的更改合并到一起(由上次修改文档的人解决)。 )
我已经详尽阅读了有关该主题的Google文档,即使我确定可以找到答案,但我个人还没有很快地了解到它们的全部知识:(https://cloud.google.com/firestore/docs/how-to )
数据模型:
import Foundation
import Firebase
protocol DocumentSerializable {
init?(dictionary:[String:Any])
}
struct Llama {
var name: String
var color: String
var gender: String
init(name: String, color: String, gender: String) {
self.name = name
self.color = color
self.gender = gender
}
var dictionary:[String:Any] {
return [
"name":name,
"color":color,
"gender":gender
]
}
}
extension Llama:DocumentSerializable {
init? (dictionary: [String:Any]) {
guard
let name = dictionary["name"] as? String,
let color = dictionary["color"] as? String,
let gender = dictionary["gender"] as? String
else {return nil}
self.init(name: name, color: color, gender: gender)
}
}
查看控制器代码:
var llamas = [Llama]()
override func viewDidLoad() {
super.viewDidLoad()
checkForUpdates()
}
func checkForUpdates() {
let firestore = Firestore.firestore()
firestore.collection("Llama").addSnapshotListener{
QuerySnapshot, error in
guard let snapshot = QuerySnapshot else {return}
snapshot.documentChanges.forEach {
update in
if update.type == .added {
self.llamas.append(Llama(dictionary: update.document.data())!) // Works great!
}
if update.type == .modified {
// How can I update the correct llama object, hopefully just the field(s) that changed?
}
if update.type == .removed {
// How can I remove the correct llama object?
}
}
}
}
如View Controller代码中所示,我有一个函数,该函数在View Controller打开时被调用,该函数初始化addSnapShotListener()方法。原样的代码不会运行任何错误或警告。我该如何前进?
答案 0 :(得分:1)
这个问题有几个部分,要解决所有这些问题将是一个很长的答案。这是一个解决方案,将首先加载您的dataSource数组,然后监视和处理谨慎的``对象''的添加,修改和删除事件,例如文档。
在此示例中,我们将使用一个可跨设备跟踪喜爱的葡萄酒的应用。我们会跟踪葡萄酒的名称,产地和等级。
结构是这个
wines
doc_0
name: "Insignia"
rating: "98"
state: "CA"
doc_1
name: "Quilceda Creek"
rating: "99"
state: "WA"
doc_0,doc_1等是添加文档时自动创建的文档ID。
然后我们需要一个班级来举办每种葡萄酒
class WineClass {
var wine_id = ""
var name = ""
var rating = ""
var state = ""
init(withDoc: QueryDocumentSnapshot) {
self.wine_id = withDoc.documentID
self.name = withDoc.get("name") as? String ?? "no name"
self.rating = withDoc.get("rating") as? String ?? "no rating"
self.state = withDoc.get("state") as? String ?? "no state"
}
func updateProperties(withDoc: QueryDocumentSnapshot) {
self.name = withDoc.get("name") as? String ?? "no name"
self.rating = withDoc.get("rating") as? String ?? "no rating"
self.state = withDoc.get("state") as? String ?? "no state"
}
}
最后是一个类var Array,它将用作tableView或collectionView的数据源。
var wineArray = [WineClass]()
然后是代码-我们仅对CA的葡萄酒感兴趣,因此查询将结果限制为仅此示例中的结果。
func handleSpecificChanges() {
let collectionRef = self.db.collection("wines")
collectionRef.whereField("state", isEqualTo: "CA").addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
let wineToAdd = WineClass(withDoc: diff.document)
self.wineArray.append(wineToAdd)
print("added: \(wineToAdd.wine_id) \(wineToAdd.name) \(wineToAdd.rating) \(wineToAdd.state)")
}
if (diff.type == .modified) {
let docId = diff.document.documentID
if let indexOfWineToModify = self.wineArray.firstIndex(where: { $0.wine_id == docId} ) {
let wineToModify = self.wineArray[indexOfWineToModify]
wineToModify.updateProperties(withDoc: diff.document)
print("modified: \(wineToModify.wine_id) \(wineToModify.name) \(wineToModify.rating) \(wineToModify.state)")
}
}
if (diff.type == .removed) {
let docId = diff.document.documentID
if let indexOfWineToRemove = self.wineArray.firstIndex(where: { $0.wine_id == docId} ) {
self.wineArray.remove(at: indexOfWineToRemove)
print("removed: \(docId)")
}
}
}
}
}
一些笔记
docId(doc_0,doc_1)等是将其全部结合在一起的密钥。保证它是唯一的,这是您知道哪种酒已被修改或删除的方式,以及在Firestore中创建该酒时对其进行分配的方式。
此代码最初运行时,将为每个与查询匹配的葡萄酒调用.add。这样一来,您就可以在初始时使用匹配项填充dataSource数组,然后再监视添加的事件。
您的问题之一是在不完全覆盖对象的情况下更新对象的字段。这可能并不重要-从修改后的事件中可以看出,我们只是将当前字段传递给对象以更新所有字段。如果您确实不希望“覆盖”字段,则可以添加if检查以查看传入的字段数据是否与实际不同。
if self.rating != newRatnig {
self.rating == newRating
}
但同样,这可能不是必需的。
我认为这解决了所有问题。