I am developing an iPhone app and whenever I make a call to my web service I want to make sure that the user is connected to the internet.
I used the Reachability class provided by Tony Million on github, link is her for anyone who wants to grab it. https://github.com/tonymillion/Reachability
The I just followed the examples and set everything up and have the following code
Reachability* reach = [Reachability reachabilityWithHostname:#"www.google.com"];
reach.reachableBlock = ^(Reachability*reach)
{
// call web service here
};
reach.unreachableBlock = ^(Reachability*reach)
{
// alert user not reachable
};
[reach startNotifier];
Now I test this in the simulator and everything works perfectly fine, my service is being called when connected to the wifi. And if I switch the wifi off, I see an alert displayed which is exactly what I need.
Now I test this on the device and get decent results but not exactly what I need. It is important to note that my phone does not have a data package.
So here are the scenario's
I connect my phone to wifi and run the app, perfect, a call is made to the web service. Nothing to worry about here.
I disconnect my phone from the wifi and run the app again, I EXPECT the alert to come up telling me that its not connected but then I see the UI activity indicator spinning in my app which means the app has detected a connection of some sort and is trying to connect to my web service. But this will never happen, I know it is detecting the cellular 3g as I go into settings->general->usage->cellular usage, reset the stats to 0. After a while, I can see data being sent and received.
I go to settings->general->cellular->turn 3g off, run the app and now it shows the alert of no connectivity.
I know a lot of people have data packages and also I have see apps hit the marketplace with the level of reachability I have mentioned above, I just feel this could be improved in the scenario I just explained.
I have a 3g connection but I do not know how
[Reachability reachabilityWithHostname:#"www.google.com"];
gets pinged, or perhaps its not ?
Although I did switch my wifi off and opened safari, it behaves the same way, it shows that its loading for quite a while and then just says safari could not open ...
Is this just something that cannot be accomplished ?
Finally, I even saw the sample, Tony Million provided on the github page, when run on my phone, it shows reachable despite wifi being off and me not having a data package.
I looked at a few stack over flow answers where users asked about checking for internet connectivity but most answers either revolved on the "type" wifi, wwan etc rather than detecting if it is a valid internet connection rather than being connected to a network.
Thank you for your time.
After quite a few a few days of research I found a solution that was useful for me and hopefully somebody in the future might find it useful.
I created a singleton class with the above reachability code so that I could use it and check it whenever I make web calls easily. Although this did not solve my issue as I mentioned before but this is the first check.
Then I went ahead and just did the regular stuff to get data from the url
NSdata * data [NSData dataWithContentsOfURL:myurlhere];
if([data length] == 0)
{
show alert view here
}
else
{
doing stuff with the items in data
}
So using a combination of reachability (this does not always work in some cases like mine where a cellular network uses 3g or 4g) and checking the data downloaded can serve as a good check point for the app.
First, you need to check your cellular data permission for your app in settings.
Related
I am having consistent issues with the Reachability class reporting incorrect state and losing notifications from Tony Millions reachability class. Although something similar has been reported here I would like to add more information that may or may not be useful in resolving my variation of the problem.
Firstly, I'm using the latest Xcode Version 6.3.2 (6D2105) and am working on a Macbook Pro connected via Wifi and have downloaded the latest Reachability class (as of today by post date 5 Aug 2015) which I have set up as an instance that I pass around my iPhone application (not a singleton pattern).
Secondly, and probably quite importantly I'm using the iOS simulator in order to get the application behaviour correct before deploying it to an iPhone.
I'm taking advantage of both the notification mechanism, and also the direct 'isReachable' method depending on my usage about the code - notifications for triggering sync'ing and status bar updates and the 'isReachable' for conditional method calls. The reachability class is instantiated in the app delegate using this style:
[Reachability reachabilityWithHostname:hostAddress]
And the reachability notification service is started using the
[reachability startNotifier]
method. Now, when the application starts, the reachability correctly indicates network access (be that up or down) and I also receive notifications as expected.
If I simulate the network failing by turning of Wifi (Turn off wifi from the computers perspective) I correctly receive the notification that the network is no longer reachable. But when I turn Wifi back on again I receive another notification which also tells me that the network is no longer reachable - even though it is. In addition, the 'isReachable' method also reports incorrectly.
Note that I call isReachable immediately after the result of a notification from NSNotificationCentre. I also ask for isReachable calls on different threads (does this matter)? Occasionally I simply don't get reachability updates at all, and eventually the notifier just seems to stop working.
Any one have any thoughts on what I'm doing wrong here? Thanks!
I'm having quite a lot of trouble on identifying rigorously the kind of network access an iPhone has. I have seen many questions like this one on StackOverflow but none of them helped me. For example I have already seen this: How to check for an active Internet connection on iOS or OSX?
But I want to know precisely when the 3 following cases occur:
Wifi and 3G are disabled
Wifi or 3G are enabled but there is no internet connection. It could be due to 3G not really working, even though it is showing up on the status bar, or the WiFi is a hotspot asking to log in before letting you access the Internet or last scenario: The WiFi source is a local network but does not provide any Internet connection.
Everything works fine
Of course I have tried various stuff, like this for instance:
// Allocate a reachability object
Reachability* reach = [Reachability reachabilityWithHostname:#"www.google.com"];
// Set the blocks
reach.reachableBlock = ^(Reachability*reach)
{
/* NOT REACHABLE */
dispatch_async(dispatch_get_main_queue(), ^{
iarcSBInternetWarning.visible = NO;
});
};
reach.unreachableBlock = ^(Reachability*reach)
{
/* REACHABLE */
iarcSBInternetWarning.visible = YES;
};
// Start the notifier, which will cause the reachability object to retain itself
[reach startNotifier];
But this seems just to say wether Wifi or 3G are enabled and does not check for possible internet restrictions. I would be highly delighted by any answer helping me identifying the three scenarios.
Of course analyzing a direct HTTP request output from a personal server API returning a specific byte sequence would work but that is not what I would like to do. I'd prefer using any kind of Reachability API.
I've used Apple's reachability API, it does not seem to recognize hotspot redirections.
If this is not possible with the Reachability APIs, then what would be the lowest resource-consuming method to make a simple request to a server, check that it is reachable before a specified timeout, and that the URL was not redirected to a hotspot login page (maybe only check the headers, not the whole server byte sequence output)? Is it a good idea to run such a script every 15 seconds, or would it use too much battery and/or network data?
The simplest method to check outbound connectivity is to GET Google's generate204 page and check the response code
http://www.google.com/generate_204
Or you can use
http://www.apple.com/library/test/success.html which is the page the OS uses
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.
I am writing an app which connects via WiFi to a proprietary device. The proprietary device acts as a WiFi access point.
When the device powers down, the WiFi connection is terminated, as I would expect.
The iPhone continues to send probe requests, looking for networks to connect to. It follows an exponential backoff algorithm for sending probes.
My problem is that eventually the intervals between the probe requests sent by the iPhone are longer than the timeout on my device, so they don't make a connection.
I am using the Reachability code and it works as I would expect.
It seems that pressing the home button will reset the backoff and send a probe request immediately. Does anyone know of a way to have my app do something similar?
Thanks for any help.
Instead of pinging the internet every time with Reachability, ping a host on the local network like the DNS server or the router (192.168.1.1).
I somehow experienced a similar situation to check if the device was connected in a specific VPN connection. The approach was to ping to a machine in the local network, via standard ping or implementing a ping web service.
If you don't have a backend in your local network then the easiest would be the ping. You can check the following code example from the Apple developer site:
http://developer.apple.com/library/mac/#samplecode/SimplePing/Introduction/Intro.html
I would suspect that if you reconfigure the connection (re-set the WiFi configuration) in your app that the iPhone would restart scanning without the backoff. So your app could keep track of how long it was since the connection was lost and then reconfigure the link after an appropriate amount of time. Possibly you would have to reconfigure to a different SSID and then switch back, depending on how smart the iOS-libraries are.
I'm not shure, if i understand you right. You want to check if the device is still connected to the access point?
I've build an app to connect to a scanner via wifi, the scanner acts as access point. for that i check if the SSID the device is currently connected to is the one of the scanner. to check the CurrentSSID you can use the following code:
+(NSString*)currentSSID {
CFArrayRef myArray = CNCopySupportedInterfaces();
CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
if (!myDict) {
return nil;
}
NSDictionary *myDictionary = (__bridge_transfer NSDictionary*)myDict;
return [myDictionary objectForKey:#"SSID"];
}
hope that helps.
I am afraid there is no way of doing so without being rejected during the review (you could read on how to access the SBWifiManager). Apple does not enable any way to access the wifi manager from the sandbox environment. I experienced similar issue connecting my device to various access points (for locating with probe request), up to now with the iOS 7 the probe requests were sent between huge intervals (even 15 min). Try to modify your access points.
G'day Guys,
I've been using the reachability API with reachability status callbacks to determine whether an application is connected over 3G or wifi. It's an application that acts as a voice extension for an existing piece of hardware and as such we're using the VoIP APIs to run in the background and accept calls etc.
Is there a definitive way other than using reachability status callbacks to determine whether you can access a particular IP endpoint or not? I could use an ASIHTTPRequest and then check if it timed out but that may cause potential problems for me in the long run.
I'm not looking for a programmatical answer but more any insights other developers would have on how to manage a roaming between the two in the background if you have a persistent connection. Basically if the device roams over to 3G I need to destroy the session on the device and if it roams back over to Wifi I need to recreate the session.
Any feedback or advice would be welcome.
The Reachability APIs will provide the connection change notifications to your app so that you can know when the connectivity changed from WWAN to wifi. It will not tell you if you've changed from Edge to 3G or LTE unfortunately. The Reachability API also has methods to test reachability to a specific host. So, in your app you can listen for the notifications that the connection method has changed, then when it does change test reachability to your target host and at that time make the decision whether to rebuild the session or leave it intact.