I would like to know if there's an automatic way of knowing when the connection was recovered.
My app connects to a webservice, lets say the network is not available in that moment so the app won't get the info from the server, but I would like the app to automactily try to reconect to the server if it "feels" that the connection was recovered.
Is there such a callback?
In whatever class you handle your NSURLConnection you need to add some connection check. So below I have posted an example
Create a Reachability instance
Add an observer to the Reachability did change notification
When the connection will change the - (void)networkReachabilityDidChange:(NSNotification *)notification will be fired.
You obviously check the networkStatus before firing off a connection in the first place.
-(id)init
{
self = [super init];
if(self)
{
Reachability* newInternetReachability = [Reachability reachabilityForInternetConnection];
[newInternetReachability startNotifier];
self.networkReachability = newInternetReachability;
networkStatus = [self.networkReachability currentReachabilityStatus];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(networkReachabilityDidChange:) name:kReachabilityChangedNotification object:nil];
}
return self;
}
- (void) startHTTPRequest
{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:YOUR_URL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:YOUR_REQUEST_TIMEOUT];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest: delegate:self];
}
- (void)networkReachabilityDidChange:(NSNotification *)notification
{
Reachability *currReach = [notification object];
NSParameterAssert([currReach isKindOfClass: [Reachability class]]);
int currStatus = [currReach currentReachabilityStatus];
// Check that current reachability is not the same as the old one
if(currReach != self.networkReachability)
{
switch (currStatus) {
case ReachableViaWiFi:
// fire off connection
[self startHTTPRequest];
break;
case ReachableViaWWAN:
// Fire off connection (3G)
[self startHTTPRequest];
break;
case NotReachable:
// Don't do anything internet not reachable
break;
default:
break;
}
[self updateReachability];
}
This is only a simple example but you probably need to persist the request until the connection has become available so you can fire it off later. This could be done via NSOperationQueue or something similar.
There isn't such a thing from the standard library perspective. You will have to implement that yourself. You could use apple's Reachability code to listen for network changes. So once you receive a notification from the Reachability code saying that the internet is now connected, you could fire off an URL connection. If you need an example I could mock something up quickly for you.
Related
In my iphone app, the user can set whether he wants to download data from internet via wifi or 3G/Carrier data.
How can we do that programatically?
In other words, how can I force iphone to get data from carrier data not from wifi?
Any suggestion guys?
You can't, if the iPhone is connect to WiFi you can not programmaticly force it to use the cellular network to download.
You can't force the iPhone to use carrier data (3G / Edge) instead of WiFi if the phone is connected to WiFi. You can use the SCNetworkReachabilityGetFlags function to determine if you're on WiFi or have a carrier data connection.
What you could do is, if the user is connected to WiFi, pop up a message saying that your app only works with carrier data and ask the user to switch off WiFi and restart the app. I would not recommend this though as it will just irritate the hell out of your users, although this didn't stop Vodafone Portugal from doing it for a number of their apps in a silly attempt to force you to use more (expensive) carrier data.
I'm not sure if this is helpful to you:
http://developer.apple.com/library/ios/#samplecode/Reachability/Introduction/Intro.html
For that you need to detect the state of the phone and you can easily identifies the weather data isn't transfer when a phone uses the wifi.
-(void) viewWillAppear:(BOOL)animated
{
// check for internet connection
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkNetworkStatus:) name:kReachabilityChangedNotification object:nil];
internetReachable = [[Reachability reachabilityForInternetConnection] retain];
[internetReachable startNotifier];
// check if a pathway to a random host exists
hostReachable = [[Reachability reachabilityWithHostName: #"www.apple.com"] retain];
[hostReachable startNotifier];
// now patiently wait for the notification
}
- (void) checkNetworkStatus:(NSNotification *)notice {
// called after network status changes
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
switch (internetStatus){
case NotReachable:
{
NSLog(#"The internet is down.");
self.internetActive = NO;
break;
}
case ReachableViaWiFi:
{
NSLog(#"The internet is working via WIFI.");
self.internetActive = YES;
break;
}
case ReachableViaWWAN:
{
NSLog(#"The internet is working via WWAN.");
self.internetActive = YES;
break;
}
}
NetworkStatus hostStatus = [hostReachable currentReachabilityStatus];
switch (hostStatus)
{
case NotReachable:
{
NSLog(#"A gateway to the host server is down.");
self.hostActive = NO;
break;
}
case ReachableViaWiFi:
{
NSLog(#"A gateway to the host server is working via WIFI.");
self.hostActive = YES;
break;
}
case ReachableViaWWAN:
{
NSLog(#"A gateway to the host server is working via WWAN.");
self.hostActive = YES;
break;
}
}
}
more information visits this link.
this code is to check if internet connection is available or not.If internet connection is available then username and password should get validated from server database i.e send request method should get called and if not then username and password should get validated from local database i.e check method should get called.but the prob here is when the internet is off then also it gets into the send request method instead of getting in check nethod.What may be the prob.Please help me in solving this problem. i have added reachability files and imported the CFNetwork.framework.
- (void) showNetWorkAlert {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
UIAlertView *networkAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Network connection unavailable."
delegate:self cancelButtonTitle:#"OK" otherButtonTitles:#"Retry", nil];
[networkAlert show];
[networkAlert release];
}
#pragma mark To Check Network Connection.
- (BOOL) currentNetworkStatus {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
BOOL connected;
const char *host = "www.apple.com";
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, host);
SCNetworkReachabilityFlags flags;
connected = SCNetworkReachabilityGetFlags(reachability, &flags);
BOOL isConnected = YES;
isConnected = connected && (flags & kSCNetworkFlagsReachable) && !(flags & kSCNetworkFlagsConnectionRequired);
CFRelease(reachability);
if(!isConnected) {
// sleep(1);
[self showNetWorkAlert];
//[self check];
}
else
return isConnected;
//[self sendRequest];
return isConnected;
}
-(IBAction)buttonPressed:(id)sender
{
//[self sendRequest];
//[[Reachability sharedReachability] setHostName:kHostName];
//Set Reachability class to notifiy app when the network status changes.
//[[Reachability sharedReachability] setNetworkStatusNotificationsEnabled:YES];
//Set a method to be called when a notification is sent.
//[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reachabilityChanged:) name:#"kNetworkReachabilityChangedNotification" object:nil];
//[self updateStatus];
//[self sendRequest];
//NSLog(<#NSString *format#>)
//this is to select username and password from database.
//[self check];
if ([self currentNetworkStatus]) {
[self sendRequest];
}
else {
[self check];
}
}
Rocky your code is right .Just check the URL is proper or not .
there is other way also
On button check the host like this
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
hostReach = [[Reachability reachabilityWithHostName:#"www.google.com"]retain];
[hostReach startNotifier];
and in the (reachabilityChanged:) method call make like this
-(void)reachabilityChanged:(NSNotification*)note
{
static BOOL showNotConnnected =NO;
Reachability *curReach =[note object];
NSParameterAssert([curReach isKindOfClass:[Reachability class]]);
NetworkStatus netStatus = [curReach currentReachabilityStatus];
if(netStatus != NotReachable)
{
UIAlertView *notconnect1 = [[UIAlertView alloc]initWithTitle:#"Server is connected" message:#"Server is connected" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:#"Cancel",nil];
[notconnect1 show];
[notconnect1 release];
}
else if(showNotConnnected == NO)
{
showNotConnnected =YES;
UIAlertView *notconnect = [[UIAlertView alloc]initWithTitle:#"Server is Not connected" message:#"Server may be slow or not connected" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:#"Cancel",nil];
[notconnect show];
[notconnect release];
}
}
I hope this will help you
I do think there is a sample code called Reachability from apple can help you. see the URL below
http://developer.apple.com/library/ios/samplecode/Reachability/Introduction/Intro.html
You should look at Apple's reachability code to check for a connection to the Internet. There is a sample project you can get from them in the iOS Developer Portal.
Reachability should not be used to detect if the network is available before making a network request because the act of making a network request can bring up the network if needed. Reachability should only be used to detect when the network becomes available after being unavailable.
Just try making the network request -- you'll get an error back if the network is unavailable. Usually the response will come back right away, but if the network is spotty it can take a while, so you shouldn't ever make synchronous network calls on the main thread. Use NSURLConnection if possible and you'll get callbacks when something happens.
Is it wise to use Reachability Class(from Apple) to check for a remote host's availability ? say for example, www.google.com
or should I use
NSString *connectedString = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://www.google.com/"]];
if ([connectedString length] != 0) // Host Available
Which is the best option since I've heard that Reachability is having bug with checking for host's availability ?
Here is a good way to check if host is reachable:
NSURLResponse *response=nil;
NSError *error=nil;
NSData *data = nil;
NSURLRequest *request = [NSURLRequest requestWithURL:your_url];
data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
If host is offline you will receive some error code in error, nil in data and nil in response variables.
If host is online and responds there will be some response, data, and error==nil.
Reachability will not tell you if a remote host is contactable. It only tests the first hop i.e. can you send a packet to your router. If the router cannot reach the wider internet, reachability will still tell you that you have a wifi connection. You have to implement one of the other suggested solutions to test "true" reachability.
As mentioned by others, reachability detects changes in your hardware instead of the real availability of a server. After reading many posts, I came up with this code.
Here is a full implementation of a ReachabilityManager class that uses both Reachability and URLConnection to make sure that the connection is available or not. Please note that depends on the Reachability classes (I am using the Tony Million implementation but I am sure it works with the Apple one)
If you test this in the simulator and enable/disable your wireless connection you will see that it detects (sometimes takes a few seconds) the connection/disconnection. Something that before with just the reachability class did not work.
I have also added a couple of notifications that are more effective than the ones from Reachability for the rest of your code. You may want to handle this in a different way.
Also, this is a singleton so you can instantiate if from anywhere. You can convert it to a non static class and instantiate it from the AppDelegate.
If a reader has time to create some UnitTests for it, let me know, so we can share more knowledge around the Reachability.
Just create a new NSObject class and copy this contents:
.h
#import <Foundation/Foundation.h>
#interface ReachabilityManager : NSObject
+ (void)startReachabilityWithHost : (NSURL *)hostName;
#end
.m
#import "ReachabilityManager.h"
#import "Reachability.h"
#implementation ReachabilityManager
static ReachabilityManager *_sharedReachabilityManager;
static Reachability *reachability;
static NSURL *_hostName;
static BOOL isServerReachable;
+ (void)initialize
{
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
_sharedReachabilityManager = [[ReachabilityManager alloc] init];
}
}
+ (void)startReachabilityWithHost : (NSURL *)hostName{
_hostName = hostName;
reachability = [Reachability reachabilityWithHostname:hostName.host];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
[reachability startNotifier];
}
+ (void)stopReachability{
[reachability stopNotifier];
[[NSNotificationCenter defaultCenter]removeObserver:kReachabilityChangedNotification];
}
+(void)reachabilityChanged: (NSNotification *)notification{
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
BOOL isServerCurrentlyReachable = NO;
do{
isServerCurrentlyReachable = [self checkConnectivityToServer];
BOOL wasServerPreviouslyReachable = isServerReachable;
isServerReachable = isServerCurrentlyReachable;
if (NO == wasServerPreviouslyReachable && YES == isServerCurrentlyReachable)
{
NSLog(#"REACHABLE!");
[[NSNotificationCenter defaultCenter]postNotificationName:#"kNetworkReachabilityCustom" object:[NSNumber numberWithBool:YES]];
}
else if (YES == wasServerPreviouslyReachable && NO == isServerCurrentlyReachable)
{
NSLog(#"UNREACHABLE!");
[[NSNotificationCenter defaultCenter]postNotificationName:#"kNetworkReachabilityCustom" object:[NSNumber numberWithBool:NO]];
}
[NSThread sleepForTimeInterval:5.0];
}while(!isServerCurrentlyReachable);
});
}
+(BOOL)checkConnectivityToServer{
NSURLResponse *response;
NSError *error=nil;
NSData *data = nil;
NSURLRequest *request = [NSURLRequest requestWithURL:_hostName];
data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
return (data && response);
}
#end
It is wise to check if you have any internet connection first, and for that I use Reachability. And I try to do my connection with the server only I have internet.
Here is a tutorial on how to use Reachability.
http://www.raddonline.com/blogs/geek-journal/iphone-sdk-testing-network-reachability/
NSString *connectedString = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://www.google.com/"]];
if ([connectedString length] != 0) // Host Available
The code you provided should also work but might not give the desired result.. Reachability is more reliable..
The following code from developer.apple.com might help you..
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [[NSMutableData data] retain];
} else {
// Inform the user that the connection failed.
}
I make use of Apples Reachability class and it works if I keep it on the main thread(bad approach). If I instead move it to a separate thread the notification is never called.
In didFinishLaunchingWithOptions i call the following:
[NSThread detachNewThreadSelector:#selector(checkConnection) toTarget:self withObject: nil];
checkConnection looks like follows:
-(void)checkConnection {
//Test for Internet Connection
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];
Reachability *r = [[Reachability reachabilityWithHostName:#"appspot.com"] retain];
[r updateReachability:appDelegate.reachability];
[r startNotifier];
[pool release];
}
and reachabilityChanged looks like this:
- (void)reachabilityChanged:(NSNotification *)note {
Reachability* curReach = [note object];
NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
[self updateReachability: curReach];
}
and finally updateReachability looks like this:
- (void)updateReachability:(Reachability *)curReach {
NetworkStatus internetStatus = [curReach currentReachabilityStatus];
if ((internetStatus != ReachableViaWiFi) && (internetStatus != ReachableViaWWAN)) {
NSLog(#"No net");
} else {
NSLog(#"Lots of net");
}}
Hope you guys can help me to understand why reachabilityChanged is never called.
Cheers...
I've used Apple's reachability example for the Mac and have had it working fine. You are suppose to run reachability on the main thread. The whole point of the reachability is that you don't have to pool for an internet connection. The system automatically spawns a background thread to monitor for changes in interent connection and will notify you of any changes.
Have your app ever stalled because of reachability or are you just postulating that it might happen?
See this answer:
Reachability sometimes fails, even when we do have an internet connection
When my app launches, I check for reachability because I need an immediate internet connection. My problem, though, is that it appears there's no immediate confirmation for the NetworkStatus, which means right after the Reachability is setup, I check whether there's a connection, and it returns that there isn't, regardless of whether I am in fact on WiFi/3G, or have turned the radio off.
I can confirm that I am in fact getting an Internet connection, because instantly after applicationDidFinishLaunching, there's a notification which then logs "ReachableViaWiFi"..
What am I doing wrong? Why is it not confirming a valid Internet connection right away?
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NetworkStatus netStatus = [hostReach currentReachabilityStatus];
if (netStatus == NotReachable) {
ErrorViewController *errorViewController = [[ErrorViewController alloc] initWithNibName:#"ErrorView" bundle:[NSBundle mainBundle]];
[tabBarController.view removeFromSuperview];
[window addSubview:[errorViewController view]];
return;
}
}
-(void)setupReachability {
[[NSNotificationCenter defaultCenter] addObserver: self selector: #selector(reachabilityChanged:) name:kReachabilityChangedNotification object: nil];
hostReach = [[Reachability reachabilityWithHostName:#"www.google.com"] retain];
[hostReach startNotifier];
}
-(void)reachabilityChanged:(NSNotification *)notification {
Reachability* curReach = [notification object];
NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
NetworkStatus netStatus = [curReach currentReachabilityStatus];
BOOL connectionRequired = [curReach connectionRequired];
switch (netStatus)
{
case NotReachable:
{
[[NSUserDefaults standardUserDefaults] setInteger:kNOTREACHABLE forKey:kREACHABILITYSTATUS];
NSLog(#"NotReachable");
connectionRequired = NO;
break;
}
case ReachableViaWWAN:
{
[[NSUserDefaults standardUserDefaults] setInteger:kREACHABLEVIAWWAN forKey:kREACHABILITYSTATUS];
NSLog(#"ReachableViaWWAN");
break;
}
case ReachableViaWiFi:
{
[[NSUserDefaults standardUserDefaults] setInteger:kNOTREACHABLE forKey:kREACHABILITYSTATUS];
NSLog(#"ReachableViaWiFi");
break;
}
}
}
OK, so after trying a few things out myself, I actually got it to work, by adding one extra line of code:
-(void)setupReachability {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(reachabilityChanged:) name:kReachabilityChangedNotificationV2 object:nil];
hostReach = [[ReachabilityV2 reachabilityWithHostName:#"www.google.com"] retain];
[hostReach connectionRequired]; // this line was added, and apparently forces a connection requirement..
[hostReach startNotifier];
}
The Reachability sample code provides you with asynchronous callbacks/notifications to inform you of how/when the reachability has changed. In order for your code to work, you should modify your code as follows:
- (void) applicationDidFinishLaunching:(UIApplication *)application {
// setup reachability
[self setupReachability];
}
Then in your callback, when you get the notification you react as needed by your application.
In other words, you can not check immediately for the network status in applicationDidFinishLaunching(). If you want to do so, then you must use a synchronous/blocking method, for instance you can use the code provided in my answer to this question.
You have to make hostReach as class level variable.