How to handle network connectivity issues (Firebase FireStore)? - ios

For testing, I've turned off networking. I've got caching disabled:
settings.isPersistenceEnabled = false // https://firebase.google.com/docs/firestore/manage-data/enable-offline -- done to help demonstrate async handling
I'm noticing that error is not set even though I see this in my logs:
2018-08-04 10:26:26.441529-0400 Behavior-based Notifications
prototype[52077:2250670] 5.4.1 - [Firebase/Firestore][I-FST000001]
Could not reach Cloud Firestore backend. Connection failed 2 times.
Most recent error: Error Domain=FIRFirestoreErrorDomain Code=2
"Network error (such as timeout, interrupted connection or unreachable
host) has occurred." UserInfo={NSLocalizedDescription=Network error
(such as timeout, interrupted connection or unreachable host) has
occurred.} This typically indicates that your device does not have a
healthy Internet connection at the moment. The client will operate in
offline mode until it is able to successfully connect to the backend.
Here's relevant code snippet:
DBIFirebase.db.collection(URI).getDocuments() {
(snapshot, error) in
if tx < 1 {
print("returned from Firebase TX when tx count is exhausted, dropping")
return
}
if let error = error {
// in either case, there was a previous error
print("Firestore error loading data for app \(app) - \(error.localizedDescription)")
callback(samples, Errors.firebaseError)
tx = 0
return
}
error is always nil. documents are always empty.
edit:
Very near after that if let error, I use this to process the documents:
guard let documents = snapshot?.documents else {
print("no documents for app \(app)")
callback(samples, nil)
tx = 0
return
}
for document in documents {
I set a breakpoint. error is seen to be nil. stepping into this block skips over it (the guard block doesn't trigger, I'm talking about stepping into from a breakpoint at the for loop). If I turn networking on, it goes into it (not a swifty caching loops thing).
How would I distinguish between an empty but successful return and a network connectivity issue?

Related

go error handling of 'dial tcp i/o timeout' from docker

We have an application hosted in a docker container calling a REST API, and we'd like to identify network timeouts and handle them in a specific way
Right now to detect timeouts we're using the following code snippet:
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
// do something...
}
While this did work in capturing some cases, when I setup an integration test where firewall/security groups was not configured correctly and the host isn't reachable, it resulted in the following error:
Post \"https://notreal.com/endpoint\": dial tcp 10.130.140.150:8443: i/o timeout
In this case it doesn't seem to fall under net.Error, and ideas on how else to identify this?
Edit:
Looks like this is an issue with our own libraries. The error was originally one that implemented net.Error but was swallowed, returning only the string and no longer implementing Timeout() or Temporary().
You have to implement it by yourself like you can see in this SO answer. However another way to this is to use an external package that will do that for you like retryablehttp.
resp, err := retryablehttp.Get("/foo")
if err != nil {
panic(err)
}
The returned response object is an *http.Response, the same thing you would usually get from net/http. Had the request failed one or more times, the above call would block and retry with exponential backoff.
If you need to distinguish transient errors from permanent errors (like endpoint that does not exist) you may try something like this (not tested by myself)?
if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
// retry
}
if err != nil {
// unrecoverable error
log.Fatal(err)
}
Catching specific net errors can be tedious since there is a bunch of errors AddrError, DNSError and so on.

Detecting Connection State Called Twice

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")
}
})
I'm using this to detect my connection state to my firebase. My problem is when their is internet connection the result goes "Not Connected" then afterwards goes "Connected". When their is no internet connection it just goes directly to "Not Connected". Can someone please explain?
What you're seeing is the expected behavior.
The .info/connected flag determines whether the app/client is connected to the Firebase Database backend. While this of course requires that you have an internet connection, there is more to it than that. That's why it is possible for .info/connected to be false even though you have a working internet connection.
This is especially true when you start the app. It takes a few moments after the app starts before the Firebase client is connected to its database server, so usually the .info/connected value starts as false and then becomes true. Sometimes it even toggles a few times before settling.
Also see:
How to handle internet connection status Firebase
Firebase connection state listener returns false in javascript

