Should I listen for reachability updates in each UIViewController? - ios

I see a lot of Reachability examples where people only display a message when reachability status changes.
But recently, I saw in Foursquare app that they display a message every time the user try to make an action requiring an Internet connection.
I think this is more robust and a better UX to remind the user he can't do anything without Internet. Mainly because users can switch between apps, do something else and forget he has no connection when he comes back.
Also as soon as they get the connection back I can see that they fetch data from the Internet and refresh the UI.
What I am really looking for is the best way to do this. How this is done?
Do they have a general UIViewController that checks for reachability each time it needs a connection?
Or do they have a kind of proxy class before each Internet request that cancels the request and display a message?
How you guys are dealing with that?
Thanks.
EDIT:
The solution I came up with is using AFNetworking which also provide reachability status in the box.
Basically I created an AFHTTPClient and set a reachability callback block on it to listen to status changes. The AFHTTPClient object is application wide (kind of a singleton). (in fact I have one AFHTTPClient per host I need to reach a.com, b.com ...).
Then when I need to perform a request I create a new AFHTTPRequestOperation (AFJSONRequestOperation in my case) and I enqueue it on my AFHTTPClient object.
In the failure block of the operation I check to see if the host is reachable with the networkReachabilityStatus property of the AFHTTPClient. If it's unreachable I display a message that there is no internet connection to the user.
I wrapped that up so I don't have to do this each time I create an operation. So now in the app, each time the user try to do something when there is no connection he got a message remembering him that he has no internet access.
I also use the reachability callback to reload data on a screen once I get the connection back (or rather once I am supposed to have a connection).
I don't know if it's best practice but I think it's nice to know that the app takes care of reloading important data as soon as a new connection is available.
If someone is interested by a sample code I can provide it.

On a WWDC talk this year the Apple engineer on stage recommended users to never base the application internet access on the Reachability example app status. Often reachability doesn't provide a complete information (it is based on a complex mechanism) and the suggestion provided by the engineer was this:
try to do your internet connection, whatever it is the Reachability status; then set your UI hint based on success/fail result
if it fails due to networking issue, then register to Reachability and retry again when Reachability gives the green light; this is needed when you want to recover automatically from the fail condition
in any case give the user the possibility to "force a retry", whatever is the Reachability status. If it succeeds, reset your UI hint immediately.
What the Apple engineer said is perfectly true: quite often you can see in the console log Reachability failure messages while the internet connection is perfectly alive.
Other thing: there is no better "network hint" than the one displayed in the status bar: if you have there the wi-fi icon, the 3G/4G icon, the cellular field strength.
Returning to your original question: there is no absolute better way to manage this stuff, this depends heavily on the application architecture. In case you prefer to concentrate your networking stuff in a dedicated class (not a UIViewController but a NSObject subclass) then it could make sense to define a read-only property for that class that is updated with "success/fail" after latest internet connection with the servers app (it makes no sense to ping other servers like Google or Apple: first of all it's not elegant, second the issue may come from your the servers that serve the app and not the device internet connection status!). #property (readonly) BOOL lastConnectionToMyServerSuccess
Then your view controllers can register (by KVO or by central notification) to this property changes and update their UI accordingly by showing an icon or else (I repeat: leave the user the possibility to try manually to connect to the internet). View controllers should unregister from KVO when out of sight ("viewWillDisappear:") or unloaded ("viewDidLoad:") or dealloc'd.
Of course this adds some extra complexities. E.g.: you use the app, the internet light was green. Then you suspend it, do something else and after a few minutes your return to the app. In such case the app should ping your servers to restore the internet light status again, as after a few minutes network conditions could have changed (e.g. you are on a train). In any case all loaded view controllers will get the KVO notification from the network dedicated class and update themselves.

Related

What network protocol does Reachability use on iOS?

I am sure there is little % of iOS developers who haven't used Reachability, directly or via some framework like Alamofire.
What I am interested what it actually does? The best guess I can make is that given host it opens sockets and then listens for said host. But what network protocol does it use, is it simple UDP (it is not http as far as I can observe), where it periodically sends packages to said host and awaits answer?
Reachability sends no packets at all. It doesn't even tell you that a host is actually reachable. It just tells you that if you made a network request, then the system has an active network interface that it would try to use. That's all. There's no promise those packets would arrive (let alone that you'd get a response), just that iOS would try to send them.
Reachability really only has a couple of uses (and most of the time shouldn't be used). It is useful if "no network is available" would cause you to modify your user interface, or to tell you that it might be a good time to re-try a previously failed connection. (Since iOS 12, you should really use NWPathMonitor for this. I don't know any good uses for Reachability since iOS 12.)
The only way that you can know that a request will actually succeed is to try to send it and see if you get a response. That's why it is not recommended that you test Reachability before sending requests. Just send the request and deal with the errors if they come, since it is always possible to have an error, even if Reachability said you could connect.

