Stefan
Stefan

Reputation: 936

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" : "[email protected]",
          "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 = "[email protected]";
    name = Steve;
    profileImageUrl = 
"https://firebasestorage.googleapis.com";
})

This format works for the dictionary

data snapshot using print(room.members):

Optional({
    K0tA3yTnxAWxCFBZoeyAjcr3yYy1 =     {
        email = "[email protected]";
        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

Upvotes: 0

Views: 588

Answers (2)

Jay
Jay

Reputation: 35657

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

Upvotes: 1

jlmurph
jlmurph

Reputation: 1090

Weird solution, but you shouldn't cast to [String:AnyObject] with firebase. It's never worked for me either, but doing:

snapshot.value as? [String:Any]

has worked. It's more the dev's responsibility to add custom classes to interprete the snapshot's value in custom use cases (class for rooms, cars, etc. etc.) by casting possible keys of snapshot.value appropriately.

Upvotes: 0

Related Questions