I am trying to check for errors while the app is trying to connect to the firebase-realtime-database.
My main concern is when the user fires up the app for the first time, but with no internet connection. I create the ref to the database and try to observe a single event, but the console log starts to throw error messages and none of them is captured by my code.
//------- variable declaration
var ref:DatabaseReference?
//------- inside de method
ref = Database.database().reference(withPath: "myPath")
ref?.observeSingleEvent(of: .value, with: { (snapshot) in
//I cannot even fire up the observer, so I never get here
}){(error) in
//no error is captured here also
}
Even if I comment or delete the observeSingleEvent I can see the errors getting thrown in the console, but I would like to capture them.
Does anyone know how to do that?
Thanks in advance
It is not an "error" to be offline at the time of a query. The SDK will continue trying to make the request for as along as your app is running and your code has an observer attached at that location.
If you want to detect the connection state, you can attach an observer at /.info/connected to get a callback that indicates the current state of the connection. Note that this information might be out of date, as a stalled connection doesn't immediately become fully disconnected until after a while.
Related
I've setup a listener on my database that is only failing to respond in a weird offline scenario.
On setup I'm adding a listener to a database that should fire anytime there's an update...
let mySpots = self.databaseRefSpots.child("users").child(globalMasterUser.userUID).child("type").observe(.value, with: { snapshot in
//code that should be executed when there's an update.
Once the user takes some action that should get saved, it's saved to the firebase database using...
annotationdb.child("type").setValue("Feature")
annotationdb.child("properties").setValue(propDict)
annotationdb.child("geometry").setValue(["type":"Point"])
annotationdb.child("geometry").child("coordinates").setValue(coordinates)
This code works whenever the user is online, and when a user starts the app online and goes offline.
But, if the listener is created when the app is initialized in an offline state, it looks like the listener is setup successfully, but after my setValue's are called, the listener isn't called. If I turn the data connection on, at that point my listeners are called with the values I set with setValue.
Any idea why the listeners aren't firing in this offline state?
I've tried stepping through the firebase code to see if the listener is getting hit and it's not. I've tried creating separate listeners to catch this scenario and they wont fire either.
I am listening to .info/connected to figure out if my device is online or not. If it is online it adds a child with the device id to the database.
_firebaseDatabase.reference().child('.info/connected').onValue.listen((event) {
if (event.snapshot.value == true) {
setDeviceOnline(userUid: uid, deviceUid: deviceUid)
}
});
I am using the onDisconnect callback on Firebase Realtime Database to remove this device id when the corresponding client disconnects.
Future<void> setDeviceOnline({String userUid, String deviceUid}) async {
DatabaseReference currentDeviceConnection = _firebaseDatabase.reference().child('users/$userUid/connections').push();
// Add this device to my connections list
await currentDeviceConnection.set(deviceUid);
// When I disconnect, remove this device
currentDeviceConnection.onDisconnect().remove();
}
Now:
When the client explicitly disconnects, the onDisconnect fires and removes the device id from the db.
When I turn on airplane mode the onDisconnect fires after a short timeout and removes the device id from the db.
When I turn on airplane mode and turn it back off before this timeout happens, the onDisconnect fires immediately when the client reconnects. Like it tells the db "I was offline"
The problem with point 3 is when the clients reconnects, onDisconnect fires and removes the present id from the database and due to the .info/connected listener which shows event.snapshot.value == true again, it immediately adds a new child with the device id to the database.
I think this works as intended. So I don't think this is a bug. But is there a way to prevent removing the child and adding a new one if there is still a corresponding child present in the db?
Once the server detects that the original connection is gone (either on its own, or because the client tells it about it), it executes the onDisconnect handler that you set for that connection.
There is no way to prevent the removal of the previous connection node.
But since a new connection is established, your setDeviceOnline should generate a new currentDeviceConnection connect ID again. In fact, that is precisely why the presence system in the Firebase documentation generates a new ID each time it reconnects. Is that not happening for you?
APIClient?.update(Record, completion: { response, error in
dLog(response)
dLog(error)
})
We've been trying to work through a buggy API that is provided by another company. Sometimes, the API works and I get a response record. Occasionally, the update disappears into a blackhole and completion never gets called, so I don't get an error either.
Ultimately we want the API to be fixed, but what is the correct way to catch this in the meantime so I can popup an alert?
I've used XCTest/expectation/waitFor, but I think that's mainly for Unit Testing, not sure if that could apply here.
We are building a real-time chat app using Firestore. We need to handle a situation when Internet connection is absent. Basic message sending code looks like this
let newMsgRef = database.document(“/users/\(userId)/messages/\(docId)“)
newMsgRef.setData(payload) { err in
if let error = err {
// handle error
} else {
// handle OK
}
}
When device is connected, everything is working OK. When device is not connected, the callback is not called, and we don't get the error status.
When device goes back online, the record appears in the database and callback triggers, however this solution is not acceptable for us, because in the meantime application could have been terminated and then we will never get the callback and be able to set the status of the message as sent.
We thought that disabling offline persistence (which is on by default) would make it trigger the failure callback immediately, but unexpectedly - it does not.
We also tried to add a timeout after which the send operation would be considered failed, but there is no way to cancel message delivery when the device is back online, as Firestore uses its queue, and that causes more confusion because message is delivered on receiver’s side, while I can’t handle that on sender’s side.
If we could decrease the timeout - it could be a good solution - we would quickly get a success/failure state, but Firebase doesn’t provide such a setting.
A built-in offline cache could be another option, I could treat all writes as successful and rely on Firestore sync mechanism, but if the application was terminated during the offline, message is not delivered.
Ultimately we need a consistent feedback mechanism which would trigger a callback, or provide a way to monitor the message in the queue etc. - so we know for sure that the message has or has not been sent, and when that happened.
The completion callbacks for Firestore are only called when the data has been written (or rejected) on the server. There is no callback for when there is no network connection, as this is considered a normal condition for the Firestore SDK.
Your best option is to detect whether there is a network connection in another way, and then update your UI accordingly. Some relevant search results:
Check for internet connection with Swift
How to check for an active Internet connection on iOS or macOS?
Check for internet connection availability in Swift
As an alternatively, you can check use Firestore's built-in metadata to determine whether messages have been delivered. As shown in the documentation on events for local changes:
Retrieved documents have a metadata.hasPendingWrites property that indicates whether the document has local changes that haven't been written to the backend yet. You can use this property to determine the source of events received by your snapshot listener:
db.collection("cities").document("SF")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
let source = document.metadata.hasPendingWrites ? "Local" : "Server"
print("\(source) data: \(document.data() ?? [:])")
}
With this you can also show the message correctly in the UI
I am trying to setup some ServiceListeners, in particular two:
zeroConf.addServiceListener("100.1.168.192.in-addr.arpa.", myListener);
zeroConf.addServiceListener("_workstation._tcp.local.", myListener);
Whenever I do this, I get callbacks for myListener on serviceResolved() and serviceAdded() for all services that match "_workstation._tcp.local." However, I get no callbacks for "100.1.168.192.in-addr.arpa." ... despite the fact that jmDns sends out the queries, and a response comes back! I've attached a tcpdump of the request packets that jmdns sends out, and the response that comes back for it. However, the callbacks are not called so I never see the response in my application.
Does anyone know why this might be happening?
http://users.ece.cmu.edu/~gnychis/jmdns_nocallback.pcap
After some debugging of the actual event type that comes in, the event type resolves to "_tcp.in-addr.arpa." Adding this to my service listeners triggers the call back.