iOS Networking - HTTP Connections & running in background

I have an app that lets the user send messages with images. A user might hit send, then immediately close their phone or switch to another app.
We were running into an issue that if there's temporarily a bad network connection the message would fail to send. We switched to using NSURLSession backgroundConfigurationWithIdentifier so that backgrounding the app doesn't immediately time out the running request. We switched to using this for all our api requests, thinking that it wouldn't hurt for every request to able to continue in the background if the app were closed at the wrong time.
Fast forward a couple weeks we're noticing all requests seem slow. Using wireshark I just discovered that this background session seems to use a new http connection per request, meaning it requires setting up a TCP connection and new TLS handshake for every request, which was adding a ~500ms latency on every request in our app. This is a pretty big deal but I can't find this behavior documented anywhere, including the link above or Apple's background transfer considerations.
So my question is, is this behavior expected, or am I doing something wrong somewhere? Is there an easy way with NSURLSession to make an HTTP request that will use an existing keep-alive connection if there is one, but can fall back to the backgroundConfiguration if the app gets moved to the background?
NSURLSession is the recommended way to fulfill your use case. Have you tried setting backgroundSessionConfig.discretionary = true
iOS Reference
A Boolean value that determines whether background tasks can be
scheduled at the discretion of the system for optimal performance.
If that doesn't help, I recommend filing a bug with iOS.

iOS Reachability when app is in Background

My application requires exact information on whether or not user is connected to Wifi network and not on internet connectivity so do not comment on the use case please. The task is: user needs to verify that their Wifi is connected but if they dont have any Wifi network available to connect to, they can do it later. For this I need my app to notify the user when he later connects to a Wifi network that now you can continue with the test.
Can I accomplish this using Reachability in background fetch request? Are there any limitations to tasks that app can perform in Background fetching?
This is not solvable in the general case. You cannot run arbitrary code in the background for an indefinite period of time (unless you already are an allowed background task for other reasons, such as location services or VoIP, but these still have restrictions). And you cannot request to be launched when reachability changes.
You can, however, configure an NSURLRequest to not use cellular access. That's not exactly the same as "wireless" but it's close and may be what you actually mean (you're not clear on why "wireless" is the criteria). You can then use NSURLSessionUploadTask or NSURLSessionDownloadTask to ask the OS to perform the action when possible (without waking you up or involving you in any way when you're in the background). If that request were to your server, you could then use a push notification to alert the user and achieve the experience you're describing.

Proper way of checking for Reachability and App Store submission tips

