I was trying to read the old value from firebase, update it, then save it back with the following code:
var unreadChatsRef = usersRef.childByAppendingPath("\(user.objectId!)/unreadChats/\(self.chat.objectId!)")
unreadChatsRef.observeSingleEventOfType(FEventType.Value, withBlock: { (snapshot) -> Void in
if snapshot.exists() {
var msgCnt = snapshot.value as? Int
msgCnt = (msgCnt ?? 0) + 1
unreadChatsRef.setValue(msgCnt)
}
})
Previously I made a mistake and used observeEventType instead of single event so by saving the value, it triggers itself to update the value again and again. I stopped my iOS program while seeing this in debug session. Somehow none of the subsequent updates made it to the firebase server. I had the local cache enabled by calling Firebase.defaultConfig().persistenceEnabled = true. So I end up with a cached value different from the value on the server.
What puzzles me is that even if my other iOS chat client updates that number, this iOS device (the code above) continues to see the cached value and can never fetch the correct value from the server. Am I doing something wrong? How do I tell my program to invalidate the cache and fetch a fresh copy from the server?
Related
I have been facing some issues with firebase persistence once enabled, I had a chance to read through the rest of posted questions and reviewed there answers but still haven't got things to work as expected.
I have enabled firebase persistence and using observe by value to fetch recent update of particular node. Not only it keeps fetching old values but also once I leave a particular view controller and go back to that view controller the value changes to recent one.
Is there a proper way to request for recent value at first call?
Code I have tried:
// MARK: Bill authenticate function
func authenticateBill(completion: #escaping (_ bill: Double?, _ billStatus: BillError?) -> Void) {
// Observe incase bill details exist for current case
let billRef = self.ref.child("bills").child((caseRef?.getCaseId())!)
billRef.observe(FIRDataEventType.value, with: { (billSnapshot) in
if !billSnapshot.exists() {
completion(nil, BillError.unavailable)
return
}
if let billDictionary = billSnapshot.value as? [String: AnyObject] {
let cost = billDictionary["cost"] as! Double
print("Cost: ", cost)
completion(cost, nil)
}
})
}
No, with persistence enabled, your first callback will contain the immediately-available value from the cache (if any), followed by updates from the server (if any). You can't indicate to the SDK that you want either cached or fresh data.
If you want to keep a particular location up to date all the time while persistence is enabled, you can use keepSynced to indicate to the SDK it should always be listening and caching the data at that location. That comes at an indeterminate cost in bandwidth, depending on how frequently the data changes.
You can use the REST API to request fresh data without going through any caching mechanisms.
I have a situation in which my Firebase reference is not invoking the callback for observeSingleEvent.
let uid = FIRAuth.auth()?.currentUser?.uid
FIRDatabase.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { (snapShot) in
//code here not called
}) { (error) in
//code here not called either
}
In my case it appears the uid property which is coming from FIRAuth.auth()?.currentUser?.uid is somehow being cached from a previous app installation when I'm switching between Firebase environments (building with different dev and staging .plist files.)
However, if I just add a random non existing string in place of uid the callback occurs as expected with an empty snapshot for a missing reference node.
I believe it's relevant to mention that I'm using synced Firebase references with persistanceEnabled = true.
I'm in a situation where I'm switching between environments and my app is hanging because promises are not being fulfilled or rejected because the results of by observeSingleEvent method call are never completing.
I have a simple iOS app that syncs 1,000 Contacts with my Firebase. persistenceEnabled is set to true in my AppDelegate.
In my UITableViewController I observe the ChildAdded event, download the Contacts and this works fine.
The problem is this - the next time I open the app, navigate back to the UITableViewController, the ChildAdded event fires again and loads the 1,000 Contacts.
What I thought would happen is that on subsequent launches the ChildAdded event would not fire since persistenceEnabled is true and no new Children have been added.
Have I misunderstood how Firebase works ?
ref.child("contacts").observeEventType(.ChildAdded, withBlock: { snapshot in
if let json = snapshot.value as? NSDictionary {
for key in json.keyEnumerator() {
if let dict = json.valueForKey(key as! String) as? NSDictionary {
let contact = Contact(data: dict)
contacts.append(contact)
}
}
}
})
From the Firebase documentation on .ChildAdded:
The FIRDataEventTypeChildAdded event is triggered once for each existing child and then again every time a new child is added to the specified path.
Using persistence doesn't make a difference to this behavior. It just ensure that the event will also work in this way if the app is started while you don't have network connectivity.
Since You are registering observeEventType not observeSingleEventType. So register event only once in viewDidLoad. For the first time you will get all the data available in that node. next time onwards it will give you only the new child added. In case if you want to query firebase every time then register observeSingleEventType but again it will return all the data available in that node and will remove observer as it is observeSingleEventType.
use this method :
Database.database().reference().child("anything").observeSingleEvent(of: .value, with: { snapshot in ...
this will fetch data once ..
I'm using firebase with app started in offline mode, when i'm subscribing to child values of some node the callback from observe*(_:,withBlock:) is not firing (neither for initial values nor changes). Subscriptions to direct values (childless) works fine. Take a look at a snippet :
let database = FIRDatabase.database()
database.reference().keepSynced(true)
let databaseRef = database.reference()
database.goOffline()
databaseRef.child("user").setValue("user1")
let userKey = databaseRef.child("usr").childByAutoId().key
let userValues = ["uid": "uid",
"name" : "name",
"surname" : "surname"]
databaseRef.child("/usr/\(userKey)/").setValue(userValues)
//1
databaseRef.child("user").observeSingleEventOfType(.Value, withBlock:{ snap in
print("works")
})
//2
databaseRef.child("usr").observeSingleEventOfType(.Value, withBlock:{ snap in
print("doesnt work")
})
//3
databaseRef.child("usr/\(userKey)/uid").observeSingleEventOfType(.Value, withBlock:{ snap in
print("works")
})`
subscriptions 1 & 2 works fine, but subscribtion 2 won't fire, until at least once database go online. From the moment database syncronize with remote i can go offline and everything works as it should. Anyone know how to handle this issue?
When your app is offline, the Firebase client will fire events from its cache. If your app has never connected to the Firebase servers, this cache will be empty.
That means that the Firebase client has no knowledge on whether a value exists at the location you request. For that reason it will not fire an event.
I had a similar issue but I was using deep links with updateChildValues, which somehow caused the local cache to not fire events on intermediate (/path/intermediate/otherpath) keys. The workaround I found was to be more verbose in the dictionary I passed to updateChildValues. (I still believe this is a bug in the Firebase SDK).
See this Stack Overflow question
my firebase data structure looks like the following
user
|__{user_id}
|__userMatch
|__{userMatchId}
|__createdAt: <UNIX time in milliseconds>
I'm trying to listen for the child added event under userMatch since a particular given time. Here's my swift code:
func listenForNewUserMatches(since: NSDate) -> UInt? {
NSLog("listenForNewUserMatches since: \(since)")
var handle:UInt?
let userMatchRef = usersRef.childByAppendingPath("\(user.objectId!)/userMatch")
var query = userMatchRef.queryOrderedByChild("createdAt");
query = query.queryStartingAtValue(since.timeIntervalSince1970 * 1000)
handle = query.observeEventType(FEventType.ChildAdded, withBlock: { snapshot in
let userMatchId = snapshot.key
NSLog("New firebase UserMatch created \(userMatchId)")
}, withCancelBlock: { error in
NSLog("Error listening for new userMatches: \(error)")
})
return handle
}
What's happening is that the event call back is called only once. Subsequent data insertion under userMatch didn't trigger the call. Sort of behaves like observeSingleEventOfType
I have the following data inserted into firebase under user/{some-id}/userMatch:
QGgmQnDLUB
createdAt: 1448934387867
bMfJH1bzNs
createdAt: 1448934354943
Here are the logs:
2015-11-30 17:32:38.632 listenForNewUserMatches since:2015-12-01 01:32:37 +0000
2015-11-30 17:45:55.163 New firebase UserMatch created bMfJH1bzNs
The call back was fired for bMfJH1bzNs but not for QGgmQnDLUB which was added at a later time. It's very consistent: after opening the app, it only fires for the first event. Not sure what I'm doing wrong here.
Update: Actually the behavior is not very consistent. Sometimes the call back is not fired at all, not even once. But since I persist the since time I should use when calling listenForNewUserMatches function. If I kill the app and restart the app, the callback will get fired (listenForNewUserMatches is called upon app start), for the childAdded event before I killed the app. This happens very consistently (callback always called upon kill-restart the app for events that happened prior to killing the app).
Update 2: Don't know why, but if I add queryLimitedToLast to the query, it works all the time now. I mean, by changing userMatchRef.queryOrderedByChild("createdAt") to userMatchRef.queryOrderedByChild("createdAt").queryLimitedToLast(10), it's working now. 10 is just an arbitrary number I chose.
I think the issue comes from the nature of time based data.
You created a query that says: "Get me all the matches that happened after now." This should work when the app is running and new data comes in like bMfJH1bzNs. But older data like QGgmQnDLUB won't show up.
Then when you run again, the since.timeIntervalSince1970 has changed to a later date. Now neither of the objects before will show up in your query.
When you changed your query to use queryLimitedToLast you avoided this issue because you're no longer querying based on time. Now your query says: "Get me the last ten children at this location."
As long as there is data at that location you'll always receive data in the callback.
So you either need to ensure that since.timeIntervalSince1970 is always earlier than the data you expect to come back, or use queryLimitedToLast.