Swift: How to Fix Infinite Loop When Adding a Value to a Firebase Variable

Swift: How to fix infinite loop when adding a value to a Firebase variable

Ivan's answer will solve the current problem you have. But loading all messages from the server to detect which one the user clicked sounds like a potentially big waste of bandwidth.

If you're showing a list of messages from Firebase to the user, I recommend keeping track of the key of each message. With that information, you won't have scan all messages, but can instead directly look up the message that was clicked and increase its score with a transaction:

rootRef.child("messages").child(postID).child("score").runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in
// Set value and report transaction success
currentData.value = currentData.value + 1
return FIRTransactionResult.successWithValue(currentData)
}) { (error, committed, snapshot) in
if let error = error {
print(error.localizedDescription)
}
}

A transaction will also solve the potential race condition you now have: if two users click the same message at almost the same time, one might be overwriting the score-increment of the other.

Swift closure not setting variable

.observeSingleEvent is working async.

You can do something like this:

func getRetname(completion: @escaping(_ retName: String) -> Void) {
if let uid = FIRAuth.auth()?.currentUser?.uid {
FIRDatabase.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
if let name = dictionary["name"] as? String {
ret_name = name
print(ret_name)
completion(ret_name)
}
}
})
}

Then, you can use it everywhere you want:

getRetname(completion: { ret_name in 
// ret_name - your data
})

Hope it helps

Wait until swift for loop with asynchronous network requests finishes executing

You can use dispatch groups to fire an asynchronous callback when all your requests finish.

Here's an example using dispatch groups to execute a callback asynchronously when multiple networking requests have all finished.

override func viewDidLoad() {
super.viewDidLoad()

let myGroup = DispatchGroup()

for i in 0 ..< 5 {
myGroup.enter()

Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
print("Finished request \(i)")
myGroup.leave()
}
}

myGroup.notify(queue: .main) {
print("Finished all requests.")
}
}

Output

Finished request 1
Finished request 0
Finished request 2
Finished request 3
Finished request 4
Finished all requests.

Core Motion Pedometer Save to Firebase

So you have global variable stepNumberVule and every time when your Save action gets called, you're saving the value of this variable to the Firebase, but the problem here is that you're not updating this value in the updateStepsCountLabelUsing(startDate: Date) or startCountingSteps(). If you want to save the exact number of steps, you should update the stepNumberVule from pedometer data.

E.g:

Create function

private func updateStepCounterValue(_ numberOfSteps: NSNumber) {
stepNumberVule = numberOfSteps.intValue
stepsCountLabel.text = numberOfSteps.stringValue
}

Then, call it to update your label and variable inside:

// Start counting steps
private func startCountingSteps() {
pedometer.startUpdates(from: Date()) {
[weak self] pedometerData, error in

guard let pedometerData = pedometerData, error == nil else { return }
DispatchQueue.main.async {
self?.updateStepCounterValue(pedometerData.numberOfSteps)
}
}
}

and

private func updateStepsCountLabelUsing(startDate: Date) {
pedometer.queryPedometerData(from: startDate, to: Date()) {
[weak self] pedometerData, error in

if let error = error {
self?.on(error: error)
} else if let pedometerData = pedometerData {
DispatchQueue.main.async {
self?.updateStepCounterValue(pedometerData.numberOfSteps)
}
}
}
}

In this way, your stepNumberVule will always be up to date with your current data from the pedometer.

Firebase & Swift: Asynchronous calls, Completion Handlers

Tomte's answer solved one of my problems (thank you!). The calculations will be carried out after userAranking and userBranking are received with this code:

while let nextMeme = enumerator.nextObject() as? DataSnapshot {
let group = DispatchGroup()

group.enter()
self.getRankingA(userA: userA, memeID: nextMeme.key) {
self.rankingAreceived = true
group.leave()
}

group.enter()
self.getRankingB(userB: userB, memeID: nextMeme.key) {
self.rankingBreceived = true
group.leave()
}

// is called when the last task left the group
group.notify(queue: .main) {
self.calculation()
}

}

Still, the completion call to updateScores would happen at the end of the loop but before all of the userArankings and userBrankings are received and before the rankings undergo calculations. I solved this problem by adding another dispatch group:

let downloadGroup = DispatchGroup()

while let nextMeme = enumerator.nextObject() as? DataSnapshot {
let calculationGroup = DispatchGroup()

downloadGroup.enter()
calculationGroup.enter()
self.getRankingA(userA: userA, memeID: nextMeme.key) {
downloadGroup.leave()
calculationGroup.leave()
}

downloadGroup.enter()
calculationGroup.enter()
self.getRankingB(userB: userB, memeID: nextMeme.key) {
downloadGroup.leave()
calculationGroup.leave()
}

// is called when the last task left the group
downloadGroup.enter()
calculationGroup.notify(queue: .main) {
self.calculation() {
downloadGroup.leave()
}
}

}

downloadGroup.notify(queue: .main) {
completion(self, userA, userB)
}

I had to add a completion handler to the calculation method as well to ensure that the updateScores method would be called once userAranking and userBranking undergo calculations, not just once they are received from the database.

Yay for dispatch groups!

Firebase datadescription returns empty array

The main issue here is that FireStore is asynchronous. It takes time for Firestore to return data from the internet and that data is only valid inside the closure following the getDocument.

That means the print(Groups) function will execute before the code inside the closure, so it's empty. Moving the print inside the closure will work.

var Groups = [String:Any]()
let docRef = AppDelegate.db.collection("JoinedGroups").document(user)
docRef.getDocument(source: .cache) { (document, error) in
if let document = document {
let dataDescription = document.data()
self.Groups = dataDescription! // unwrapping here
print("Cached document data: \(dataDescription)")
} else {
print("Document does not exist in cache")
}
print(self.Groups)
}
}

May I also suggest you stick with naming conventions where vars are lowercases, e.g. groups instead of Groups. Upper case are usually for class definitions UserClass or GroupClass.

One last thing... documentID's (their 'key') cannot be changed. What that means is if your structure is this

JoinedGroups
jimmy@email.com
name: "Jimmy"
fav_food: "Pizza"

and you reference that user throughout your app, when they decide to change their email provider, you'll have to go through everywhere in your strucutre, read in that node, delete the node and write it back out with the updated email. How you fix it is it disassociate the documentID (key) from the data it contains

JoinedGroups
users_uid
name: "Jimmy"
fav_food: "Pizza"

as uid's never change, the email can change 20 times and not affect your app.

Swift, Firebase. Check if a users, childs value exists

You are querying your root ref. You should query the /users node instead

let rootRef = FIRDatabase.database().reference()
let usersRef = rootRef.childByAppendingPath("users")
usersRef.queryOrderedBy....

You can shorten that up but I used the verbose model for clarity.

As a side note, with Firebase 3.x, the default is to only allow authenticated users to read and write. This is accomplished through Rules in the Realtime Database section.

If you want to test your code without authenticating, change your Rules to this

{
"rules": {
".read": true,
".write": true
}
}

PLEASE NOTE: This opens up your data to ANYONE that wants to read it, but if you are just learning or testing an app it makes it a bit more convenient.



Related Topics



Leave a reply



Submit