views:

3108

answers:

4

I'm currently using the following to check whether Wi-Fi is available for my application:

#import <SystemConfiguration/SystemConfiguration.h>
static inline BOOL addressReachable(const struct sockaddr_in *hostAddress);

BOOL localWiFiAvailable()
{
    struct sockaddr_in localWifiAddress;
    bzero(&localWifiAddress, sizeof(localWifiAddress));
    localWifiAddress.sin_len = sizeof(localWifiAddress);
    localWifiAddress.sin_family = AF_INET;
    // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
    localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);

    return addressReachable(&localWifiAddress);
}

static inline BOOL addressReachable(const struct sockaddr_in *hostAddress)
{
    const SCNetworkReachabilityRef target =
          SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault,
                                                 (const struct sockaddr *)hostAddress);
    if (target != NULL)
    {
        SCNetworkReachabilityFlags flags = 0;
        const BOOL reachable = SCNetworkReachabilityGetFlags(target, &flags);
        CFRelease(target);
        return reachable && (flags & kSCNetworkFlagsReachable);
    }
    return NO;
}

This, however, does not return NO as it should when the iPhone is connected only to a cellular network but not a Wi-Fi network. Does anyone know how to fix this?

Edit

So this is what I ended up using:

#import <arpa/inet.h> // For AF_INET, etc.
#import <ifaddrs.h> // For getifaddrs()
#import <net/if.h> // For IFF_LOOPBACK

BOOL localWiFiAvailable()
{
    struct ifaddrs *addresses;
    struct ifaddrs *cursor;
    BOOL wiFiAvailable = NO;
    if (getifaddrs(&addresses) != 0) return NO;

    cursor = addresses;
    while (cursor != NULL) {
     if (cursor -> ifa_addr -> sa_family == AF_INET
      && !(cursor -> ifa_flags & IFF_LOOPBACK)) // Ignore the loopback address
     {
      // Check for WiFi adapter
      if (strcmp(cursor -> ifa_name, "en0") == 0) {
       wiFiAvailable = YES;
       break;
      }
     }
     cursor = cursor -> ifa_next;
    }

    freeifaddrs(addresses);
    return wiFiAvailable;
}

Thanks "unforgiven" (and Matt Brown apparently).

A: 

Check out the Reachability class provided by Apple. This allows you to check what connectivity the device currently has, between Wifi, cellular or none. You can even register for notifications when the connectivity changes.

Unfortunately, Google is down for me at the moment, but Googling "iPhone reachability" will get you what you need.

iKenndac
+3  A: 

See this link: http://developer.apple.com/iphone/library/samplecode/Reachability/

You have to be registered developer in order to download the code sample. Also and it's important! Reachability API works for 3.0+ SDKs, it crashes for lower versions.

BTW, by new HIG all applications that rely on WiFi connection have to alert user on its absence, i.e if the device is not connected to Wifi, let user know.

Nava Carmon
+5  A: 

First, modify your addressReachable method. Instead of

return reachable && (flags & kSCNetworkFlagsReachable);

add the following:

BOOL isReachable = ((flags & kSCNetworkFlagsReachable) != 0);
BOOL needsConnection = ((flags & kSCNetworkFlagsConnectionRequired) != 0);
return (isReachable && !needsConnection) ? YES : NO;

This is the proper way to check for a connection available. Now, if you want to clearly distinguish between cellular and wifi, modify your method to return an int and use the following

BOOL isReachable = ((flags & kSCNetworkFlagsReachable) != 0);
BOOL needsConnection = ((flags & kSCNetworkFlagsConnectionRequired) != 0);

if(isReachable && !needsConnection) // connection is available 
{

   // determine what type of connection is available
   BOOL isCellularConnection = ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0);
   NSString *wifiIPAddress = [self getWiFiIPAddress];

   if(isCellularConnection) 
        return 1; // cellular connection available

   if(wifiIPAddress)
       return 2; // wifi connection available
}
else
   return 0; // no connection at all

The getWiFiIPAddress method is courtesy of Matt Brown, and can be found here.

One more thing. The kSCNetworkReachabilityFlagsIsDirect flag can tell you whether the network traffic goes through a gateway or arrives directly. This may be helpful in some cases.

The code works correctly on the device. On the simulator, it will declare that you are connected trough wifi when you are connected through the ethernet cable, and will declare no connection if you are connected through wifi.

unforgiven
A: 

THat is a very tidy solution, thanks. I wish there was a similar way to find out of BLUETOOTH is available. As far as I know, there is really no way to do that.

Does anyone know of a way to determine if Bluetooth is available?

#import <arpa/inet.h> // For AF_INET, etc.
#import <ifaddrs.h> // For getifaddrs()
#import <net/if.h> // For IFF_LOOPBACK

BOOL localWiFiAvailable()
{
    struct ifaddrs *addresses;
    struct ifaddrs *cursor;
    BOOL wiFiAvailable = NO;
    if (getifaddrs(&addresses) != 0) return NO;

    cursor = addresses;
    while (cursor != NULL) {
        if (cursor -> ifa_addr -> sa_family == AF_INET
                && !(cursor -> ifa_flags & IFF_LOOPBACK)) // Ignore the loopback address
        {
                // Check for WiFi adapter
                if (strcmp(cursor -> ifa_name, "en0") == 0) {
                        wiFiAvailable = YES;
                        break;
                }
        }
        cursor = cursor -> ifa_next;
    }

    freeifaddrs(addresses);
    return wiFiAvailable;
}
Joe Blow