ios - 如何检索 Cloud Firestore 文档并在 TableView 中显示数据?
问题描述
我有我的数据结构:我的 Firestore 数据库
正如您将看到的,我有一个“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()
}
}
}
}
我添加了一个打印语句来读取值,但它返回空。
我的 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)
} }
作为旁注,我已检查以确保我的单元格标识符与“单元格”匹配。此外,当我将单元格文本更改为“Hello World”时,我可以让它显示在我的 tableView 中。非常感谢您的任何帮助,谢谢。
解决方案
代码没有太多问题,但问题中有两个问题。
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()
}
}
上面的代码将返回与查询匹配的所有文档。迭代快照中的项目,每个项目都是 .add、.modified 或 .removed。第一次调用该函数时,所有差异都将是 .childAdded,它允许您最初填充数据源。
此后的任何文档更改都将只是更改的文档,不同之处在于 .add、.modified 和 .removed。
编辑:
解决问题 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
推荐阅读
- c++ - 如何将 C++ 类型包装到可变参数列表中
- file-upload - 使用 apollo-upload-client 有效负载格式上传文件
- flutter - 如何在变量中使用类?
- html - rvest:如何在 R 中只提取一个文本?
- spring - 从登录页面重定向的 Spring Security 中排除特定页面
- database - 如何使用 laravel 从 sql 数据库中删除 html 标签?我用过 {!! 描述 !!}
- php - SQLSTATE[HY000]:一般错误:1005 无法创建表 Laravel 8
- json - 错误:无效的 JSON RPC 响应:未定义
- python - 以指定间隔计算曲线下的部分面积
- c# - C# Windows 窗体 Web Api 身份验证