【问题标题】:Having Trouble Pulling Data From Firebase RT Database从 Firebase RT 数据库中提取数据时遇到问题
【发布时间】:2021-10-12 04:36:21
【问题描述】:

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

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

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

Exercise 是我创建的一个自定义类,它有一个名称、组数、代表数和一个布尔“isTimed”。此代码打印:

不存在快照,[]

尝试其他的东西,我已经得到它来打印类似的东西:

空, 0, 0, 假的

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

  • 使用斜线导航而不是在 .observe.value 中链接 .childs
  • 使用 .getData 代替 .observe
  • 到处乱扔 DispatchQueue.main.async
  • 将executiveRef设为整个数据库,然后在分配snapshot.value时调用到特定点
  • 还有很多

此时我可能已经为此投入了大约 15 个小时,但我真的无法弄清楚。任何帮助将不胜感激。如果需要,我会密切关注这篇文章并发布我可能遗漏的任何信息。

谢谢!

更新

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

【问题讨论】:

    标签: ios swift firebase-realtime-database uikit


    【解决方案1】:

    这是我的解决方案:

    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” ”。 Firebase 会在您添加/推送或提取数据时自动为您排序。将您的 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)") 上观察仍然会下载所有练习日。

    【讨论】:

    • 感谢您的回复!我一直在尝试将其集成到我的代码中,但我仍然遇到了一些麻烦。首先,我尝试添加 [Exercise] 的返回值,然后调用 let aNewArray = downloadPost。这返回了一个空数组。然后,我尝试简单地在函数之外创建一个数组,让函数填充它,然后使用它,我仍然得到一个空数组。你如何从这个函数中得到数组?我尝试在 for...in 循环的末尾添加几个打印函数以进行调试,但它们似乎从未运行过,所以似乎 for...in 没有被调用
    • 我修改了 downloadPost 函数以返回练习数组。我还添加了如何调用 downloadPost 并从中提取数组的代码。如果 for 循环从未被调用,那么您应该检查 children 不是 nil 并且快照存在。
    • 从数据库读取时需要确保所有子节点都存在。您似乎没有 child("Users") 节点。你应该在 "writeNewExercise" Database.database().reference(withPath: "Users/(user.uid)/splits/(splitName)/day (day)/(key)").setValue.. .
    • 一定是它!我从写入函数中删除了“用户”路径,因为我认为它是多余的,但从下载函数中删除。剩下的晚上我都很忙,但明天我会把这一切都启动并运行起来,然后接受你的回答。再次感谢!
    • 一切顺利!我知道 cmets 只是用于解决技术问题,但我只想说我真的很感谢你抽出时间来帮助一个完全陌生的人。这让我非常沮丧,我开始认为我应该一起放弃编码。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-20
    • 2018-02-27
    • 2018-12-13
    • 1970-01-01
    • 1970-01-01
    • 2020-08-05
    • 2019-12-24
    相关资源
    最近更新 更多