I´m trying to retrieve all the values from one child into a collectionView
I keep getting this error
Could not cast value of type '__NSDictionaryM' in this line
let follower = snapshotValue!["Followers"] as! String
here is my code
followRef.child("users").child((FIRAuth.auth()?.currentUser?.displayName)!).observe(FIRDataEventType.value, with: {
snapshot in
let snapshotValue = snapshot.value as? Dictionary<String,AnyObject>
let follower = snapshotValue!["Followers"] as! String
self.postsFollow.insert(postFollowStruct(follower: follower) , at: 0)
self.followersView.reloadData()
})
By what i understand you have many json objects in your snapshot.value? Then you want to cast to an object each one of them and put them into an array?
if let dict = snapshot.value as? [String: AnyObject]{
let followers = dict
for follower in followers {
if let restDict = follower.value as? [String:AnyObject] {
let followerObj = Follower()
followerObj.setValuesForKeys(restDict)
//add to array
}
}
}
Related
I have stored the comments under a post in firebase realtime database. The problem i have is that when i try to parse out the data from firebase i get an error that says Unexpectedly found nil while unwrapping an Optional value. So for example if i try to pront the data stored under degree, i get this nil error. But when i print "comments" instead of the "degree" i successfully fetch the data. My database structure looks like this.
func obeserveComments() {
// get auto-id of post
let commentKey = self.keyFound
let postRef = Database.database().reference().child("posts").child(commentKey)
var tempComments = [Comments]()
postRef.observe(.value, with: {(snapshot) in
if let dict = snapshot.value as? [String:Any] {
if let comments = dict["comments"] as? [String:Any] {
let degree = comments["reply degree"] as! String
// let name = comments["reply name"] as! String
// let text = comments["reply text"] as! String
// let university = comments["reply university"] as! String
// let photoURL = comments["reply url"] as! String
// let url = URL(string: photoURL)
// let timestamp = comments["timestamp"] as! Double
print(degree)
}
}
})
}
The answer by #aytroncb is a good answer, I prefer to leave Firebase data 'Firebasy' as long as possible. In other words coverting to dictionaries looses ordering and and find code like this
[String: [String: [String: Any]]]
To be very hard to read.
I prefer
let snap = snapshot.childSnapshot("comments") //snap becomes a DataSnapshot
So my solution maintains the order and leverages .childSnapshot to leave data in it's DataSnapshot form.
func readPostComments() {
let postRef = self.ref.child("posts") //self.ref points to my firebase
postRef.observeSingleEvent(of: .value, with: { snapshot in
let allPosts = snapshot.children.allObjects as! [DataSnapshot]
for postSnap in allPosts {
print("postId: \(postSnap.key)")
let commentsSnap = postSnap.childSnapshot(forPath: "comments") //will be a DataSnapshot
let allComments = commentsSnap.children.allObjects as! [DataSnapshot]
for commentSnap in allComments {
print(" commentId: \(commentSnap.key)")
let replyDegree = commentSnap.childSnapshot(forPath: "reply_degree").value as? String ?? "No Degree"
let replyName = commentSnap.childSnapshot(forPath: "reply_name").value as? String ?? "No Name"
print(" degree: \(replyDegree) by: \(replyName)")
}
}
})
}
EDIT
For a single post, remove the top part of the code that reads in and iterates over all posts.
func readCommentsForOnePost() {
let postRef = self.ref.child("posts")
let postCommentRef = postRef.child("post_0")
postCommentRef.observeSingleEvent(of: .value, with: { snapshot in
print("postId: \(snapshot.key)")
let commentsSnap = snapshot.childSnapshot(forPath: "comments") //will be a DataSnapshot
let allComments = commentsSnap.children.allObjects as! [DataSnapshot]
for commentSnap in allComments {
print(" commentId: \(commentSnap.key)")
let replyDegree = commentSnap.childSnapshot(forPath: "reply_degree").value as? String ?? "No Degree"
let replyName = commentSnap.childSnapshot(forPath: "reply_name").value as? String ?? "No Name"
print(" degree: \(replyDegree) by: \(replyName)")
}
})
}
Its because firebase is returning your data like this
{
"MAKFW244kdL)Cw;1": [Array of data],
"LOPSw!35pa3flAL4": [Array of data],
"ALV34VR4_A6Vn1a": [Array of data]
}
So change your initial casting of snapshot.value to this:
if let dict = snapshot.value as? [String: [String: Any]]
then loop through that new dictionary like this:
for objectJson in dict.values {
if let comments = objectJson["comments"] as? [String: [String: Any]] {
for commentJson in comments.values {
let degree = commentJson["reply_degree"] as? String
}
}
}
Update
Just read through your post again and noticed your trying to access the comments directly with a key, your first going to need to provide the PostId. Then you can use the above code to loop through the objects
let postRef = Database.database().reference().child("posts").child(postID)
alternatively I believe you can have the comments returned as a normal list by doing something like this:
let postRef = Database.database().reference().child("posts").child("\(postID)/{id}")
How can i read all child data from Firebase.
let ShopRef = Database.database().reference(withPath: “ShoppingMallLst”).child(“ShoppingMall1”)
ShopRef.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
for data in snapshot.children.allObjects as! [DataSnapshot] {
if let data = data.value as? [String: Any] {
let Description = data["Description"] as? String
let Floor = data[“Floor”] as? Int
….
}
}
}
})
But how can i read the data from child "ShopPath?"
child "ShopPath" has type [String: [String: String]]
you can try:
let ShopPath = data["ShopPath"] as? [String: [String: String]]
The key to firebase is to keep data in snapshots as long as you can. So in this case instead of casting items to dictionaries, which loose ordering or arrays that get more complex as the structure gets deeper, leverage DataSnapshots.
Here's the main function to read all shops in ShoppingMall1 - similar to the code in your question.
func readMallShops() {
let ref = self.ref.child("ShoppingMallList").child("ShoppingMall1")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allShopsSnap = snapshot.children.allObjects as! [DataSnapshot]
for shopSnap in allShopsSnap {
let shop = ShopClass(withSnap: shopSnap)
}
})
}
Then a class that holds data about each shop. Note that I pass each snapshot in to initialize the class
class ShopClass {
var name = ""
var height = ""
convenience init(withSnap: DataSnapshot) {
self.init()
let name = withSnap.childSnapshot(forPath: "ShopName").value as? String ?? "No Shop Name"
print("Shop: \(name)")
self.name = name
let shopPathSnap = withSnap.childSnapshot(forPath: "ShopPath")
let shopChildSnap = shopPathSnap.children.allObjects as! [DataSnapshot]
for childDataSnap in shopChildSnap { //iterate over the array in ShopPath
let height = childDataSnap.childSnapshot(forPath: "Height").value as! String
print(" height: \(height)")
self.height = height
}
}
}
And the output looks like this
Shop name: Test
height: 1,180
Shop name: Test 2
height: 2,000
I left off the other child nodes as if you can read height, you can read the rest. So this just assigns and prints out the shop name and height (as a string).
A suggestion as well. Arrays are not well suited for NoSql databases and their use is very situational (avoid if possible). If you're using an array, there's probably a better structure available.
I just started learning Swift and I am trying to read the value from every child in the real-time-firebase. I would like to save it as a string in an array. This is what I have tried:
self.ref.child("someId/name").observe(.value) { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let value = snap.value
self.preis.append(value as! String)
}
}
But I am always getting the error: Thread 1: signal SIGABRT
in this line: self.preis.append(value as! String)
I tried
self.ref.child("someId/name").observe(.value) { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let value = snap.value
self.preis.append(snapshot.value as! String)
}
}
And now I get the value like this: Optional(...)
You can try
self.ref.child("someId/name").observe(.value) { snapshot in
let res = snapshot.value as! String
print(res)
self.preis.append(res)
}
I'm a newb to Swift programming, but experience in other languages.
I am having problem accessing items within NSDictionary to build out view elements. This is coming back from a Firebase instance.
Can someone take a look at the code and the output and lead me in the right direction to access these object properties?
ref.observe(.value, with: { (snapshot) in
for child in snapshot.children { //even though there is only 1 child
let snap = child as! DataSnapshot
let dict = snap.value as? NSDictionary
for (joke, item) in dict ?? [:] {
print(joke)
print(item)
}
}
})
This is the output from the print() methods.
joke2
{
PostUser = "Bobby D";
Punchline = "His money went to the movies.";
Rating = 1;
Setup = "Why did the dad go hungry?";
}
joke
{
PostUser = "Billy G";
Punchline = "Because he couldn't moo to a job.";
Rating = 3;
Setup = "Why did the cow go to school?";
}
Can someone tell me how to create items from these objects? Something like:
var posterName = joke.PostUser
When I try this, I get the error Value of type 'Any' has no member 'PostUser'. I've tried to access these DB object properties in multiple different ways described on SO and can't get any further.
I would recommend you to convert the output into objects like this:
struct Item {
var postUser: String?
var punchline: String?
var rating: Int?
var setup: String?
init(fromDict dict: [String: AnyObject] ) {
self.postUser = dict["PostUser"] as? String
self.punchline = dict["Punchline"] as? String
self.rating = dict["Rating"] as? Int
self.setup = dict["Setup"] as? String
}
}
And use it like this:
ref.observe(.value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
guard let dict = snap.value as? [String: AnyObject] else { continue }
let myItem = Item(fromDict: dict)
print(myItem)
}
})
But you could also access items in your dictionary directly like this:
let posterName = joke["PostUser"] as? String
This is my data structure.
This is how I load club data and its address.
func loadClubs() {
ref = Database.database().reference()
ref.child("club").observe(DataEventType.childAdded, with: { (clubSnapshot) in
if let clubDict = clubSnapshot.value as? [String : AnyObject] {
let name = clubDict["name"] as! String
let explanation = clubDict["explanation"] as! String
let courtNum = clubDict["courtNum"] as! Int
let membershipFee = clubDict["membershipFee"] as! Int
let visitorFee = clubDict["visitorFee"] as! Int
let hasCarParking = clubDict["hasCarParking"] as! Bool
let club2 = Club2(name: name, explanation: explanation, courtNum: courtNum, membershipFee: membershipFee, visitorFee: visitorFee, hasCarParking: hasCarParking)
self.club2Array.append(club2) // Add to clubArray
print(self.club2Array)
self.tableView.reloadData()
}
let addressRef = Database.database().reference()
addressRef.child("address").child(clubSnapshot.key).observe(DataEventType.childAdded, with: { (addressSnapshot) in
if let addressDict = addressSnapshot.value as? [String: AnyObject] {
let clubAddress = ClubAddress(postCode: addressDict["postcode"] as! String, cityName: addressDict["city"] as! String, ward: addressDict["ward"] as! String, address1: addressDict["address1"] as! String, address2: addressDict["address2"] as! String)
self.addressArray.append(clubAddress)
print(self.addressArray)
}
})
})
}
basically, after retrieving each snapshot of club, I get club's key (-KsJB9TkoGNIkiZFGg7), then use that key to retrieve address.
However, print(self.addressArray) doesn't not print anything.
I add a debug breakpoint at if let addressDict = addressSnapshot.value as? [String: AnyObject] { , it does not stop the debug process.
Why is it not calling?
What do I miss here?
Ah! Your code is very close.
Keep in mind that .childAdded iterates over all of the child nodes and loads each one.
In your case, you don't want to iterate over all of the address nodes, you just want one, and don't want to leave an observer.
To do that, we load the specific node child data of the address node, by observeSingleEvent(of: .value. Here's a snippet of the important part.
let addressRef = Database.database().reference()
addressRef.child("address").child(clubSnapshot.key)
.observeSingleEvent(of: .value, with: { (addressSnapshot) in
let dict = addressSnapshot.value as! [String: Any]
let address = dict["address1"] as! String
print(address)