reading data on firebase - swift

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)

Related

Not able to read data from Firebase realtime database

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 fetch specific data from firebase and store it in a dictionary?

I'm trying to fetch data from my firebase database, such that i can store it in a form of dictionary which is a type [String: [Any]] where key is the unique id and the value is a type of array which has the data stored under uniqueID->Question.
func getData(currUser: String, completion: #escaping (([String : [Any]]) -> ())) {
var newArray = [String : [Any]]()
let ref = Database.database().reference(fromURL: "MYURL").child("users/\(currUser)/Questions").observeSingleEvent(of: .value, with: { (snap) in
let enumerator = snap.children
while let rest = enumerator.nextObject() as? DataSnapshot, let value = rest.value{
newArray.updateValue([value], forKey: rest.key)
}
completion(newArray)
})
}
this completion block gives me:
["-LlpbizBpQTXOQ6zv0zd": [{
Qusetion = (
Hello,
test,
kkkkkkkkkkk
);
}]]]
Instead how can i get
["-LlpbizBpQTXOQ6zv0zd": [Hello,test,kkkkkkkkkkk]]
You're converting the value to a string, while it's actually a JSON object. That's why the value in your dictionary is a JSON object.
To only get the question text under Qusetion (typo?), you'll need to loop over that child snapshot and collect the individual values. Something like:
var newArray = [String : [Any]]()
let ref = Database.database().reference(fromURL: "MYURL").child("users/\(currUser)/Questions").observeSingleEvent(of: .value, with: { (snap) in
let enumerator = snap.children
while let rest = enumerator.nextObject() as? DataSnapshot {
var values = [String]
let valueEnumerator = rest.childSnapshot(atPath: "Qusetion").children
while let valueRest = valueEnumerator.nextObject() as? DataSnapshot, let value = rest.value {
values.append(value)
}
newArray.updateValue([values], forKey: rest.key)
}
completion(newArray)
})

Swift Ambiguous use of 'subscript'

