Can't Save Snapshot to Data Model? - ios

In my viewController file, I initially had the following code:
self.rootRef.child("users/").child("stringKey").observeEventType(.Value, withBlock: { snapshot in
self.userA.name = snapshot.value!.objectForKey("name") as? String
self.userA.urlOne = snapshot.value!.objectForKey("urlOne") as? String
self.userA.urlTwo = snapshot.value!.objectForKey("urlTwo") as? String
})
print(self.userA.name)
print(self.userA.urlOne)
print(self.userA.urlTwo)
But for reasons unknown to me, the output is empty. When I put the print statements inside the observer event, I get the desired output. Why aren't I allowed to save the snapshot to a data model?

[observeEventType] is processed in the background and your print commands are on the main thread.
you need to do the saving in the background thread or use a method with a completion handler

Related

Firebase setValue method is taking time

I am using firebase in my iOs app.
When I use the setValue method for updating node value, it takes around 5-10 secs to update value.
How can I overcome this delay?
Code
let shiftReportsRef = Database.database().reference().child(Constants.FireBase().users).child(Constants.FireBase().shiftReports).child(firebaseToken).child(cashierId.stringValue).child(todayDate).child(posPointId.stringValue).child(shiftid.stringValue)
shiftReportsRef?.observe(.value, with: { (peakHoursSnapShot) in
self.shiftDetails = peakHoursSnapShot.value as? [String: AnyObject] ?? [:]
let lastUpdated = Date().gmtDateTime(.dateTimeSec)
self.shiftDetails!["lastUpdated"] = lastUpdated as AnyObject
if self.hasUpdatedValues == false
{
self.updatePeakHoursAndLastUpdateDate(amount, refndedAmount, pymtType, timeSlt)
}
self.hasUpdatedValues = true
})
The code in the question is a little unclear as to what the expected outcome is. Let's walk through it...
First shiftReportRef is defined
let shiftReportsRef =
Database.database().reference().child(Constants.FireBase().users).child(Constants.FireBase().shiftReports).child(firebaseToken).child(cashierId.stringValue).child(todayDate).child(posPointId.stringValue).child(shiftid.stringValue)
then an an observer is added to that ref. The observer is .value which will observe any changes to that node and return them in a snapshot via an event
shiftReportsRef?.observe(.value
But then when the first event occurs and the code within the closure executes, the data at that location is overwritten
shiftReportsRef?.setValue(self.shiftDetails)
which then causes another .value event to fire, therefore overwriting the node again (I think you can see where this is going). Over and over.
So this isn't exactly an answer because we don't know the expected outcome but that's probably why there are delays in the app.

Swift Firebase changing value is unstable

I'm trying to change the value in Firebase, using TableView.
And Here is the Swift code in didSelectRowAt.
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
let nameDB = child.childSnapshot(forPath: "contactName").value! as! String
if nameDB == receiverName {
let autoID = child.key as String
if receiverAmount.characters.contains("-") {
self.dbRef.child("user/contacts/\(autoID)/borrowAmount").setValue("")
self.dbRef.child("user/contacts/\(autoID)/borrowDueDate").setValue("")
}
else {
self.dbRef.child("user/contacts/\(autoID)/lendAmount").setValue("")
self.dbRef.child("user/contacts/\(autoID)/lendDueDate").setValue("2099-12-31")
}
But While my app is, Values are unstable.
Original value and new value are endlessly changed, and then my app is stopped.
Any idea for solving this problem...?
Thank you!!
1) As you are using a tableView I am sure you are having al values required even if you do not use for loop ? instead using for loop on didselectAction just perform value update directly by creating a reference not using for loop.
2)Due to for loop Condition you are getting unstable outputs So, Just avoid for loop here and Directly update values

Issues reading data from Firebase Database

Okay I am reading from a database and when I print the individual variables they print out correctly. However it seems like the data refuses to append to the array. Anyone know why? I can't figure it out at all.
let commuteBuilder = Commutes()
Database.database().reference().child("Users").child(user).child("Trips").observe(DataEventType.childAdded, with: { (snapshot) in
//print(snapshot)
if let dict = snapshot.value as? NSDictionary {
commuteBuilder.distance = dict["Distance"] as! Double
commuteBuilder.title = dict["TripName"] as! String
commuteBuilder.transportType = (dict["Transport"] as? String)!
}
commuteArray.append(commuteBuilder)
})
print("helper")
print(commuteArray.count)
return commuteArray
The data is correctly added to the array, just not at the time that you print the array's contents.
If you change the code like this, you can see this:
let commuteBuilder = Commutes()
Database.database().reference().child("Users").child(user).child("Trips").observe(DataEventType.childAdded, with: { (snapshot) in
if let dict = snapshot.value as? NSDictionary {
commuteBuilder.distance = dict["Distance"] as! Double
commuteBuilder.title = dict["TripName"] as! String
commuteBuilder.transportType = (dict["Transport"] as? String)!
}
commuteArray.append(commuteBuilder)
print("added one, now have \(commuteArray.count)")
})
print("returning \(commuteArray.count)")
return commuteArray
You'll see it print something like this:
returning 0
added one, now have 1
added one, now have 2
etc.
This is likely not the output you expected. But it is working as intended. Firebase loads data from its database asynchronously. Instead of blocking your code, it lets the thread continue (so the user can continue using the app) and instead calls back to the code block you passed to observe when new data is available.
This means that by the time this code returns the array it is still empty, but it later adds items as they come in. This means that you cannot return data from a function in the way you are trying.
I find it easiest to change my way of thinking about code. Instead of "First get the data, then print it", I frame it as "Start getting the data. When data comes back, print it".
In the code above, I did this by moving the code that prints the count into the callback block. Instead of doing this, you can also create your own callback, which is called a completion handler or closure in Swift. You can find examples in this article, this article, this question Callback function syntax in Swift or of course in Apple's documentation.

