【问题标题】:Firebase - iOS Swift: load table view cell with data retrieved from two separate child nodesFirebase - iOS Swift:使用从两个单独的子节点检索到的数据加载表格视图单元格
【发布时间】:2016-08-10 20:00:45
【问题描述】:

我正在使用 Firebase 构建一个在表格视图中显示问题列表的应用。每个表格视图单元格都包含问题的文本、发布问题的用户的姓名以及发布问题的用户的个人资料照片。

这是我构建 JSON 树的方式:

"questions"
    "firebase_autoID_01"
        "name": "Jim"
        "text": "Why is the earth round?"
        "userID": "123456"
"userInfo"
    "userID": "123456"
    "photoURL": "gs://default_photo"

我正在尝试检索每个问题的 nametext 以及具有相应 userID 的用户的 photoURL,然后将这 3 个元素放在每个表格视图单元格中。所以我试图从questions 子节点和单独的userInfo 子节点中检索数据,以将该数据放入同一个表格视图单元格中。这是我一直在尝试的代码:

当第一次创建用户时,我将他们的 photoURL 设置为我手动上传到 Firebase 存储的默认图像:

...
    let placeholderPhotoRef = storageRef.child("Profile_avatar_placeholder_large.png")
    let placeholderPhotoRefString = "gs://babble-8b668.appspot.com/" + placeholderPhotoRef.fullPath
    //let placeholderPhotoRefURL = NSURL(string: placeholderPhotoRefString)

    let data = [Constants.UserInfoFields.photoUrl: placeholderPhotoRefString]
    self.createUserInfo(data)
}

func createUserInfo(data: [String: String]) {
    configureDatabase()
    let userInfoData = data
    if let currentUserUID = FIRAuth.auth()?.currentUser?.uid{
        self.ref.child("userInfo").child(currentUserUID).setValue(userInfoData)
    }
}

当我尝试从 questionsuserInfo 检索数据时,问题开始出现:

var photoUrlArray = [String]()

func configureDatabase() {
    ref = FIRDatabase.database().reference()
    _refHandle = self.ref.child("questions").observeEventType(.ChildAdded, withBlock: {(snapshot) -> Void in
        self.questionsArray.append(snapshot)
        //unpack the userID from the "questions" child node to indicate which user to get the photoURL from in the "userInfo" child node
        if let uid = snapshot.value?[Constants.QuestionFields.userUID] as? String {
            self._photoURLrefHandle = self.ref.child("userInfo").child(uid).observeEventType(.ChildAdded, withBlock: {(snapshot) -> Void in
                if let photoURL = snapshot.value as? String {
                    self.photoUrlArray.append(photoURL)
                        self.tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: self.questionsArray.count-1, inSection: 0)], withRowAnimation: .Automatic)
                }
            })
        }
    })
}

我在 Xcode 控制台中收到以下错误:

“由于未捕获的异常'NSInternalInconsistencyException'而终止应用程序,原因:'无效更新:第0节中的行数无效。更新后现有节中包含的行数(2)必须等于更新前该节中包含的行数 (0),加上或减去从该节插入或删除的行数(1 插入,0 删除),加上或减去移入或移出该节的行数(0 移入,0 移出)。'"

我在这里所做的是创建一个 firebase 句柄来观察/检索来自 userInfo 子级的用户 photoURL,并且该句柄是在观察/检索来自 @ 的数据的句柄的闭包内创建的987654335@孩子。

问题:如果这不是检索我想要的数据的正确方法,我应该如何处理?如果 JSON 数据目前的结构不正确,我应该如何构建它?

这是我从表格视图中的questions 子节点解压缩nametext 的方法,它工作正常:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell: UITableViewCell! = self.tableView.dequeueReusableCellWithIdentifier("tableViewCell", forIndexPath: indexPath)
    //unpack question from database
    let questionSnapshot: FIRDataSnapshot! = self.questionsArray[indexPath.row]
    var question = questionSnapshot.value as! Dictionary<String, String>
    let name = question[Constants.QuestionFields.name] as String!
    let text = question[Constants.QuestionFields.text] as String!
    cell!.textLabel?.text = name + ": " + text

    return cell!
}

问题:我应该在这里解压photoURL还是在之前的configureDatabase()函数中解压?

