Firebase - iOS Swift:使用从两个单独的子节点检索到的数据加载表格视图单元格

Posted

技术标签:

【中文标题】Firebase - iOS Swift:使用从两个单独的子节点检索到的数据加载表格视图单元格【英文标题】:Firebase - iOS Swift: load table view cell with data retrieved from two separate child nodes 【发布时间】: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()函数中解压?

【问题讨论】:

【参考方案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 中,然后发送到表格视图以显示带有适当数据的每个问题。

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

【讨论】:

以上是关于Firebase - iOS Swift:使用从两个单独的子节点检索到的数据加载表格视图单元格的主要内容,如果未能解决你的问题,请参考以下文章

Firebase.google.com iOS 中的 Firebase 动态链接(Swift)

使用firebase问题推送通知(ios,swift 4)[关闭]

搜索 firebase 数据:iOS、Swift

带有 Swift 的 Firebase 推送通知在 ios 11.4 中不起作用

swift 3,ios 10 - 未收到推送通知 firebase

如何在 UILabel 中显示名字?我正在使用 Firebase 和 Swift 开发 IOS 应用程序