Repeat an asynchronous request if it fails?

I am writing an ios app that relies on being able to tell when a user is connected to wifi and, when he or she connects or disconnects, send an asynchronous request using alamo fire.
The first time I connect, my asynchronous succeeds.
However, after I first connect, any toggling of the wifi results in 404s.
I suspect this is because I am sending the request as soon as the user connects/disconnects, meaning that for a brief moment he or she has no internet service.
My question is, can I repeat the request if it fails or is it possible to "cache" the requests I want to make and wait until the user has internet connection to make them?
There are many solutions to solve this. One is to call the download method recursively again and so implementing an automatic retry mechanism on errors:
func downloadSomething() {
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
.response { request, response, data, error in
if let error = error {
log(error)
self.downloadSomething() // recursive call to downloadSomething
} else {
// do something on success
}
}
}
You can extend this by:
showing the user also an altert view asking him if he want's to retry
the download or not before retrying the download. (depending on your
UI strategy on network errors)
a specified count of automatic re-trys and then ask the user.
checking the error status code and then depending on the code do
different network error handling strategies...
etc...
I think there is no needed to re-invented apple code like reachability or this swift reachability porting. You can able to check if a user is connected to the net or wifi very easily:
class func hasConnectivity() -> Bool {
let reachability: Reachability = Reachability.reachabilityForInternetConnection()
let networkStatus: Int = reachability.currentReachabilityStatus().rawValue
return networkStatus != 0
}
For a Wi-Fi connection:
(reachability.currentReachabilityStatus().value == ReachableViaWiFi.value)

"Server Rejected Request" (15/2001); "Request failed with http status code 500"

fetchUserRecordIDWithCompletionHandler returns:
<CKError 0x14daad30: "Server Rejected Request" (15/2001); "Request failed with http status code 500">
I have never seen this error with CloudKit. Do you think it is associated that some iCloud service was down nowadays?
defaultContainer.fetchUserRecordIDWithCompletionHandler({ _userRecordID, error in
if error == nil {
userRecordID = _userRecordID
loggedInUserRecordName = _userRecordID.recordName
dispatch_async(dispatch_get_main_queue(), {
self.progressView.setProgress(2 / self.steps, animated: true)
})
} else {
Utility.log("error 1231: \(error.localizedDescription)")
}
dispatch_semaphore_signal(self.sema)
})
Strange that fetchUserRecordIDWithCompletionHandler works in one of my other project with an other container, but usually does not work with this project with this container.
Any idea way?
Probably a server issue at the other end. 500 error code is unexpected internal error at server.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
This can happen when you are using wrong container. In my project I was using custom container that did not match default app container. Switching to correct one with custom identifier solved the issue.
Check the id of the CKContainer in Capabilities and your CKContainer's initialization make ensure that they are the same. In my case, I make this mistakeļ¼Œ and the error?.localizedDescription is Optional("Request failed with http status code 500")

Connection problems from a netty client in Android

I am using a Netty server in linux with one netty client in Android. (Netty 4.0.23).
It works perfect most of the time. However, sometimes, my client is not able to connect to server.
The ChannelFuture received when trying to connect tells me that connection is done but not success (with cause not null) so it is completed with failure.
About the failure I received it is a java.net.socketException - EHOSTUNREACH error (No route to host).
I am quite sure that the problem is not in server.
Important:
I always run the server and the client in the same lan wifi network
with local ip addresses.
I have tested it in two different wifi
networks. In one of them, the reported error occurs rarely and in the
other occurs quite often.
CONNECT_TIMEOUT_MILLIS is configured with enough time in both contexts
I attach the way I do the connection from client:
.............
final ChannelFuture futureConnection = bootstrap.connect(HOST, PORT);
futureConnection.awaitUninterruptibly();
if (futureConnection.isDone()){
if (futureConnection.isCancelled()) {
// log error
}
else if (futureConnection.isDone() && futureConnection.isSuccess()){
// do something
}
else if (futureConnection.isDone() && (futureConnection.cause() != null)) {
// log error THIS IS THE ERROR REPORTED
}
else {
// log error
}
}
............

Resources