如何检索 firestore 数据并从字典中填充 Tableview Rows/Sections?
How to retrieve firestore data and populate Tableview Rows/Sections from a dictionary?
我正在尝试使用我已经解析并存储在字典中的 Firestore 数据填充我的表视图的部分和行,看起来像这样...
dataDict = ["Monday": ["Chest", "Arms"], "Wednsday": ["Legs", "Arms"], "Tuesday": ["Back"]]
坦率地说,我什至不确定是否应该像我那样将数据存储在字典中。这样做是错误的吗?此外,由于数据是异步提取的,我如何才能仅在 字典完全加载我的网络数据后填充我的部分和行?我正在使用完成处理程序,但是当我尝试打印 dataDict
的结果时,它会连续打印出三个数组,就像这样...
["Monday": ["Chest", "Arms"]]
["Tuesday": ["Back"], "Monday": ["Chest", "Arms"]]
["Tuesday": ["Back"], "Monday": ["Chest", "Arms"], "Wednsday": ["Legs", "Arms"]]
而我希望它在完成后 return 阵列的单个打印。我做错了什么?
var dataDict : [String:[String]] = [:]
//MARK: - viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
vcBackgroundImg()
navConAcc()
picker.delegate = self
picker.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
tableView.tableFooterView = UIView()
Auth.auth().addStateDidChangeListener { (auth, user) in
self.userIdRef = user!.uid
self.colRef = Firestore.firestore().collection("/users/\(self.userIdRef)/Days")
self.loadData { (done) in
if done {
print(self.dataDict)
} else {
print("Error retrieving data")
}
}
}
}
//MARK: - Load Data
func loadData(completion: @escaping (Bool) -> ()){
self.colRef.getDocuments { (snapshot, err) in
if let err = err
{
print("Error getting documents: \(err)");
completion(false)
}
else {
//Appending all Days collection documents with a field of "dow" to daysarray...
for dayDocument in snapshot!.documents {
self.daysArray.append(dayDocument.data()["dow"] as? String ?? "")
self.dayIdArray.append(dayDocument.documentID)
Firestore.firestore().collection("/users/\(self.userIdRef)/Days/\(dayDocument.documentID)/Workouts/").getDocuments { (snapshot, err) in
if let err = err
{
print("Error getting documents: \(err)");
completion(false)
}
else {
//Assigning all Workouts collection documents belonging to selected \(dayDocument.documentID) to dictionary dataDict...
for document in snapshot!.documents {
if self.dataDict[dayDocument.data()["dow"] as? String ?? ""] == nil {
self.dataDict[dayDocument.data()["dow"] as? String ?? ""] = [document.data()["workout"] as? String ?? ""]
} else {
self.dataDict[dayDocument.data()["dow"] as? String ?? ""]?.append(document.data()["workout"] as? String ?? "")
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
// print(self.dataDict)
}
completion(true)
}
}
}
self.dayCount = snapshot?.count ?? 0
}
}
}
我认为这只是你程序的流程。每次循环遍历集合时,都会将它得到的内容添加到字典中。所以在第一次通过时,它会打印字典有 1 个项目。在第二遍中,它向字典中添加了另一个项目,然后打印了现在有 2 个项目的字典,因此打印了 2 个项目。我认为您没有看到意外的行为,这只是您如何根据循环方式对日志语句进行排序。
换句话说,这样打印是有道理的。
我同意@ewizard 的回答。问题出在您的程序流程中。您遍历文档并为每次迭代获取集合中的文档。您还多次重新加载 tableView 和调用完成闭包,这是您不想做的。
为了改善您的程序流程,请尝试使用 DispatchGroup 来获取您的数据,然后在获取所有数据后将它们放在一起一次。请参阅下面的示例以了解基本概念。我的示例是您代码的一个非常简化的版本,我想向您展示您应该执行的重要更改。
func loadData(completion: @escaping (Bool) -> ()) {
self.colRef.getDocuments { (snapshot, err) in
// Handle error
let group = DispatchGroup()
var fetchedData = [Any]()
// Iterate through the documents
for dayDocument in snapshot!.documents {
// Enter group
group.enter()
// Fetch data
Firestore.firestore().collection("/users/\(self.userIdRef)/Days/\(dayDocument.documentID)/Workouts/").getDocuments { (snapshot, err) in
// Add your data to fetched data here
fetchedData.append(snapshot)
// Leave group
group.leave()
}
}
// Waits for until all data fetches are finished
group.notify(queue: .main) {
// Here you can manipulate fetched data and prepare the data source for your table view
print(fetchedData)
// Reload table view and call completion only once
self.tableView.reloadData()
completion(true)
}
}
}
我也同意其他评论,您应该重新考虑 tableView 的数据模型。更合适的结构是二维数组(数组的数组 - 第一个数组转换为 table 视图部分,内部数组对象转换为部分项目)。
这是一个例子:
// Table view data source
[
// Section 0
[day0, day1],
// Section 1
[day2, day3],
// Section 2
[day4, day5],
]
用法示例:
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].count
}
}
我正在尝试使用我已经解析并存储在字典中的 Firestore 数据填充我的表视图的部分和行,看起来像这样...
dataDict = ["Monday": ["Chest", "Arms"], "Wednsday": ["Legs", "Arms"], "Tuesday": ["Back"]]
坦率地说,我什至不确定是否应该像我那样将数据存储在字典中。这样做是错误的吗?此外,由于数据是异步提取的,我如何才能仅在 字典完全加载我的网络数据后填充我的部分和行?我正在使用完成处理程序,但是当我尝试打印 dataDict
的结果时,它会连续打印出三个数组,就像这样...
["Monday": ["Chest", "Arms"]]
["Tuesday": ["Back"], "Monday": ["Chest", "Arms"]]
["Tuesday": ["Back"], "Monday": ["Chest", "Arms"], "Wednsday": ["Legs", "Arms"]]
而我希望它在完成后 return 阵列的单个打印。我做错了什么?
var dataDict : [String:[String]] = [:]
//MARK: - viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
vcBackgroundImg()
navConAcc()
picker.delegate = self
picker.dataSource = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
tableView.tableFooterView = UIView()
Auth.auth().addStateDidChangeListener { (auth, user) in
self.userIdRef = user!.uid
self.colRef = Firestore.firestore().collection("/users/\(self.userIdRef)/Days")
self.loadData { (done) in
if done {
print(self.dataDict)
} else {
print("Error retrieving data")
}
}
}
}
//MARK: - Load Data
func loadData(completion: @escaping (Bool) -> ()){
self.colRef.getDocuments { (snapshot, err) in
if let err = err
{
print("Error getting documents: \(err)");
completion(false)
}
else {
//Appending all Days collection documents with a field of "dow" to daysarray...
for dayDocument in snapshot!.documents {
self.daysArray.append(dayDocument.data()["dow"] as? String ?? "")
self.dayIdArray.append(dayDocument.documentID)
Firestore.firestore().collection("/users/\(self.userIdRef)/Days/\(dayDocument.documentID)/Workouts/").getDocuments { (snapshot, err) in
if let err = err
{
print("Error getting documents: \(err)");
completion(false)
}
else {
//Assigning all Workouts collection documents belonging to selected \(dayDocument.documentID) to dictionary dataDict...
for document in snapshot!.documents {
if self.dataDict[dayDocument.data()["dow"] as? String ?? ""] == nil {
self.dataDict[dayDocument.data()["dow"] as? String ?? ""] = [document.data()["workout"] as? String ?? ""]
} else {
self.dataDict[dayDocument.data()["dow"] as? String ?? ""]?.append(document.data()["workout"] as? String ?? "")
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
// print(self.dataDict)
}
completion(true)
}
}
}
self.dayCount = snapshot?.count ?? 0
}
}
}
我认为这只是你程序的流程。每次循环遍历集合时,都会将它得到的内容添加到字典中。所以在第一次通过时,它会打印字典有 1 个项目。在第二遍中,它向字典中添加了另一个项目,然后打印了现在有 2 个项目的字典,因此打印了 2 个项目。我认为您没有看到意外的行为,这只是您如何根据循环方式对日志语句进行排序。
换句话说,这样打印是有道理的。
我同意@ewizard 的回答。问题出在您的程序流程中。您遍历文档并为每次迭代获取集合中的文档。您还多次重新加载 tableView 和调用完成闭包,这是您不想做的。
为了改善您的程序流程,请尝试使用 DispatchGroup 来获取您的数据,然后在获取所有数据后将它们放在一起一次。请参阅下面的示例以了解基本概念。我的示例是您代码的一个非常简化的版本,我想向您展示您应该执行的重要更改。
func loadData(completion: @escaping (Bool) -> ()) {
self.colRef.getDocuments { (snapshot, err) in
// Handle error
let group = DispatchGroup()
var fetchedData = [Any]()
// Iterate through the documents
for dayDocument in snapshot!.documents {
// Enter group
group.enter()
// Fetch data
Firestore.firestore().collection("/users/\(self.userIdRef)/Days/\(dayDocument.documentID)/Workouts/").getDocuments { (snapshot, err) in
// Add your data to fetched data here
fetchedData.append(snapshot)
// Leave group
group.leave()
}
}
// Waits for until all data fetches are finished
group.notify(queue: .main) {
// Here you can manipulate fetched data and prepare the data source for your table view
print(fetchedData)
// Reload table view and call completion only once
self.tableView.reloadData()
completion(true)
}
}
}
我也同意其他评论,您应该重新考虑 tableView 的数据模型。更合适的结构是二维数组(数组的数组 - 第一个数组转换为 table 视图部分,内部数组对象转换为部分项目)。 这是一个例子:
// Table view data source
[
// Section 0
[day0, day1],
// Section 1
[day2, day3],
// Section 2
[day4, day5],
]
用法示例:
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].count
}
}