Firebase Query snapshot nil?

后端 未结 3 1532
孤独总比滥情好
孤独总比滥情好 2020-12-20 12:10

I\'m creating a simple chat app to learn Swift and Firebase. I have a query that checks for a chat room\'s messages to load into a tableView. The query returns a snapshot bu

相关标签:
3条回答
  • 2020-12-20 12:53

    Your dictionary look like :

    ▿ 1 element
      ▿ 0 : 2 elements
        - key : "-KotqLUUucaRagTRt967"
        ▿ value : 3 elements
          ▿ 0 : 2 elements
            - key : sender
            - value : eGTYRSo81JefgasYLRHUFHUTnEC3
          ▿ 1 : 2 elements
            - key : text
            - value : test
          ▿ 2 : 2 elements
            - key : timestamp
            - value : 1499914135546
    

    But in fact it should look like (If you want to access to dictionary["sender"]) :

    ▿ 2 elements
      ▿ 0 : 2 elements
        - key : sender
        - value : eGTYRSo81JefgasYLRHUFHUTnEC3
      ▿ 1 : 2 elements
        - key : text
        - value : test
      ▿ 2 : 2 elements
        - key : timestamp
        - value : 1499914135546
    

    First solution :

    You need to add .child("-KotqLUUucaRagTRt967") in your query.

    OR

    Second solution :

    You need to do something like that :

    query.observe(.childAdded, with: { snapshot in
        for child in snapshot.children {
            guard let value = child.value as? NSDictionary else {
                return
            }
    
            guard let sender = value["sender"] as? String else {
                return
            }
    
            // You can user the sender
        }
    })
    

    UPDATE :

    query.observe(.childAdded, with: { snapshot in
        for child in snapshot.children.allObjects as! [FIRDataSnapshot] {
            if let value = child.value as? [String:Any], let sender = value["sender"] as? String {  
                // You can user the sender
            }
        }
    })
    

    NOTE

    I replaced observeSingleEvent with observe, as Frank van Puffelen said, it's an uncommon combination for .childAdded.

    0 讨论(0)
  • 2020-12-20 13:02

    I do not think you want a ChildAdded handler, so I go with a observeSingleEvent example since you want to query data from the database at that time without triggers. When you use a observeSingleEvent, it is important to keep the database in sync. I would recommend using the code below:

    query.keepSynched(true) //keeps data in sync with database, if you have data persistince on in your appDelegate
    query.observeSingleEvent(of: .value, with: { (snapshot) in //notice the changed here
                print(snapshot)
                //Since you want to loop again because there could be multiple
                //messages in that chatroom which all have a unique ID, do this loop:
                let enumerator = snapshot.children
                while let rest = enumerator.nextObject() as? FIRDataSnapshot {
                   //this is 1 single message here
                   let values = rest.value as? NSDictionary
                   for (key, value) in values{
                   print("Key: \(key), value: \(value)") 
                   }
                   //lets say you want to check if there is a value with a key named "text":
                let textUser = values?["text"] as? String ?? "No text found" 
                   //providing a default value if there is no text. You could leave it empty, than it is nil (not recommend)
                   //Not only is this a lot more readable, I do not use force unwrapping so your app 
                   //cannot cause an exception.
    
                   //Get more values from the dictionary as I did with textUser
                   //Initialize message than here. You will see you do not need to force unwrap anything :D
                   }
    
                }
    
            })
    
    0 讨论(0)
  • 2020-12-20 13:07

    As Pipiks explained, your issue is that you are attempting to access individual message details at one level higher than the returned data.

    The snapshot value is giving you a dictionary of chat messages, where the top level's keys are the chat message keys. To map your returned data to an array of messages, I would use the following code:

    query.observeSingleEvent(of: .childAdded, with: { snapshot in
    
        guard let messagesDict = snapshot.value as? [String: AnyObject] else { return }
    
        self.messages = messagesDict.flatMap({ (messageId: String, messageData: Any) -> Message? in
            guard
                let sender = messageData["sender"] as? String,
                let text = messageData["text"] as? String,
                let timestamp = messageData["timestamp"] as? Int,
                let message = Message(key: messageKey, sender: sender, text: text, timestamp: timestamp) else {
                    return nil
            }
            return message
        })
    
        self.tableView.reloadData()
    })
    

    What this does is map your dictionary of messages to an array of Message objects.

    I have used a flatMap to filter out any messages which do not have sender, text or timestamp values (so the flatMap returns a [Message] object).

    Does that solve the issue?

    0 讨论(0)
提交回复
热议问题