从 Firebase RT 数据库中提取数据时遇到问题

Having Trouble Pulling Data From Firebase RT Database

对编码非常陌生,如果这里有什么特别明显的地方,我们深表歉意。

我正在开发一个应用程序,可以用来跟踪我的举重分配。我这样写数据:

public func writeNewExercise(splitName: String, day: Int, exerciseNum: Int, exerciseName: String, sets: String, repsSecs: String, isTimed: Bool, completion: @escaping (Bool) -> Void) {
        
        let user = AuthManager.shared.user
        var exerciseRef: DatabaseReference!
        exerciseRef = Database.database().reference(withPath: "\(user.uid)/splits/\(splitName)/day \(day)/exercise \(exerciseNum)")
        
        var dataDictionary: [String: Any] = [:]
        dataDictionary["Exercise Name"] = exerciseName
        dataDictionary["Sets"] = sets
        dataDictionary["Reps or Secs"] = repsSecs
        dataDictionary["Is Timed"] = isTimed
        
        exerciseRef.setValue(dataDictionary) { error, _ in
            if error == nil {
                completion(true)
                return
            } else {
                completion(false)
                return
            }
            
        }
        
    }

这为我提供了 Firebase 中的 JSON 字典,如下所示:

{
  "8aIzPgurRLPPEYDpXWv54r5JjvH3" : {
    "splits" : {
      "Test Split" : {
        "day 1" : {
          "exercise 0" : {
            "Exercise Name" : "Curls",
            "Is Timed" : false,
            "Reps or Secs" : "12",
            "Sets" : "4"
          }
        }
      }
    }
  },

我现在想做的是提取这些数据,这样我就可以将每个练习插入到 tableView 单元格中。不想对它做任何花哨的事情——只是能够查看它,这样我就可以跟随我的拆分。我这样做更多是为了练习而不是实用性。我试过大约 15 种不同的方式提取数据,但无论我做什么都行不通。我完全被难住了。这是我现在拥有的代码:

    public func downloadPost(splitName: String, day: Int, completion: @escaping (Bool) -> Void){
        
        let user = AuthManager.shared.user
        var exerciseRef: DatabaseReference!
        exerciseRef = Database.database().reference()
        
        var exerciseArray = [Exercise]()
        
        exerciseRef.child("Users").child(user.uid).child("splits").child(splitName).child("day \(day)").observe(.value) { snapshot in
            
            if snapshot.exists(){
                for x in 0...100{
                
                let nameValue = snapshot.childSnapshot(forPath: "exercise \(x)/Exercise Name").value
                let setsValue = snapshot.childSnapshot(forPath: "exercise \(x)/Sets").value
                let repsOrSecsValue = snapshot.childSnapshot(forPath: "exercise \(x)//Sets/Reps or Secs").value
                let isTimedValue = snapshot.childSnapshot(forPath: "exercise \(x)/Sets/Is Timed").value
                
                let exercise = Exercise(name: "\(nameValue!)",
                                        sets: "\(setsValue!)",
                                        repsOrSecs: "\(repsOrSecsValue!)",
                                        isTimed: isTimedValue as? Bool ?? false)
                    print(exercise.name)
                    print(exercise.sets)
                    print(exercise.repsOrSecs)
                    print(exercise.isTimed)
                exerciseArray.append(exercise)
                completion(true)
                return
            }
        } else {
            print("no snapshot exists")
        }
        
        print(exerciseArray)
        
    }
    }

练习是我创建的自定义 class,它具有名称、组数、重复次数和 Bool“isTimed”。此代码打印:

不存在快照,[]

正在尝试其他方法,我可以打印类似的内容:

无, 0, 0, 假

我尝试过的其他一些东西是:

此时我可能已经为此投入了大约 15 个小时,但我真的想不通。任何帮助将不胜感激。我会密切关注此 post 和 post 如果需要我可能遗漏的任何信息。

谢谢!

更新

使用下面 Medo 提供的代码,一切正常。对于其他尝试这样做的人,在按照 Medo 演示的方式拉出数组后,只需将 tableViewCell 中的所有标签设置为 ExportedArray[indexPath.row].theClassPropertyYouWant

这是我的解决方案:

public func downloadPost(splitName: String, day: Int, completion: @escaping (([Exercise]) -> ())){
    
    let user = AuthManager.shared.user
    var exerciseRef: DatabaseReference!
    exerciseRef = Database.database().reference()
    
    var exerciseArray = [Exercise]()
    
    exerciseRef.child(user.uid).child("splits").child(splitName).child("day \(day)").observe(.value, with: { snapshot in
        
        guard let exercises = snapshot.children.allObjects as? [DataSnapshot] else {
            print("Error: No snapshot")
            return
        }
        
        
        
        for exercise in exercises {
            let exerciseData = exercise.value as? [String:Any]
            
            let exerciseName = exerciseData["Exercise Name"] as? String
            let isTimed = exerciseData["Is Timed"] as? Bool
            let repsOrSecs = exerciseData["Reps or Secs"] as? String
            let sets = exerciseData["Sets"] as? String
            
            let exerciseIndex = Exercise(name: "\(exerciseName)",
                                    sets: "\(sets)",
                                    repsOrSecs: "\(repsOrSecs)",
                                    isTimed: isTimed)
            
            exerciseArray.append(exerciseIndex)
            
        }
      completion(exerciseArray)  
    }
}

您可以调用函数 downloadPost 并从中提取数组,如下所示:

downloadPost(splitName: "", day: 0, completion: {
    aNewArray in
    
    // aNewArray is your extracted array [Exercise]
    print("\(aNewArray)")
    
})

需要注意的几点:

  1. 如果您想确保按顺序存储练习(并按顺序提取数据),那么不要练习 0、1、2...(在您的数据库中),名称它由一个名为“childByAutoId”的 id。 Firebase 会在您 add/push 或提取该数据时自动为您排序。将 writeNewExercise 函数替换为:

    let user = AuthManager.shared.user
    var exerciseRef: DatabaseReference!
    let key = Database.database().reference().childByAutoId().key ?? ""
    exerciseRef = Database.database().reference(withPath: "\(user.uid)/splits/\(splitName)/day \(day)/\(key)")
    
    var dataDictionary: [String: Any] = [:]
    dataDictionary["Exercise Name"] = exerciseName
    dataDictionary["Sets"] = sets
    dataDictionary["Reps or Secs"] = repsSecs
    dataDictionary["Is Timed"] = isTimed
    
    exerciseRef.setValue(dataDictionary) { error, _ in
        if error == nil {
            completion(true)
            return
        } else {
            completion(false)
            return
        }
    
    }
    
  2. Firebase 实时数据库是广度优先搜索和下载。所以你应该尽可能地扁平化你的数据库结构。这意味着在 exerciseRef.child("Users").child(user.uid).child("splits").child(splitName).child("day \(day)") 上观察仍会下载所有练习日。