I had recently added new pods to my project and updated the previous ones, this code had all worked before and now I am getting "Ambiguous use of 'subscript'" I have been checking other answers and it seems like the way I have it currently set up is the correct way. I am not sure why all of a sudden these lines of code would get flagged as errors.
databaseRef.child("following").queryOrderedByKey().observeSingleEvent(of: .value, with: { (postsSnapshot) in
let posts = postsSnapshot.value as! [String: AnyObject]
for (_, post) in posts {
for (_, postInfo) in post as! [String: AnyObject] {
if let followingID = postInfo["uid"] as? String {
for each in self.following {
if each == followingID {
guard let uid = postInfo["uid"] as! String? else {return}
guard let name = postInfo["businessName"] as! String? else {return}
guard let address = postInfo["businessStreet"] as! String? else {return}
guard let state = postInfo["businessState"] as! String? else {return}
guard let city = postInfo["businessCity"] as! String? else {return}
let data = ["uid":postInfo["uid"] as! String, "businessName":postInfo["businessName"] as! String, "businessStreet":postInfo["businessStreet"] as! String, "businessState":postInfo["businessState"] as! String, "businessCity":postInfo["businessCity"] as! String,"businessZIP":postInfo["businessZIP"] as! String, "businessPhone":postInfo["businessPhone"] as! String, "businessLatitude":postInfo["businessLatitude"] as! String, "businessLongitude":postInfo["businessLongitude"] as! String, "businessWebsite":postInfo["businessWebsite"] as! String, "facebookURL":postInfo["facebookURL"] as! String, "foursquareURL":postInfo["foursquareURL"] as! String, "googleURL":postInfo["googleURL"] as! String, "instagramURL":postInfo["instagramURL"] as! String, "snapchatURL":postInfo["snapchatURL"] as! String, "twitterURL":postInfo["twitterURL"] as! String, "yelpURL":postInfo["yelpURL"] as! String]
The problem is your use of AnyObject. postInfo is being declared as AnyObject but then you attempt to use it like a dictionary, hence the error.
If post is a dictionary of dictionaries, then declare is as such:
Change:
for (_, postInfo) in post as! [String: AnyObject] {
to:
for (_, postInfo) in post as! [String: [String: AnyObject]] {
This will change postInfo to be [String: AnyObject] instead of AnyObject.

Code only retrieving one value from data in Firebase

As the title suggests, I'm trying to retrieve some data from firebase database, but my code's not working. I have three children (I guess that's how you call them) inside "Posts" called "title", "description" and "username" and I'm trying to get all of them and append them into a variable to use them later, but it only retrieves the first value of each of them, despite the fact that there are like 5 values. Anyone knows why?
By the way, I'm calling upon this function on my ViewDidLoad.
let postDB = Database.database().reference().child("Posts")
postDB.queryOrderedByKey().observeSingleEvent(of: .value) { (snapshot) in
if let snapshotValue = snapshot.value as? NSDictionary {
let postTitle = snapshotValue["title"] as? String
let postUsername = snapshotValue["username"] as? String
let postDesc = snapshotValue["description"] as? String
let postArray = postStruct(title: postTitle, description: postDesc, username: postUsername)
self.newPost.insert(postArray, at: 0)
}
import UIKit
import FirebaseDatabase
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// MARK: - variables
var postDB: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
// getting a node from database //
postDB = Database.database().reference().child("Posts")
// observing data changes //
postDB.observe(DataEventType.value) { (dataSnapshot) in
self.postArray.removeAll()
if dataSnapshot.childrenCount > 0 {
for post in dataSnapshot.children.allObjects as! [DataSnapshot] {
let object = post.value as! [String: Any]
let description = object["description"] as! String
let title = object["title"] as! String
let userName = object["username"] as! String
let model = postStruct(title: title, description: description, username: userName))
self.postArray.append(model)
}
}
self.tableView.reloadData()
}
}
}
Try this – the code replaces what you currently have in the snapshot handler.
if let firebaseList = snapshot.children.allObjects as? [FIRDataSnapshot] {
if let swiftList = snapshot.value as? [String:AnyObject] {
for firebaseItem in firebaseList {
let childID = firebaseItem.key as String
let swiftItem = swiftList[childID]
let postTitle = swiftItem?["title"] as? String
let postUsername = swiftItem?["username"] as? String
let postDesc = swiftItem?["description"] as? String
let postArray = postStruct(title: postTitle, description: postDesc, username: postUsername)
self.newPost.insert(postArray, at: 0)
}
}
}
}
Worked for me. It gets all the values now you just have to put them in an array
postDB.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull #NotNull Task<DataSnapshot> task) {
if(!task.isSuccessful()){
Log.e("firebase", "Error getting data; ", task.getException());
}else{
Log.d("firebase", String.valueOf(task.getResult().getValue()));
}
}
});

Can't get node of firebase children

Hi there i'm newest in swift. I am working with a firebase database with at 2 layer of hierarchy as well as many children for each node. I got 1st layer (descript, enddata and other), but i stll can't get the news node. Is in 3 to 5 random keys. I sow many issues but still not have issue for me.
I'm understand i'm doing some wrong but what?
The Firebase is:
i need retreat the news child
struct is
struct ICONews {
let ICOId: String
let news1: String
let news2: String
let news3: String
init?(ICOId: String, dict: [String: Any] ) {
self.ICOId=ICOId
guard let news1 = dict[""] as? String,
let news2 = dict[""] as? String,
let news3 = dict[""] as? String
else { return nil }
self.news1 = news1
self.news2 = news2
self.news3 = news3
}
}
struct NewsSnapShot {
let posts: [ICONews]
init?(with snapshot: DataSnapshot) {
var posts = [ICONews] ()
guard let snapDict = snapshot.value as? [String: [String: Any]] else { return nil }
for snap in snapDict {
guard let post = ICONews (ICOId: snap.key, dict: snap.value) else {continue}
posts.append(post)
}
self.posts=posts
}
}
class of DataBase
class DatabaseService {
static let shared = DatabaseService()
private init(){}
let ICOReference = Database.database().reference()
}
and retreat method
DatabaseService.shared.ICOReference.child("news").observe(DataEventType.value, with: { (snapshot) in
guard let postsSnapShot = ICOSnapShot(with: snapshot) else {return}
})
done
Database.database().reference().observeSingleEvent(of: .value, with: {(snapshot) in
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot {
let values = (rest as! DataSnapshot).value as? NSDictionary
let enumeratorMap1 = (rest as! DataSnapshot).children
while let rest2 = enumeratorMap1.nextObject() as? DataSnapshot {
let valuesMap1 = (rest2 as! DataSnapshot).value as? NSDictionary
if (rest2 as! DataSnapshot).key == "news" {
print(rest2.value)
}
}
}
})
Make the the Firebase Api call like
Database.database().reference().child("users").child(userID).observe(.childAdded, with: { (snapshot) in
if snapshot.exists() {
let receivedMessage = snapshot.value as! [String: Any]
let name = receivedMessage["name"] as? String ?? ""
let id = receivedMessage["id"] as? Double ?? 0.0
let profileurl = receivedMessage["url"] as? String ?? ""
completion(User(name: name, id: id, url: url))
} else {
failure()
}
})