cant able to show all firebase data to line chart - swift

I am trying to show my firebase data into my line chart, but it only showing one data from database and I want to show all the data from database. I am just new at this so if anyone can help me with this it would be great.
let ref = Database.database().reference().child("WeightTracker")
ref.child("\(currentUser)").queryOrderedByKey().observeSingleEvent(of: .value) { (snapshot) in
for rest in snapshot.children.allObjects as! [DataSnapshot] {
guard let restDict = rest.value as? [String: Any] else { continue }
let weight = restDict["weight"] as? String
print(weight as Any)
let date = restDict["date"] as? String
print(date as Any)
var xAxisValues = [""]
var yAxisValues = [0.0]
let total = Int(Double(weight!)!) * Int(2.20)
xAxisValues.append(date!)
yAxisValues.append(Double(total))
let formatter = WTRandomVC(lineChart: self.lineChartsView, xArray: xAxisValues , yArray: yAxisValues)
self.lineChartsView?.data?.setValueFormatter(formatter)
continue
}
}

Your code loops over the results that you get from the database, and then creates a new formatter with that data for each individual item and sets it to the chart. That means that the chart will only show the last result from the database once the code is done. To show all results, you'll need to gather the data into a single formatter and then show that on the chat.
The code should look something like this:
let ref = Database.database().reference().child("WeightTracker")
ref.child("\(currentUser)").queryOrderedByKey().observeSingleEvent(of: .value) { (snapshot) in
var xAxisValues = [""]
var yAxisValues = [0.0]
for rest in snapshot.children.allObjects as! [DataSnapshot] {
guard let restDict = rest.value as? [String: Any] else { continue }
let weight = restDict["weight"] as? String
let date = restDict["date"] as? String
let total = Int(Double(weight!)!) * Int(2.20)
xAxisValues.append(date!)
yAxisValues.append(Double(total))
}
let formatter = WTRandomVC(lineChart: self.lineChartsView, xArray: xAxisValues , yArray: yAxisValues)
self.lineChartsView?.data?.setValueFormatter(formatter)
}
So in the above we've pulled the arrays with data, and the creation of a WTRandomVC, outside of the for loop, so that they only happen once for all of the data.

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 to read all child data from firebase

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.

What is the optimal way to incorporate a filter snapshot inside another firebase snapshot?

I am currently incorporating a filter Z inside my snapshot, however it duplicates a [DataSnapshot] line. Below is how I am currently doing it with the filter Z part in bold (**). It would be ideal to move the filter Z out of the initial snapshot A onto a different file, but I need access to in order to run the if countb >= 1 line in the initial snapshot A. Can that be done, and if so, how?
let thisUsersUid = Auth.auth().currentUser?.uid
refArtists = Database.database().reference().child("people");
refArtists.observe(DataEventType.value, with: { [weak self] snapshot in
guard let self = self else { return }
if snapshot.childrenCount>0{
self.people.removeAll()
for people in snapshot.children.allObjects as! [DataSnapshot] {
/////SNAPSHOT A
if people.key != thisUsersUid {
print("peoplekey",people.key)
let peopleObject = people.value as? [String: AnyObject]
let peopleEducation = peopleObject?["Education"] as? String
_ = peopleObject?["caption"] as? Int
///Filter Z
**let coordSnap1 = people.childSnapshot(forPath: "peopleWhoLike2")
let peopleArray = coordSnap1.children.allObjects as! [DataSnapshot]
let filteredResults = peopleArray.filter { person in
let personUid = person.value as! Int
let coordSnap120 = personUid
print("kjk", coordSnap120 )
//let secondsInDay = 86400
let date1 = Date(timeIntervalSince1970: TimeInterval(coordSnap120)/1000.0)
return Calendar.current.isDateInToday(date1)
}**
////END FILTER Z
print(filteredResults, "ppp")
let countb = filteredResults.compactMap({$0}).count
print(countb, "bbb")
**if countb >= 1** {
....

Order tableviewcell by child timestamp

I tried with this code to sort my posts by timestamp it doesn't work, each time I launch the simulator the order of the cells is different, I suppose this isn't the way to do it, could somebody explain me where I am wrong...
I edited the code, now my problem is that the most recent posts are displayed at the bottom and I would like them to to be displayed at the top
self.user.removeAll()
for child in DataSnapshot.children.allObjects as! [DataSnapshot] {
print("Processing user \(child.key)")
let value = child.value as? NSDictionary
//if country == "UNITED STATES"{
if let uid = value?["userID"] as? String{
if uid != Auth.auth().currentUser!.uid {
//
let userToShow = User()
if let fullName = value?["username"] as? String , let imagePath = value?["photoURL"] as? String{
userToShow.username = fullName
userToShow.imagePath = imagePath
userToShow.userID = uid
self.user.append(userToShow)
}
}
}
}
As soon as you call DataSnapshot.value, you're converting the data in the snapshot into a dictionary. And the order if keys in that dictionary is not guaranteed.
To maintain the order of the elements as they come back from the database, you need to loop over DataSnapshot.children. See these questions for examples of how to do that:
Iterate over snapshot children in Firebase
post on the firebase-talk mailing list
For your code this would look something like:
ref.child("users").queryOrdered(byChild: "timestamp").observeSingleEvent(of: .value, with: { snapshot in
self.user.removeAll()
for child in snapshot.children.allObjects as [DataSnapshot] {
print("Processing user \(child.key)")
let value = child.value as? NSDictionary
if let uid = value["userID"] as? String {
...
}
}
self.tableview.reloadData()
})

accessing a variable inside a closure from another function with swift

I am trying to create an app that lists certain services where users can rate each of the services. The rating from each server is saved in firebase database. I am able to retrieve all the ratings and calculate averages using the following function:
func observeStars() {
let placesRef = Database.database().reference().child("Review")
placesRef.observe(.childAdded, with: { (snapshot) in
let toId = snapshot.key
var total: Double = 0.0
var average: Double = 0.0
placesRef.child(toId).observeSingleEvent(of: .value, with: { (snapshot) in
let count = snapshot.childrenCount
for childOfPlace in snapshot.children {
let childOfPlaceSnap = childOfPlace as! DataSnapshot
let dict = childOfPlaceSnap.value as! [String: Any]
let val = dict["STAR"] as! Double
total += val
}
average = total/Double(count)
print(average)
})
}, withCancel: nil)
}
As you can see, the average is printed within 2 consecutive closures inside the function. However, I am having trouble using these average values in another function in a separate swift file, so I can use it to show the average ratings in each tableViewcell representing each service. My big question is how do I bring the calculated average value outside of the observeStars() function and use it somewhere else? Any input would be very much appreciated!
var average: Double = 0.0
func observeStars() {
let placesRef = Database.database().reference().child("Review")
placesRef.observe(.childAdded, with: { (snapshot) in
let toId = snapshot.key
var total: Double = 0.0
placesRef.child(toId).observeSingleEvent(of: .value, with: { (snapshot) in
let count = snapshot.childrenCount
for childOfPlace in snapshot.children {
let childOfPlaceSnap = childOfPlace as! DataSnapshot
let dict = childOfPlaceSnap.value as! [String: Any]
let val = dict["STAR"] as! Double
total += val
}
self.average = total/Double(count)
print(average)
})
}, withCancel: nil)
}