Search code examples
swiftfirebase-realtime-databasensdictionaryfirebase-storagejson-deserialization

firebase json deserialization swift


I have JSON data I need entered in a dictionary and then appended to an array.

Below is where that should take place, but I get the error

this class is not key value coding-compliant for the key K0tA3yTnxAWxCFBZoeyAjcr3yYy1.'

let room = rooms[indexPath.row]

if let dictionary = room.members {

    let user = User()

    user.setValuesForKeys(dictionary)

    liveCell.members.append(user)

}

Below are the classes

User

class User: NSObject {
    var email: String?
    var name: String?
    var profileImageUrl: String?
}

Room

class Room: NSObject {
    var rid: String?
    var owner: String?
    var groupChatName: String?
    var groupChatDescription: String?
    var members: NSDictionary?
}

Room object in database

"rooms" : {
    "B7C7E469-945E-4327-9473-AAE34FCB9B41" : {
      "groupChatDescription" : "123ae",
      "groupChatName" : "Test123",
      "members" : {
        "K0tA3yTnxAWxCFBZoeyAjcr3yYy1" : {
          "email" : "S@gmail.com",
          "name" : "Steve",
          "profileImageUrl" : "https://firebasestorage.googleapis.com"
        }
      },
      "owner" : "Steve"
    }
},

To recap, rooms is an array of rooms, room is the specific room object, members is an array of users, and member is the specific user.

I have used the dictionary method already to retrieve users from my database, but I found some differences between the datasnapshots.

snapshot.value from FireBase:

Optional({
    email = "S@gmail.com";
    name = Steve;
    profileImageUrl = 
"https://firebasestorage.googleapis.com";
})

This format works for the dictionary

data snapshot using print(room.members):

Optional({
    K0tA3yTnxAWxCFBZoeyAjcr3yYy1 =     {
        email = "S@gmail.com";
        name = Steve;
        profileImageUrl = 
"https://firebasestorage.googleapis.com";
    };
})

This format doesn't work for the dictionary I believe it's because of the key but I don't know how to get rid of it because that is essentially how I separate each member in the members array.

Any suggestions?

As for what liveCell is here is a screen shot that explains it well... Screenshot

This is a liveCell and you can see where the data matches up. The yellow rectangle is another uicollectionview that houses the array of members profileimages in which each profile image will be inside the red square.

Hope this clears things up


Solution

  • Instead of trying to fix all of that code, here's a clean, simple way to populate an array of rooms, where each room has an array of users.

    Given a Firebase Structure similar to yours

      "rooms"
        "room_0"
          "groupChatName" : "Test123",
          "members"
            "user_0"
              "name" : "Barry"
            "user_1"
              "name" : "Maurice"
            "user_2"
              "name" : "Robin"
    

    We start off with the RoomClass and UserClass. Note that I make those classes do the heavy lifting - they tear down the snapshot and store it's values.

    Note that I treat the members child node as a snapshot so I can iterate over it while maintaining it's key: value pairs.

    class RoomClass {
        var key = ""
        var groupChatName = ""
        var membersArray = [UserClass]()
    
        init(snapshot: DataSnapshot) {
            let dict = snapshot.value as! [String: Any]
            let membersSnap = snapshot.childSnapshot(forPath: "members")
            let snapKey = snapshot.key
            let groupName = dict["groupChatName"] as! String
    
            self.key = snapKey
            self.groupChatName = groupName
    
            for member in membersSnap.children {
                let memberSnap = member as! DataSnapshot
                let user = UserClass(snapshot: memberSnap)
                self.membersArray.append(user)
            }
        }
    }
    
    class UserClass {
        var key = ""
        var name = ""
    
        init(snapshot: DataSnapshot) {
            let dict = snapshot.value as! [String: Any]
            let snapKey = snapshot.key
            let userName = dict["name"] as! String
            self.key = snapKey
            self.name = userName
        }
    }
    

    We then define the array to store the rooms and the code to populate it

    var roomsArray = [RoomClass]()
    
    func doButton0Action() {
        let roomsRef = self.ref.child("rooms")
        roomsRef.observeSingleEvent(of: .value, with: { snapshot in
    
            for child in snapshot.children {
                let snap = child as! DataSnapshot
                let room = RoomClass(snapshot: snap)
                self.roomsArray.append(room)
            }
    
            //this just demonstrates the rooms and members are loaded
            for room in self.roomsArray {
                let groupChatName = room.groupChatName
    
                print(groupChatName)
    
                for user in room.membersArray {
                    let userName = user.name
                    print("  \(userName)")
                }
            }
        })
    }
    

    and the output

    Test123
      Barry
      Maurice
      Robin