iOS firebase observer delays observing the changes in the node - ios

I have one function to get the realtime location updates of the selected user.
And in that function , i need to call the current status and history of that selected user from the firebase.
So, the problem is when I start my observer for location updates only , it's fine and it's great .
I get the value from firebase observer.
But , when i make a call for current status and history of that selected user , I am getting the delay in my firebase realtime observer .
I don't understand how come call to another firebase is affecting our realtime observer code.
Guys any suggestions,
I tried dispatching to background for current status /history call but still no luck.
Left me hopeless and clueless.
Added:
I am getting current status and realtime observers from the same node.
So , does this happens that same node is observing and same time if another firebase call is made to same node , is it colliding ???
UPDATED
Firebase function to get current status :
FIRDatabaseReference().child("personal").child(userUUID).child("realtime")
.observeSingleEvent(of: .value, with: { (snapshot) in
//print(snapshot.key)
if snapshot.exists(){
let location = LocationParser(dict: snapshot.value as! [String : AnyObject])?.location
completion(location)
}else{
completion(nil)
}
}){ (error) in
completion(nil)
debugPrint(error.localizedDescription)
}
Firebase function to observe location (continuously):
FIRDatabaseReference().child("personal").child(userUUID).child("realtime")
.observe(.value) { (snapshot:FIRDataSnapshot) in
print("\nOBSERVER FROM FIREBASE FIRED")
if let value = snapshot.value as? [String:AnyObject] {
if let location = LocationParser(dict: value)?.location {
//we cannot save location to our database
//sharedFirebaseManager.saveLocationHistoric(LocationParser(dict: value)!)
result(success(location))
}
}
}
QUESTION:
I am calling both from the same controller and I see continuous observer delays on calling another firebase function call.
Any clue.

Related

Why does my realm notification token only trigger on my active device and not all devices with the open realm?

