我有我的数据结构:My Firestore Database
您将看到我有一个“ Michael 201A”文档和一个“ Michael 201B”文档,其想法是从这些文档中检索字段并将它们显示在tableView中。另外,我希望tableView根据添加到“请求”集合的任何新文档自动更新,以便始终将tableView数据与最新添加到firestore数据库中一起填充。
从FireStore检索数据的功能
@IBOutlet weak var tableView: UITableView!
var db: Firestore!
var requestArray = [Request]()
override func viewDidLoad() {
super.viewDidLoad()
db = Firestore.firestore()
tableView.delegate = self
tableView.dataSource = self
loadData()
}
func loadData() {
db.collection("Requests").whereField("Status", isEqualTo: true).getDocuments() {(querySnapshot, err) in
if let err = err {
print("An error occurred\(err)")
} else{
self.requestArray = querySnapshot!.documents.compactMap({Request(dictionary: $0.data())})
print(self.requestArray)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
我添加了一条print语句以读取该值,但它返回空值。
我的tableView功能
extension ResidentAdvisorViewController: UITableViewDelegate {
func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped me")
}
}
extension ResidentAdvisorViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return requestArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let request = requestArray[indexPath.row]
cell.textLabel?.text = "\(request.Name)"
return cell
}
}
我的请求结构
protocol DocumentSerializable {
init?(dictionary:[String:Any])
}
struct Request {
var Name: String
var Dorm: String
var Room: Int
var Status: Bool
var UID: String
var TimeStamp: Date
var dictionary:[String:Any] {
return [
"Name":Name,
"Dorm":Dorm,
"Room":Room,
"Status":Status,
"UID": UID,
"TimeStamp": TimeStamp
]
}
}
extension Request : DocumentSerializable {
init?(dictionary: [String : Any]) {
guard let name = dictionary["Name"] as? String,
let dorm = dictionary["Dorm"] as? String,
let room = dictionary["Room"] as? Int,
let status = dictionary["Status"] as? Bool,
let uid = dictionary["UID"] as? String,
let timestamp = dictionary["Timestamp"] as? Date
else { return nil}
self.init(Name: name, Dorm: dorm, Room: room, Status: status, UID: uid, TimeStamp: timestamp)
} }
作为旁注,我已经检查以确保我的单元格标识符与“ cell”匹配。另外,当我将单元格文本更改为“ Hello World”时,我可以将其显示在tableView中。非常感谢您的任何帮助。
答案 0 :(得分:1)
代码没有很多错误,但是问题中有两个问题。
1)为什么该值为空
2)如何初始填充数据源,然后在添加新文档时对其进行更新。
让我先说2)。
要首先加载数据然后观察将来的变化,我们可以使用.addSnapshotListener函数,然后在firebase闭包内处理特定的变化类型。
func observeAllRequests() {
let requestsCollection = self.db.collection("Requests")
let query = requestsCollection.whereField("Status", isEqualTo: true)
query.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 name = diff.document.get("Name") as? String ?? "No Name"
print("added: \(name)") //add to your dataSource
}
if (diff.type == .modified) {
let name = diff.document.get("Name") as? String ?? "No Name"
print("modified: \(name)") //update the request in the dataSource
}
if (diff.type == .removed) {
let name = diff.document.get("Name") as? String ?? "No Name"
print("removed: \(name)") //remove the request from the dataSource
}
}
//tableView.reloadData()
}
}
上面的代码将返回所有与查询匹配的文档。遍历快照中的项目,每个项目都可以添加,修改或删除。第一次调用该函数时,所有差异都将是.childAdded,它使您可以初始填充数据源。
此后任何文档更改都将只是更改后的文档,不同之处在于添加,修改和删除。
编辑:
解决问题1)
数组为空的原因是由于扩展的结构方式-几乎全部或全部。这就是现在的样子
extension Request : DocumentSerializable {
init?(dictionary: [String : Any]) {
guard let name = dictionary["name"] as? String
let dorm = dictionary["Dorm"] as? String,
let room = dictionary["Room"] as? Int,
let status = dictionary["Status"] as? Bool,
let uid = dictionary["UID"] as? String,
let timestamp = dictionary["Timestamp"] as? String
else { return nil}
self.init(Name: name)
} }
如果未找到字段,则整个操作将失败并返回nil,而compactMap则返回nil,因此您最终将得到一个空数组。您的结构不包含时间戳,因此会失败。
我建议采取一些措施来保护您的代码,但允许缺少字段。 nil-coalescing运算符在这里可以很好地工作
extension Request : DocumentSerializable {
init?(dictionary: [String : Any]) {
let name = dictionary["name"] as? String ?? "No Name"
let room = dictionary["room") as? String ?? "No Room"
etc