1

I am trying to code a message chat log between users. so I want to retrieve only specific data from my message array. For example when a person sends a text message I only want to retrieve from my message array fromId,toId, and text. when a person sends an image I want to only retrieve fromId,toId and imageUrl. At the moment the app crashes because when a person sends a text "imageUrl" is nil and vice versa sending an image where "text" is nil. Is there a way around this? Or do I have to create 2 separate arrays? Thanks in advance

message array

class Message {

    var fromId: String
    var text: String
    var toId: String
    var timeStamp: NSNumber?
    var imageUrl: String



    init(fromId: String, text: String, toId: String, timeStamp: NSNumber, imageUrl: String){


        self.fromId = fromId
        self.text = text
        self.toId = toId
        self.imageUrl = imageUrl

    }

    init(snapshot:FIRDataSnapshot) {

        self.fromId = (snapshot.value! as! NSDictionary)["fromId"] as! String!

        self.toId = (snapshot.value! as! NSDictionary)["toId"] as! String!

        self.text = (snapshot.value! as! NSDictionary)["text"] as! String!

        self.imageUrl = (snapshot.value! as! NSDictionary)["imageUrl"] as! String!


    }

firebase structure

{ "messages": {

"KkGAWnQhqnOx525vv2W": {

"fromId": tvyT6UF6mWZeewAJXt076qqt7ek2,
"toId": USk3w8ZPYlRhfDmEwWwtkMhzUNX2,
"text": "Hello"
},
"hujU6UF6mWZeewAJXt0": {

"fromId": tvyT6UF6mWZeewAJXt076qqt7ek2,
"toId": USk3w8ZPYlRhfDmEwWwtkMhzUNX2,
"imageUrl": "https://firebasestorage.googleapis.com/v0/b.."
}

method used to retrieve data

var message = [Message]()

func observeMessages(){

    let ref = FIRDatabase.database().reference().child("user-messages").child(uid)

    ref.observe(.childAdded, with: { (snapshot) in


        let messageId = snapshot.key

        let messageReference = FIRDatabase.database().reference().child("messages").child(messageId)
        messageReference.observe(.value, with: { (snapshot) in


            for posts in snapshot.children{

                let message = Message(snapshot: posts as! FIRDataSnapshot )

                let chatPartnerId = message.chatPartnerId()

                self.messagesDictionary[chatPartnerId!] = message


                    self.messageTableView.reloadData()
                })
            }

    }, withCancel: nil)
}
5
  • Use optionals instead of force unwrapping the data. Commented May 17, 2017 at 17:47
  • Hi @JohnMontgomery thanks for your reply. could you elaborate more on your answer please Commented May 17, 2017 at 17:50
  • You say the text and URL are sometimes nil. That means they will crash when you use ! to force unwrap them. You need to not do that and make the properties String?s instead. Commented May 17, 2017 at 17:53
  • @j.iese Check my below solution for that also it is batter if you add one more key in your message object as I suggested in my solution Commented May 17, 2017 at 17:58
  • 1
    @NiravD ok I am trying it out now Commented May 17, 2017 at 18:04

1 Answer 1

1

The reason it is crashing is because you are forcefully wrapping value when you subscript with dictionary. You need to use if let or guard let to wrapped the optional or you can use Nil-Coalescing Operator with default value empty and later check which one is non empty text or image.

init(snapshot:FIRDataSnapshot) {
    let dictionary = snapshot.value as! [String:Any]
    self.fromId = dictionary["fromId"] as! String
    self.toId = dictionary["toId"] as! String
    self.text = dictionary["text"] as? String ?? ""
    self.imageUrl = dictionary["imageUrl"] as? String ?? ""
}

Also the batter option is to add one more key with your message object as type and with value either text or image. Make your structure like this.

{
    "messages": {

        "KkGAWnQhqnOx525vv2W": {
            "fromId": tvyT6UF6mWZeewAJXt076qqt7ek2,
            "toId": USk3w8ZPYlRhfDmEwWwtkMhzUNX2,
            "type" "text"
            "text": "Hello"
        },
        "hujU6UF6mWZeewAJXt0": {

            "fromId": tvyT6UF6mWZeewAJXt076qqt7ek2,
            "toId": USk3w8ZPYlRhfDmEwWwtkMhzUNX2,
            "type" "image"
            "imageUrl": "https://firebasestorage.googleapis.com/v0/b.."
        }
    }
}

After that get the value of type and compare is it text or image

init(snapshot:FIRDataSnapshot) {
    let dictionary = snapshot.value as! [String:Any]
    self.fromId = dictionary["fromId"] as! String
    self.toId = dictionary["toId"] as! String
    //Make one more property with name type of String
    self.type = dictionary["type"] as! String
    self.text = dictionary["text"] as? String ?? ""
    self.imageUrl = dictionary["imageUrl"] as? String ?? ""
}

Now when you access this object check its type and access its property text and imageUrl according to it.

Sign up to request clarification or add additional context in comments.

3 Comments

ok thank you so much it worked. Now with adding type to the array how would I check its type and access its property?
@j.iese Welcome mate :) Simply check if (object.type == "text") {//access text property } else { //access imageUrl property }
@j.iese Welcome mate :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.