【问题讨论】:

    标签: ios swift uitableview firebase firebase-realtime-database


    【解决方案1】:

    既然您就如何将数据库结构存储在 JSON 中寻求建议。 :-

    建议的 JSON 结构:-

      {"Users" : { 
            "userID_1" : {
                  userName : "Jim",
                  userImageURL : "gs://default_photo", 
                  questionsAsked : {
                      "questionID_1" : "QUESTION TEXT",
                       "questionID_2" : "QUESTION TEXT"
                    }
                  } 
    
            "userID_2" : {
                  userName : "Moriarty",
                  userImageURL : "gs://default_photo", 
                  questionsAsked : {
                      "questionID_1" : "QUESTION TEXT",
                       "questionID_2" : "QUESTION TEXT"
                 } 
                }
               }
              } 
    

    由于您的目标是显示发布问题的用户的图像和名称以及问题本身,我认为最好在 usersId 本身中创建questionAsked 的孩子,这样您就可以跟踪否以及该特定用户提出了哪些问题。

    保存用户的个人资料数据如下:-

     FIRDatabase.database().reference().child("Users").child(FIRAuth.auth()!.currentUser()!.uid).setValue([
      userName : "Jim",
      userImageUrl : //URL that you want to save
    ])
    //Instead of FIRAuth.auth()!.currentUser()!.uid you can use childByAutoId too but i wont recommend that.
    

    当用户使用此代码发布帖子调用函数时:-

     let rootRef = FIRDatabase.database().reference().child("Users").child(FIRAuth.auth()!.currentUser()!.uid).child("questionsAsked")
    
       rootRef.observeEventType(.Value, withBlock: {(snap) in
    
                    if snap.exists(){
    
                        if let questionsDictionary = snap.value as? NSMutableDictionary{
    
                            questionsDictionary.setObject("Why is the earth round?", forKey: //your questionID)
                            rootRef.setValue(questionsDictionary)
                    }
                 }else{
    
                      rootRef.setValue([
      your_questionID : "Why is the earth round?"
           ])
    
      }
    })
    

    如何检索问题:-

     let parentRef = FIRDatabase.database().reference().child("Users")
    
     parentRef.observeEventType(.Value, withBlock: {(userSnap) in
    
       if userSnap.exists(){
                for eachUser in userSnap.value as! [String:AnyObject]{
    
                    let userId = eachUser.key as! String
                    parentRef.child(userId).observeEventType(.Value, withBlock: {(userSnap) in
    
                   if let userInfo = userSnap.value as? [String:AnyObject]{
                        let userNameRecieved = userInfo["userName"] as! String
                        let userImageUrlRecieved = userInfo["userImageURL"] as! String
    
    
                     parentRef.child(userId).child("questionsAsked").observeEventType(.Value, withBlock: {(usersQuestion) in
                   if userQuestion.exists(){
                          for eachQ in userQuestion.value as! [String:AnyObject]{
                     let question = eachQ.value as! String
                   //Create a function named - "initialiseCell"  , that will initialise the datasource of the tableView with username, photoURL and question as its : String parameters
                               initialiseCell(question,userNameRecieved,userImageUrlRecieved)
                 }
                }
              })
            }
          })
        } 
      }
    })
    

    至于你的Question_ID:-

    let yourQuestionID = "\(Int(NSDate.timeIntervalSinceReferenceDate() * 1000))

    这将在下一秒或每毫秒生成一个唯一的 yourQuestionID

    此阶段的检索功能会调用整个应用程序数据库中的每个问题,但您可以通过进一步修改代码来过滤它...

    【讨论】:

    • Dravidian,我非常感谢详细的回答 :) 我一直在玩弄不同的 JSON 数据结构,看看哪一个最适合我的应用程序。恕我直言,我宁愿将 questionsusers 子项分开,以避免嵌套数据并保持更扁平的数据结构,正如 Firbase 文档中所建议的那样:firebase.google.com/docs/database/ios/structure-data 我将发布我的 JSON 树现在的样子下面回答。再次感谢您的帮助。
    【解决方案2】:

    这就是我最终构建 Firebase JSON 树的方式:

    "users":
        "userID4321":
            "displayName": "bob"
            "photoURL": "gs://default_photo"
    
    "questions":
        "questionID5678":
            "text": "How are you?"
            "userID": "userID4321"
    

    然后我在 configureDatabase 方法中实现了以下代码:

    func configureDatabase() {
        ref = FIRDatabase.database().reference()
        _refHandle = self.ref.child("questions").observeEventType(.ChildAdded, withBlock: {[weak self] (questionSnapshot) in
            let questionID = questionSnapshot.key
            var question = questionSnapshot.value as! [String: AnyObject]
            question[Constants.QuestionFields.questionID] = questionID
            let userID = question[Constants.QuestionFields.userID] as! String
    
            let usersRef = self?.ref.child("users")
            usersRef?.child(userID).observeEventType(.Value, withBlock: { (userSnapshot) in
                var user = userSnapshot.value as! [String: AnyObject]
                let photoURL = user[Constants.UserFields.photoUrl] as! String
                let displayName = user[Constants.UserFields.displayName] as! String
    
                question[Constants.QuestionFields.photoUrl] = photoURL
                question[Constants.QuestionFields.displayName] = displayName
    ...
    

    每次添加新孩子或创建新问题时,questions 孩子上的 observeEventType 句柄都会为我提供每个问题对象的快照。 然后,我根据从 Firebase 服务器检索到的问题快照创建本地字典。 使用该问题中的userID,然后我可以使用另一个observeEventType 方法识别相应的用户,并检索该用户的photoURL 并将其附加到问题快照中。

    一旦我需要的所有数据都在本地 question 字典中,我会将其附加到我的 questionsArray 中,然后发送到表格视图以显示带有适当数据的每个问题。

    让我知道如果有人读到这篇文章会有不同的做法!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-05
      • 2017-08-01
      • 2014-12-29
      • 2018-07-27
      • 1970-01-01
      相关资源
      最近更新 更多