I have read in the App Store Submission Tips that
If your application provides functionality that requires access to a network, it's very important that your code include a customer alert or notification when the network is not available.
In fact, there are two entries in that submission tips list concerning Reachability (Don't Forget to Include Network Error Alerts in Your Code and Be Sure to Provide Network Error Messages). But I donĀ“t know how an app is expected to manage Reachability actually:
1) Should you listen for network reachability status changes, and notify the user every time the network is not available? Or should you check for the reachability of the network when you are about to perform a network operation, and then notify if needed? Or both?
2) Is it required to check for the reachability of the certain remote hosts you need to call in your network operations, or checking for network availability (either WiFi or WWAN) will be enough?
I'd appreciate some guidance from someone who had already successfully submitted an app to the App Store.
Thanks in advance
1) If your app only needs to access the network when the user specifically chooses to do something, then checking at that time is fine. Depending on your app, you might want to listen for changes in reachability and update your UI based on the current status (such as disable a button if there is no network connection). Don't pop alerts every time the reachability status changes. That would be annoying.
2) Depends on your needs. If you have something that always connects to a specific host then checking that host would be good. If the access can be to anything on the Internet then simply check for Internet access.
All of this can be done with the Reachability class from the "Reachability" sample app.

What's the point of checking reachability before making an internet connection?

Several weeks ago reading through the Apple guidelines it says apps should check for the reachability status before attempting to make a connection, and I've read of apps being rejected from the app store for not doing this.
However the reachability APIs can take up to 30 seconds (according to the Apple documentation, and also I've seen this myself happening sometimes) to determine if reachability is available or not. In this situation the API returns not reachable.
Therefore you can have the situation where you do actually have reachability but the APIs say you don't and you won't find out you do for 30 seconds or so.
Having to wait 30 seconds is unacceptably long - especially if the connection has been initiated by the user. Consider this scenario:
User clicks some button that performs some internet related activity
In accordance with the Apple guidelines the code uses the reachability API to check if there is reachability before attempting to make the connection.
The Reachability API says there isn't reachability (but actually there is) so the code tells the user there isn't connectivity (confusing them in the process)
30 seconds later the Reachability API informs the code there now is reachability
But its too late because the user has exited the app and gone elsewhere because they got fed up waiting.
By following Apple's guidelines to first check reachability there is a real chance that an absolutely terrible user experience has resulted and the app didn't contact the server when it could have done.
This seems ridiculous, surely I am missing something?
How can you follow Apple's guidelines while still give a responsive app?
I've experiences these delays in the Reacability API and want to ditch it - because I've seen occasions when it says there isn't reachability but if you try there is, therefore I want my app to try the connection anyway regardless of what the API says. But if I do this then there is a chance the app could be rejected?
Is there a solution to this dilemma?
I hesitate to answer this because it becomes a flame war. Answer - reachability doesnt work. The fact that it takes however long on a background thread simply doesnt matter.
The bigger problem with it isnt that it says you dont have reachability when you do, its that it says you do when in fact you dont. Which is the point.
Apple is not rejecting apps that dont use reachability. They are rejecting apps which cannot handle a network switchover from 3G to wifi and back and apps which cant handle losing connectivity properly.
Apps which do not properly ping a reliable backend taking into consideration timeouts and retries regardless of reachability and notify the user when connectivity has been lost will be rejected.
Apps which lock up when the plug is pulled on the network will be rejected.
Although apple has tried to be helpful and clear, the code they provide is not the code they use themselves, and its not sufficient.
You are left as an app developer to make it work for the rejection scenarios above.
Thats it. So forget reachability and simplePing from Apple.
Run your app, kill the network when a request is happening. Does it hang? Fail.
Pull the network when a request is NOT happening (but one might soon), do you notify the user?
Thats the whole of app rejection due to network issues.
Dont complain about stuff that has never worked. Although I would like this to be Apples problem,
its ours, and I worked too long on my code for this to give it away on StackOverflow.
Ask yourself this, am I pinging a reliable backend?
Am I doing it in a thread?
Am I timing that thread for timeouts?
Am I retrying before overreacting?
Its easy, but then again, its not.
I think you are taking apple a bit too much by their word...
What the HIG actually try to express is that an App should expect to have no connection even if it needs a connection.
So the user should be gracefully told that the connection needs to be active.
What we usually do is that we simply try to reach the service connected to the app and if that fails we tell the user to enable a connection.
Using the Reachability API is not mandatory.

Resources