Do you remember the 90s? Getting an account with the local ISP, configuring dial up phone numbers and modem connection strings? Every five minutes hearing this

…as your email client dials the modem to check your email? Today we no longer hear the sounds of modem handshakes or busy signals when we want to use the internet. Networks are generally faster, and more ubiquitous. We take the availability of the network for granted - until we see it break.

The reality is that not much has changed since the 90s. Networks are still unreliable, and if anything have gotten far more complex. Most devices have multiple network interfaces (WiFi, Cellular, etc.) and multiple ways to fail.

The System Configuration APIs were designed to handle these kinds of scenarios, and evolved to perform similar functions on the lossy, unreliable wireless networks of today (and tomorrow).

Background

The user may need to perform an action to allow or create a connection - such as connecting to a wireless network. This is not unlike having to dial a modem to check email. There may be a connection, but it may not be able to connect to certain hosts - somewhat like using online services in the 1990s or your favorite cable provider today. Wireless signals may become weak or unusable and drop entirely - like a parent picking up the telephone while your modem is connected to the local ISP.

What does reachability do for you?

The problem that SCNetworkReachability solves is variable network interface availability. It can tell you when the network configuration changes such that packets sent from the device may reach a given host.

It can’t tell you wether that host is up or down, or if network conditions outside of the device may or may not allow a connection. It can only tell you wether it is or is not possible for packets to leave the device to begin their journey to that host.

The device may have a VPN configuration that does, or does not, allow a WiFi connection to the host.

The current network configuration may require the user to select a wireless network in order to connect to the host. This is very common with iPods and iPads.

The cellular and WiFi radios may be off or in Airplane Mode.

Parental control, VPN, or managed device restrictions may not allow certain hosts to be reachable unless the device is connected to specific networks.

Many applications do not take these kinds of scenarios into account. For example, an application may be designed to assume that if a network connection is available all possible hosts may be available - which is not a safe assumption to make.

Correctly using reachability

The recommended best practice when using reachability is to first attempt a connection. If the connection fails because the host is unreachable, register to be notified of reachability changes for that host. Network reachability should be used to inform the user rather than to “preflight” or “precheck” before attempting a connection.

Attempt to connect to a host.

If the connection has no NSURLResponse it is safe to check the NSError returned.

it is safe to check the returned. If the NSError has the domain NSURLErrorDomain and an error code that indicates a network interface availability problem, register to receive callbacks when the network reachability changes.

There are only a few error codes that indicate a problem with network interface availability:

NSURLErrorNotConnectedToInternet Returned when a network resource was requested, but an internet connection is not established and cannot be established automatically, either through a lack of connectivity, or by the user’s choice not to make a network connection automatically. NSURLErrorInternationalRoamingOff Returned when a connection would require activating a data context while roaming, but international roaming is disabled. NSURLErrorCallIsActive Returned when a connection is attempted while a phone call is active on a network that does not support simultaneous phone and data communication (EDGE or GPRS). NSURLErrorDataNotAllowed Returned when the cellular network disallows a connection.

NSURLErrorNotConnectedToInternet is the most obvious, and applies to the most application use cases. The others are a little less obvious, and your application may not need to handle all of these.

Implementing it in your application

Let’s look at how to use this in practice. As discussed above, the first step is to attempt the network connection as you would normally. Reachability comes into play when the network connection fails and returns an error with a relevant error domain and code - so your use of SCNetworkReachability should begin in your error handling:

- (void) presentError:(NSError *)error { if ([[error domain] isEqualToString:NSURLErrorDomain]){ NSURL *failingURL = [[error userInfo] valueForKey:NSURLErrorFailingURLErrorKey]; switch ([error code]){ case NSURLErrorInternationalRoamingOff: case NSURLErrorCallIsActive: NSURLErrorDataNotAllowed: case NSURLErrorNotConnectedToInternet: [self beginObservingReachabilityStatusForHost:[failingURL host]]; break; default: break; } } return; }

When the error is relevant to network reachability you will need the host, which is part of the URL returned in the error’s userInfo dictionary. Our beginObservingReachabilityStatusForHost: will use SCNetworkReachability to monitor the network configuration for reachability changes:

- (void) beginObservingReachabilityStatusForHost:(NSString *)host { SCNetworkReachabilityRef reachabilityRef = NULL; void (^callbackBlock)(SCNetworkReachabilityFlags) = ^(SCNetworkReachabilityFlags flags) { BOOL reachable = (flags & kSCNetworkReachabilityFlagsReachable) != 0; [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [self host:host didBecomeReachable:reachable]; }]; }; SCNetworkReachabilityContext context = { .version = 0, .info = (void *)CFBridgingRetain(callbackBlock), .release = CFRelease }; if ([host length] > 0){ reachabilityRef = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [host UTF8String]); if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)){ if (!SCNetworkReachabilitySetDispatchQueue(reachabilityRef, [self scNetworkQueue]) ){ // Remove our callback if we can’t use the queue SCNetworkReachabilitySetCallback(reachabilityRef, NULL, NULL); } [self setCurrentReachability:reachabilityRef]; } } } static void ReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkConnectionFlags flags, void* info) { void (^callbackBlock)(SCNetworkReachabilityFlags) = (__bridge id)info; callbackBlock(flags); }

We register the C function ReachabilityCallback as the callback for reachability changes, and within that we invoke a block. Using a private serial queue we start monitoring SCNetworkReachability changes for the host that failed. Using a private queue is important, as to monitor the host reachability SCNetworkReachability will have to perform a DNS query at some point, and that may still fail by timing out. This is not something that should be done on the main queue!

The block invoked by the callback calls a method that will update our user interface to indicate the host is offline. As described in the documentation, sample code, and WWDC sessions, reachability should be used to ‘hint’ the user interface. Inform the user that certain actions cannot be performed while the host is unavailable. In this case, we are disabling a button based on the reachability status:

- (void) host:(NSString *)__unused host didBecomeReachable:(BOOL)reachable { __strong UIButton *button = [self connectButton]; // Enable the button when we are notified the host became reachable [button setEnabled:reachable]; if (reachable){ [self endObservingReachabilityStatusForHost:nil]; } }

And it is pretty much that simple. You should implement the equivalent of the endObservingReachabilityStatusForHost: to stop observing reachability changes at some point. It’s important to remember that reachability status can be unreliable and slow - do not expect it to be “instantaneous”. Nothing involving network communication should ever make that assumption!

You can download a simple sample project here: SimpleOffline

Further Reading

Determining Reachability and Getting Connected

Networking Overview: Designing For Variable Network Interface Availability

Network Apps for iPhone OS, Part 2