Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Reachability reports no WiFi, when WiFi is active

I am using Reachability class from Apple to detect network events that have impact in the functionality of my app. It is a voip app that uses setKeepAliveTimeout, so every ~10 minutes wakes up reads the network status and decides if the connection should be refreshed.

BOOL res = [app setKeepAliveTimeout:600 handler:^{        
      [[WIFI instance] isWifiConnected];
      [[AClass an_instance] refresh];
  }
}];

So every 10 minutes the isWifiConnected is called and the app reads the network status again.

- (BOOL) isWifiConnected {

    self.wifiReach = [Reachability reachabilityForLocalWiFi];
    NetworkStatus wifiStatus = [self.wifiReach currentReachabilityStatus];

    switch (wifiStatus) {
        case NotReachable: {
            m_wifiConnected = NO;  
            LOG(@"NetStatus:NotReachable");
            break;
        }
        case ReachableViaWiFi: {
            m_wifiConnected = YES;   
            m_wwanConnected = NO;
            LOG(@"NetStatus:ReachableViaWiFi");
            break;
        }
    }
    return m_wifiConnected;
}

Although I have WiFi in the device the call returns false, ie no WiFi, and moreover NotReachable for the net status.

However after a very short time interval the reachability callback is called again and the the wifi seems connected. However i have already fired an event due to the error value and the app closes the connection with the server believing that there is no wi-fi.

Doing some research it I found this in the Readme file of the Reachability.m file (provided from Apple)

By default, the application uses www.apple.com for its remote host. You can change the host it uses in APLViewController.m by modifying the value of the remoteHostName variable in -viewDidLoad.

IMPORTANT: Reachability must use DNS to resolve the host name before it can determine the Reachability of that host, and this may take time on certain network connections. Because of this, the API will return NotReachable until name resolution has completed. This delay may be visible in the interface on some networks

.

Could it this be the problem? The delay in the dns lookup? Or do I need to enhance my code as well?

When I initialize the app I call this

self.hostReach = [Reachability reachabilityWithHostName: @"www.apple.com"];

If I use an IP address like this is correct?

self.hostReach = [Reachability reachabilityWithHostName: @"1.2.3.4"];

Is it safe to use a public IP? eg "17.178.96.59" is the result of an nslookup for apple.com

There is a method in the Reachability class that seems to be used from the Apple's demo.

- (BOOL)connectionRequired
{
    NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
    SCNetworkReachabilityFlags flags;

    if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))
    {
        return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
    }

    return NO;
}

Why the connectionRequired is needed? Can be used to resolved the problem?

like image 958
cateof Avatar asked Feb 25 '16 14:02

cateof


2 Answers

To preserve battery charge, iOS will close down the networking hardware when it's not being actively used. This means turning off the WiFi and cellular radios. In this situation, Reachability isn't going to be able to report a complete result, because it can't check the situation without turning everything back on again. That is what kSCNetworkReachabilityFlagsConnectionRequired is for -- to tell you that you need to make a connection to wake the hardware back up.

What you are seeing is probably something waking up when the phone is unlocked (your app or some other app with background permissions) and so everything wakes up, you see an error immediately, but then WiFi connects and you are connected again.

You need to handle Reachability as if it can tell you "definitely reachable" or "definitely not reachable" but also "situation unknown". You need to decide what you're going to do in the unknown situation. If you make a network connection immediately then you will drain the battery faster than normal. An alternative might be just to wait for the network to be woken up for some other reason. That's really up to you.

like image 181
Ewan Mellor Avatar answered Oct 01 '22 19:10

Ewan Mellor


Reachability should be created with a host name, not an explicit address. The whole point of the DNSsystem is that addresses change for hosts sometimes. Linking directly to a name server should provide some security in this regard, but that isn't how it's supposed to work.

Reachability is often a best guess, not a hard and fast. The only way to be sure is to actually try. Connection required is related to this, because it's the device saying 'everything looks ok, I just haven't tried to connect for real'.

So, arguably you should fire the keep alive without checking the reachability status and use the result of the request to decide if there's an error or not. If you need to be sure, send an actual request and review the actual result.

like image 21
Wain Avatar answered Oct 01 '22 19:10

Wain