I'm newly learning iOS/swift programming and developing an application using MongoDB Realm and using Realm sync. I'm new to programming and realm, so please feel free to correct any terminology. My question is about listening for realm notifications, which I see referred to as change listeners and notification tokens. Regardless, here is the info:
My application has a list of locations with a status (confirmed/pending/cancelled). I open this list from my realm as a realm managed object and create my notification handler:
//This is called in it's own function, but assigns the locations
locations = publicRealm?.objects(Location.self)
//This is run after the function is called
self?.notificationToken = self?.locations!.observe { [weak self] (_) in
self?.tableView.reloadData()
print("Notification Token!!!")
I then populate my table view and let a user tap on a location, which passes the location and realm to another view controller where the user can update the status. That update is made in a separate view controller.
do{
try publicRealm?.write {
selectedLocation?.statusMessage = locationStatusTextField.text!
selectedLocation?.status = selectedStatus
}
}catch{
print("Error saving location data: \(error)")
}
At this point my notification token is successfully triggered on the device where I am making the location update. The change is shown immediately. However there is no notification token or realm refresh that happens on any other open devices that are showing the locations table view. They do not respond to the change, and will only respond to it if I force realm.refresh(). The change is showing in Atlas on MongoDB server, though.
I am testing on multiple simulators and my own personal phone as well, all in Xcode.
I'm very confused how my notification token can trigger on one device but not another.
When I first started the project it was a much simpler realm model and I could run two devices in simulator and updating one would immediately fire a change notification and cause the second device to show the correct notification.
I have since updated to a newer realm version and also made the realm model more complicated. Though for this purpose I am trying to keep it simple by doing all changes via swift and in one realm.
I also have realm custom user functions running and changing data but I think reading the docs I am realizing that those will not trigger a notification - I'm not sure if that's true though? I just know right now that if I change data in the DB via a user function no notifications are triggered anywhere - but if I do realm.refresh() then the change shows.
What is it that I am missing in how I am using these notifications?
***Updating information on Public Realm:
Save the realm:
var publicRealm:Realm?
Login as an anon user and then open the realm:
let configuration = user.configuration(partitionValue: "PUBLIC")
Realm.asyncOpen(configuration: configuration) { [weak self](result) in
DispatchQueue.main.async {
switch result {
case .failure(let error):
fatalError("Failed to open realm: \(error)")
case .success(let publicRealm):
self!.publicRealm = publicRealm
guard let syncConfiguration = self?.publicRealm?.configuration.syncConfiguration else {
fatalError("Sync configuration not found! Realm not opened with sync?")
}
It is after this realm opening that the locations are loaded and notification token is created.
I use a segue to pass the location object and realm to the next VC:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! UpdateLocationViewController
destinationVC.delegate = self
if let indexPath = tableView.indexPathForSelectedRow {
destinationVC.selectedLocation = locations?[indexPath.row]
}
if indexPathRow != nil {
destinationVC.selectedLocation = locations?[indexPathRow!]
}
destinationVC.publicRealm = self.publicRealm
}
Some notes:
original public realm opened by anon user
only a logged in user can click on a location... so in the 'UpdateLocation' VC that gets passed the public realm I am a logged in user. But, I'm just using the Dev mode of Realm to allow me to read/write however I like... and I am writing straight to that public realm to try and keep it simple. (I have a custom user function that writes to both public and the user's org realm, but I stopped trying to use the function for now)
I identify the object to update based on the passed in location object from the first VC
I needed to use try! when making my write call rather than just try. I updated my write blocks as such:
try! publicRealm?.write {
selectedLocation?.statusMessage = locationStatusTextField.text!
selectedLocation?.status = selectedStatus
}
Just wanted to follow up in case anyone finds this. Thank you!

How to use observeSingleEvent in Firebase with callback when not connected?

I am building iOS App using Firebase, I found that the method observeSingleEvent(with or without cancel block) in Firebase would not even fire if the app is not connected to the network and there is no cached value for the location.
I need to show messages to users when the App lose connection to internet but no need to do it if there is cached value for the location when use keepSynced to it.
How can I do if the API without an error that can detect connect or not in return? Since the document says the cancelBlock will be called if you don't have permission to access this data, but it even not callback when the App without connection.
if and else statetment check your problem ? If it is connected, you will be call the observeSingleEvent function
let connectedRef = Database.database().reference(withPath: ".info/connected")
connectedRef.observe(.value, with: { snapshot in
if snapshot.value as? Bool ?? false {
print("Connected")
} else {
print("Not connected")
}
})
More detail : Detecting Connection State

Swift Firebase removal of observers not removing

I'm using Firebase listener to update state values for a remote camera. Once I have cycled through the camera lifecycle I want to remove the listeners so my camera does not start over and continue to take video.
Here is what I've done so far based on SO suggestions:
1) added FIRDatabaseHandle and called removeObserver(withHandle: handle) / no luck
2) simple called removeAllObservers() from the root reference to what you see below.
struct CameraActions {
let db = DataService.ds.db // this comes from a singleton used to for other Firebase calls
let uid = DataService.ds.curUser?.uid
var cameraRef:FIRDatabaseReference!
mutating func addCameraListener(cameraNum num:String, complete:#escaping(CameraStatus)->Void){
cameraRef = db.child("camera").child(num).child("status")
cameraRef.observe(.value, with: {
snap in
if let status = snap.value as? Int {
switch status {
case 0: complete(.ready)
case 2: complete(.isRecording)
case 4: complete(.hasStopped)
case 5: complete(.problem)
default: print("App is waiting on camera")
}
}
})
}
func cameraHasFinishedRecording(cameraNum num: String) {
cameraRef.removeAllObservers() // latest attempt here
db.child("camera").child(num).child("status").setValue(0) // this still triggers database call
}
Thanks in advance for any assistance.
Firebase works exactly as advertised. The removal of the observer was working but, another observer that should've been a single observer was firing. Thanks for the input and sorry for wasting your time.
Cheers!

iOS - How to disable the Firebase caching?

I made delete on post object in firebase node.
But when I query for post objects , previously cached post objects are shown.
I am not getting refreshed data from firebase.
Question:
Is there a way to stop firebase caching and get the refreshed data from firebase in iOS?
MY Code:
self.firPostRef.child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.value is NSNull { completion(nil) }
else {
//print(snapshot.debugDescription)
let postids = Array((snapshot.value as! [String:AnyObject]).keys)
print(postids)
completion(postids)
}
}) { (error) in
debugPrint("error occured in getting all comments -" + error.localizedDescription)
}
use observeSingleEventOfType, reference.keepSynced(true). It refreshes data once this code is executed.
Use observeEventOfType. This will always keep your data in sync and will execute again as soon as you made changes.
Solution :
Just disable the firebase caching using the following code:
FIRDatabase.database().reference().keepSynced(false)
Ta da ..

Firebase Connection manager should return only one result

I am following documentation located at: https://www.firebase.com/docs/ios/guide/offline-capabilities.html#section-connection-state
However, my implementation and test of:
connectionCheck.observeEventType(.Value, withBlock: { snapshot in
let connected = snapshot.value as? Bool
if connected != nil && connected! {
print("Connected")
} else {
print("Not connected")
}
})
The output in Xcode notes:
Not connected
Connected
If however I turn off the wifi, the result is simply:
Not connected
Given I wish to allow actions to occur and present to the user if there is not a connection, how can I ensure that this Firebase listener only returns the correct response once?
As a suggestion, you may want to add some additional functionality;
If you want to know about the users connection to Firebase, you should observe the .info/connected path, as stated in the docs.
The design pattern I use is to set up a global app variable called isConnected, and attach a Firebase observer to the .info/connected path.
Then in my app wherever I need to know if the user is connected or not, I attach a KVO Observer to my global isConnected var.
When the user disconnects (or connects), the Firebase observer is called which sets isConnected = false (or true).
Then, any places in my app that are observing the isConnected var is notified of the disconnect/connect and can take appropriate action.
Here's the code
let connectedRef = rootRef.childByAppendingPath(".info/connected")
connectedRef.observeEventType(.Value, withBlock: { snapshot in
self.isConnected = snapshot.value as! Bool //KVO property
//this is just so you can see it's being set, remove
if ( self.isConnected == true ) {
print("connected")
} else {
print("not connected")
}
// testing code
})

Resources