views:

1600

answers:

1

Apple's sample app named Reachability shows how to detect connections. If you have only wifi but not internet, the app stalls for over a minute on the second line below:

SCNetworkReachabilityFlags reachabilityFlags;
BOOL gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef, &reachabilityFlags);

SCNetworkReachabilityGetFlags comes from SystemConfiguration.framework. Any suggestions on how to get around this?

+3  A: 

To answer your question directly, no, there doesn't seem to be any way to "get around" SCNetworkReachabilityGetFlags() taking a long time to return under the specific circumstances you described (e.g., checking remote host reachability via WiFi connection to a router with no Internet). A couple of options:

OPTION 1. Make the call in a separate thread so that the rest of your app can keep running. Modify ReachabilityAppDelegate.m as follows for an example:

// Modified version of existing "updateStatus" method
- (void)updateStatus
{
    // Query the SystemConfiguration framework for the state of the device's network connections.
    //self.remoteHostStatus           = [[Reachability sharedReachability] remoteHostStatus];
    self.remoteHostStatus = -1;
    self.internetConnectionStatus = [[Reachability sharedReachability] internetConnectionStatus];
    self.localWiFiConnectionStatus = [[Reachability sharedReachability] localWiFiConnectionStatus];
    [tableView reloadData];

    // Check remote host status in a separate thread so that the UI won't hang
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSTimer *timer = [NSTimer timerWithTimeInterval:0 target:self selector:@selector(updateRemoteHostStatus) userInfo:nil repeats:NO];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    [pool release];
}
// New method
- (void) updateRemoteHostStatus
{
    self.remoteHostStatus = [[Reachability sharedReachability] remoteHostStatus];
    [tableView reloadData];
}

OPTION 2. Use a different API/function that uses a timeout value when trying to connect to the remote host. That way your app would only hang for X seconds before it gives up.

Some other things to note:

  • The specific call to SCNetworkReachabilityGetFlags() that you're asking about (i.e., line ~399 in Reachability.m) is trying to see if www.apple.com is "reachable" to deduce if "the external internet" is "reachable" in general.
  • In Apple's System Config framework "reachable" might not mean what you think it does. According to the official docs, "reachable" seems to mean that, in theory, your computer could connect to host X if it wanted to, but it might need to actually establish a connection first (e.g., dial a modem first). In other words, SCNetworkReachabilityGetFlags() doesn't actually establish a connection.
Clint Harris
Thanks. How do I create a timeout so that it returns to a call back if the timeout interval is hit? If UIWebViewDelegate::webViewDidFinishLoad is called before the timeout interval, I could cancel the timeout.
4thSpace
Take a look at NSTimer's scheduledTimerWithTimeInterval: method. You could create a timer in the current thread that, for example, executes some method after 5 seconds. This method could check to see if SCNetworkReachabilityGetFlags() has finished, and if it hasn't, run your timeout logic.
Clint Harris