Data from Firebase not loading into array

I don't have a storyboard. I'm doing everything programmatically.
The loadData() method takes Firebase data, put it into a Company object, and loads the object into the companies array. In the didFinishLaunchingWithOptions method in the App Delegate, I instantiated the class and called loadData()
When I run breakpoint at the line indicated by the comment and type "po companies" in the console, I get 0 companies. The print statements inside .observe are printed to the console and I can see that the company's properties are non-null, but anything outside .observe, including the for loop and the print statement called after the load data method in the App Delegate are not printed.
class informationStateController {
func loadData() {
//Set firebase database reference
ref = FIRDatabase.database().reference()
//Retrieve posts and listen for changes
databaseHandle = ref?.child("companies").observe(.childAdded, with: { (snapshot) in
//Code that executes when child is added
let company = Company()
company.name = snapshot.childSnapshot(forPath: "name").value as! String
print(company.name)
company.location = snapshot.childSnapshot(forPath: "location").value as! String
print(company.location)
self.companies.append(company)
print("databaseHandle was called")
})
for company in companies {
print(company)
}
//breakpoint inserted here
}
}
Why is my array empty and why are print statements outside .observe NOT printing to the console? The output for the console is set to "All Output". I called import FirebaseDatabase in the class and import Firebase in the App Delegate.
Data is loaded from the Firebase Database asynchronously. This means that by the time you print the companies, they won't have loaded yet.
You can easily see this by also printing the companies as they're loaded:
//Set firebase database reference
ref = FIRDatabase.database().reference()
//Retrieve posts and listen for changes
databaseHandle = ref?.child("companies").observe(.childAdded, with: { (snapshot) in
//Code that executes when child is added
let company = Company()
company.name = snapshot.childSnapshot(forPath: "name").value as! String
print(company.name)
company.location = snapshot.childSnapshot(forPath: "location").value as! String
print(company.location)
self.companies.append(company)
print("databaseHandle was called")
for company in companies {
print(company)
}
})
Now you'll first see one company printed (when childAdded fires for the first time), then two companies (when childAdded fires again), then three companies, etc.
Per the docs (emphasis mine)
Important: The FIRDataEventTypeValue event is fired every time data is changed at the specified database reference, including changes to children. To limit the size of your snapshots, attach only at the highest level needed for watching changes. For example, attaching a listener to the root of your database is not recommended.
In your case, you're observing changes to the database, but no changes are happening, so you won't bet getting new data. I think the docs make this unnecessarily confusing, if you want to pull records that already exist, you have to query for it:
https://firebase.google.com/docs/database/ios/lists-of-data#sort_data
// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
let recentPostsQuery = (ref?.child("companies").queryLimited(toFirst: 100))!
Once you have that queried data, you can then deal with the observer and append data as required when new data is pushed.
All of this aside, Frank's answer is the reason you'll never see the print when a company is added even if you set the listener up right — you need to write that inside the completion block of the observer or query.

Receiving Data using Firebase swift

I am trying to retrieve Childs from The Firebase Database using this code (which is declared in viewdidload)
let rootRef = FIRDatabase.database().reference().child("UsersInfo").child((FIRAuth.auth()?.currentUser?.uid)!)
rootRef.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
let hello = snapshot.value as! [String : AnyObject]
let usernamerecieved = hello["Username"] as! String
let Emailrecieved = hello["Email"] as! String
let bloodtyperecieved = hello["BloodType"] as! String
globalusername = usernamerecieved (EDITED)
})
and I've declared a global variable as such
var globalusername = "user"
Im trying to extract the usernamerecieved variable and casting it on a Global Variable. However when I print the global variable later on in the viewdidLoad I still get the initial value of the globalusername which is "user".
In this line of code : usernamerecieved = globalusername what you are doing in this line is that you are setting the value of the usernamerecieved to the value of globalusername i.e. 'user'
It should be globalusername = usernamerecieved.
Also sometimes it takes time to retrieve data from the server reasons could be numerous - slow network connection, heavy data retrieving such as images etc. but before it could be completed (Retrieving Action From The Server) it might be possible that line in which you are printing the globalusername gets called first , even before the completionBlock off the observeEventType is even completed (since its asynchronous , somewhat similar to dispatch_async).To tackle that you should call the printing call for globalusername inside the completionBlock or observe any change in it's value by using addObserver to the globalusername,which will let you know whenever the value of globalusername is changed or updated so that you can act accordingly .
observeEventType's block is asynchronous, it is possible that by the time you read the globalusername var, the block has not been completed yet.
Try printing globalusername inside the block and see the value

Resources