从 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,
假
我尝试过的其他一些东西是:
- 使用斜杠导航而不是在 .observe.value
中链接 .childs
- 使用 .getData 代替 .observe
- 把DispatchQueue.main.async扔得到处都是
- 将exerciseRef设为整个数据库,然后在分配snapshot.value
时调用到特定点
- 还有很多
此时我可能已经为此投入了大约 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)")
})
需要注意的几点:
如果您想确保按顺序存储练习(并按顺序提取数据),那么不要练习 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
}
}
Firebase 实时数据库是广度优先搜索和下载。所以你应该尽可能地扁平化你的数据库结构。这意味着在 exerciseRef.child("Users").child(user.uid).child("splits").child(splitName).child("day \(day)")
上观察仍会下载所有练习日。
对编码非常陌生,如果这里有什么特别明显的地方,我们深表歉意。
我正在开发一个应用程序,可以用来跟踪我的举重分配。我这样写数据:
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, 假
我尝试过的其他一些东西是:
- 使用斜杠导航而不是在 .observe.value 中链接 .childs
- 使用 .getData 代替 .observe
- 把DispatchQueue.main.async扔得到处都是
- 将exerciseRef设为整个数据库,然后在分配snapshot.value 时调用到特定点
- 还有很多
此时我可能已经为此投入了大约 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)")
})
需要注意的几点:
如果您想确保按顺序存储练习(并按顺序提取数据),那么不要练习 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 } }
Firebase 实时数据库是广度优先搜索和下载。所以你应该尽可能地扁平化你的数据库结构。这意味着在
exerciseRef.child("Users").child(user.uid).child("splits").child(splitName).child("day \(day)")
上观察仍会下载所有练习日。