【问题标题】:How to retrieve firestore data and populate Tableview Rows/Sections from a dictionary?如何从字典中检索 Firestore 数据并填充 Tableview 行/部分?
【发布时间】:2020-04-29 01:26:22
【问题描述】:

我正在尝试使用我已解析并存储在字典中的 Firestore 数据填充我的 tableview 的 Sections 和 Rows,看起来像这样......

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"]]

而我希望它在完成后返回一个数组打印。我做错了什么?

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
            }
        }
    }

【问题讨论】:

  • 你试过把DispatchQueue.main.async {放在if self.dataDict[dayDocument.data()["dow"] as? String ?? ""] == nil {上面吗?仍在处理您的代码,但可能包括在异步调用中存储数据以及 reloadData 调用。
  • 这些行:picker.delegate = self picker.dataSource = self 不引用任何内容,您需要将 tableview 委托设置为具有 dataSourcepicker 的关系如何,只有 picker 不是您的 tableview ?如果是,它的声明在哪里?
  • 我真的不认为您应该将数据存储在字典中以用于像这样的 tableview,拥有一个数组更有意义。它更容易概念化,当tableview形成时,它会循环遍历数组并获取数组中的每个插槽,并将其放在一行中。您可以将对象存储在数组中。
  • 我知道你在代码中看不到,但我实际上使用的是 UITableViewController,所以我不需要设置委托或数据源,它们是由父级隐式设置的。

标签: ios firebase asynchronous google-cloud-firestore tableview


【解决方案1】:

我认为这只是您程序的流程。每次遍历集合时,都会将它获得的内容添加到字典中。所以在第一次通过时,它将打印字典有 1 个项目。在第二遍中,它将另一个项目添加到字典中,然后打印字典,现在其中有 2 个项目,因此打印了 2 个项目。我认为您没有看到意外的行为,这只是您如何根据循环方式对日志语句进行排序。

换句话说,这样打印是有道理的。

【讨论】:

  • 没错。每个文档都会调用一次完成(在本例中为 3 次),这通常不是预期的行为。
  • 一切尽在不言中
【解决方案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)
        }
    }
}

我也同意其他 cmets 的观点,即您应该重新考虑 tableView 的数据模型。更合适的结构是二维数组(数组数组 - 第一个数组转换为表格视图部分,内部数组对象转换为部分项目)。 这是一个例子:

// 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
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-20
    • 2014-09-08
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 2018-01-19
    • 1970-01-01
    相关资源
    最近更新 更多