accessing a variable inside a closure from another function with swift - 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)
}

Related

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** {
....

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)
})

cant able to show all firebase data to line chart

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.

Retrieve values from within autoID node

I would like to get the values from within a autoID node. Below is my structure:
"workout"
"exercise"
"Bench Press"
"(AutoIDNode)"
"reps: 10"
"set: 1"
"weight: 60"
How can I get the values of "reps", "set" and "weight".
Here is my swift code:
refSets.child("workout").child("exercise").child("Bench Press").observe(DataEventType.value, with: {(snapshot) in
if snapshot.childrenCount > 0 {
self.setsData.removeAll()
for sets in snapshot.children {
let snap = sets as! DataSnapshot
let key = snap.key
let value = snap.value
print(key)
print(value!)
}
}
})
I am aware I am missing the .child() path for the autoID node following .child("Bench Press"). How can I access this node?
Thanks in advance
You can try
refSets.child("workout").child("exercise").child("Bench Press").observeSingleEvent(of: .value, with: {(snapshot) in
if let res = snapshot.value as? [String:[String:Int]] {
for item in Array(res.values) {
print(item["reps"])
}
}
})
What about that?
refSets
.child("workout")
.child("exercise")
.child("Bench Press").observe(.value, with: {(snapshot) in
guard let values = snapshot.value as [String: Any] else {
return
}
for (key, value) in values {
guard let objDict = value as? [String: Any],
let reps = objDict["reps"] as? Double /*not sure maybe Int*/,
let set = objDict["set"] as? Double,
let weight = objDict["weight"] as? Double else {
continue
}
let bp = BenchPress(reps: reps, set: set, weight: weight)
}
})
struct BenchPress {
private let reps: Double
private let set: Double
private let weight: Double
init(reps: Double, set: Double, weight: Double) {
self.reps = reps
self.set = set
self.weight = weight
}
}

How to get follower and following count quickly with Firebase and Swift

I am currently trying to fetch all the followers for a specific user with firebase. In my didSet clause, I call the function setFollowingCount() to fetch the users that the current user follows and assign it to a text field:
var user: User? {
didSet {
setFollowingCount()
guard let following = self.user?.following else {return}
let attributedText = NSMutableAttributedString(string: "\(following)\n", attributes: [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 14)])
attributedText.append(NSAttributedString(string: "followers", attributes: [NSAttributedStringKey.foregroundColor: UIColor.lightGray, NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)]))
self.followingLabel.attributedText = attributedText
}
}
The setFollowingCount() function is:
func setFollowingCount(){
var i = 0
guard let userId = self.user?.uid else { return }
Database.database().reference().child("following").child(userId).observe(.value) { (snapshot) in
self.user?.following = Int(snapshot.childrenCount)
}
}
The problem is that this takes very long to load and often freezes the entire app when you look at a user's profile. How can I speed this up or make it work more efficiently?
self.user?.following = Int(snapshot.childrenCount)
Is not an efficient solution. .childrenCount actually loops over the snapshot and counts all of the children which is going to be slow.
Instead you want to store the number of followers as a single value you can retrieve it faster.
following: {
uid: {
followingCount: 100,
follwersCount: 150
}
}
Then you can query like this:
Database.database().reference().child("following").child(userId).observeSingleEvent(of: .value) { (snapshot) in
if let counts = snap.value as? [String: AnyObject] }
let followingCount = counts["followingCount"] as? Int
let followersCount = counts["followersCount"] as? Int
// save these values somewhere
}
})
I would also recommend you increment / decrement the follower counts in a transaction block so the count doesn't get messed up. That can look something like this:
static func incrementCount(countName: String) {
if let uid = Auth.auth().currentUser?.uid {
let databaseReference = Database.database().reference()
databaseReference.child("following").child(uid).runTransactionBlock { (currentData: MutableData) -> TransactionResult in
if var data = currentData.value as? [String: Any] {
var count = data[countName] as! Int
count += 1
data[countName] = count
currentData.value = data
return TransactionResult.success(withValue: currentData)
}
return TransactionResult.success(withValue: currentData)
}
}
}
Lastly,
If you're going to use .observe you need to remove the reference. In this case though you aren't looking for updates so you can use .observeSingleEvent