Advertisement
iOS SDK

iOS SDK: Detecting Network Changes with Reachability

by

Most mobile applications access the web for some reason or another. This implies that these applications will - or should - behave differently when no network connection is available. In this quick tip, I will show you how to detect network interface changes using Tony Million's Reachability class.


Solutions

Even though Apple provides sample code for monitoring changes in network availability, Apple's Reachability class is a bit outdated and doesn't support ARC (Automatic Reference Counting). If you use AFNetworking, then you may want to consider AFHTTPClient, which also lets you monitor network interface changes.

However, my preferred solution is the wonderful Reachability class created and maintained by Tony Million. It supports ARC and leverages GCD (Grand Central Dispatch). Let me show you how to integrate Tony's Reachability class.


1. Setting Up the Project

Create a new project in Xcode by selecting the Single View Application template from the list of templates (figure 1). Name your application Reachability, enter a company identifier, set iPhone for the device family, and check Use Automatic Reference Counting. The rest of the checkboxes can be left unchecked for this project (figure 2). Tell Xcode where you want to save the project and click Create.

iOS Quick Tip: Detecting Network Changes with Reachability - Choosing a Project Template
Figure 1: Choosing a Project Template
iOS Quick Tip: Detecting Network Changes with Reachability - Configuring the Project
Figure 2: Configuring the Project

2. Integrating Reachability

Step 1: Adding the Reachability Class

Integrating Tony Million's Reachability class is trivial. Visit the project's GitHub page, download the latest version, and drag Reachability.h/.m into your Xcode project (figure 3). If you take this path, make sure to copy the class files into your Xcode project (figure 4). You can also use CocoaPods to to add Reachability to your project.

iOS Quick Tip: Detecting Network Changes with Reachability - Adding the Reachability Class to Your Project
Figure 3: Adding the Reachability Class to Your Project
iOS Quick Tip: Detecting Network Changes with Reachability - Make Sure to Copy the Class Files into Your Project
Figure 4: Make Sure to Copy the Class Files into Your Project

Step 2: Linking Against the System Configuration Framework

The Reachability class depends on the System Configuration framework for some of its functionality. With your project selected in the Project Navigator, select the Reachability target in the list of targets, open the Build Phases tab, and expand the Link Binary with Libraries drawer. Click the plus button and search for SystemConfiguration.framework (figure 5).

iOS Quick Tip: Detecting Network Changes with Reachability - Link Your Project Against the System Configuration Framework
Figure 5: Link Your Project Against the System Configuration Framework

3. Reachability Basics

The Reachability class provides two ways to monitor changes in network availability: blocks and notifications. Let me show you how this works.

Step 1: Importing Reachability.h

To keep things simple, we will add the reachability logic to the application delegate. Start by adding an import statement for Reachability.h to MTAppDelegate.m as shown below.

#import "MTAppDelegate.h"

#import "Reachability.h"
#import "MTViewController.h"

Step 2: Blocks

We first need to create an instance of the Reachability class in application:didFinishLaunchingWithOptions: (MTAppDelegate.m) as shown below. We then set the reachableBlock and unreachableBlock on the Reachability instance. The reachableBlock is invoked when the network availability changes from unreachable to reachable. The reverse is true for the unreachableBlock. It is key to send the Reachability instance a message of startNotifier so that it knows that it should start monitoring for reachability changes. It is also important to know that the call to startNotifier causes the Reachability instance to retain itself, which means that you don't need to store a reference to the Reachability object. However, I am not a fan of this approach and will show you an alternative solution a bit later.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Initialize Reachability
    Reachability *reachability = [Reachability reachabilityWithHostname:@"www.google.com"];

    reachability.reachableBlock = ^(Reachability *reachability) {
        NSLog(@"Network is reachable.");
    };

    reachability.unreachableBlock = ^(Reachability *reachability) {
        NSLog(@"Network is unreachable.");
    };

	// Start Monitoring
    [reachability startNotifier];

    // Initialize View Controller
    self.viewController = [[MTViewController alloc] initWithNibName:@"MTViewController" bundle:nil];

    // Initialize Window
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // Configure Window
    [self.window setRootViewController:self.viewController];
    [self.window makeKeyAndVisible];

    return YES;
}

Before we take a look at notifications, I need to stress that the reachable and unreachable blocks are invoked on a background thread. Keep this in mind when you need to update your application's user interface when the network interface changes.

Step 3: Notifications

The advantage of using notifications for reachability changes is that any object in your application can register itself as an observer for these notifications. In contrast to the use of blocks, the reachability notifications are posted and delivered on the main thread. As you can see below, in application:didFinishLaunchingWithOptions:, we create an instance of the Reachability class and tell it to start monitoring for network changes.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Initialize Reachability
    Reachability *reachability = [Reachability reachabilityWithHostname:@"www.google.com"];

	// Start Monitoring
    [reachability startNotifier];

    // Initialize View Controller
    self.viewController = [[MTViewController alloc] initWithNibName:@"MTViewController" bundle:nil];

    // Initialize Window
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // Configure Window
    [self.window setRootViewController:self.viewController];
    [self.window makeKeyAndVisible];

    return YES;
}

As an example, if the view controller needs to be informed of changes in network availability, then we need to add it as an observer of the notifications the Reachability instance posts. Open MTViewController.m, add an import statement for Reachability.h, and update initWithNibName:bundle: as shown below.

#import "MTViewController.h"

#import "Reachability.h"
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {
        // Add Observer
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityDidChange:) name:kReachabilityChangedNotification object:nil];
    }

    return self;
}

Every time the network interface changes, reachabilityDidChange: is invoked and the view controller can respond appropriately. The object property of the notification is the Reachability instance that posted the notification. The Reachability class provides a number of useful instance methods, such as isReachable, isReachableViaWWAN, and isReachableViaWiFi. You can even tell the Reachability instance whether it should consider WWAN as unreachable by setting the reachableOnWWAN property accordingly.

- (void)reachabilityDidChange:(NSNotification *)notification {
    Reachability *reachability = (Reachability *)[notification object];

    if ([reachability isReachable]) {
        NSLog(@"Reachable");
    } else {
        NSLog(@"Unreachable");
    }
}

4. Reachability Manager

For applications that require a network connection, I usually use an alternative approach for managing reachability. I create a separate class named ReachabilityManager that adopts the singleton pattern. The singleton object manages a Reachability instance and provides a number of useful class methods. Let me walk you through the internals of this class.

Step 1: Interface

As I mentioned, the MTReachabilityManager class adopts the singleton pattern and it provides access to the singleton object through the sharedManager class method. This is useful if an object needs direct access to the reachability instance that the singleton object manages.

Through a number of class methods objects can ask the reachability manager about the current network interface. In addition, objects can still add themselves as observers for the kReachabilityChangedNotification notifications as we saw earlier.

#import <Foundation/Foundation.h>

@class Reachability;

@interface MTReachabilityManager : NSObject

@property (strong, nonatomic) Reachability *reachability;

#pragma mark -
#pragma mark Shared Manager
+ (MTReachabilityManager *)sharedManager;

#pragma mark -
#pragma mark Class Methods
+ (BOOL)isReachable;
+ (BOOL)isUnreachable;
+ (BOOL)isReachableViaWWAN;
+ (BOOL)isReachableViaWiFi;

@end

Step 2: Implementation

The implementation of MTReachabilityManager isn't too surprising. If you are unfamiliar with the singleton pattern, then the implementation of sharedManager might seem a bit odd. Even though the MTReachabilityManager class makes use of the singleton pattern, it is technically possible to create instances of the class. We could prevent this by checking the value of _sharedManager in the init, but I assume that whoever uses the MTReachabilityManager class has taken a look at the class's interface file revealing that it adopts the singleton pattern.

#import "MTReachabilityManager.h"

#import "Reachability.h"

@implementation MTReachabilityManager

#pragma mark -
#pragma mark Default Manager
+ (MTReachabilityManager *)sharedManager {
    static MTReachabilityManager *_sharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedManager = [[self alloc] init];
    });

    return _sharedManager;
}

#pragma mark -
#pragma mark Memory Management
- (void)dealloc {
    // Stop Notifier
    if (_reachability) {
        [_reachability stopNotifier];
    }
}

#pragma mark -
#pragma mark Class Methods
+ (BOOL)isReachable {
    return [[[MTReachabilityManager sharedManager] reachability] isReachable];
}

+ (BOOL)isUnreachable {
    return ![[[MTReachabilityManager sharedManager] reachability] isReachable];
}

+ (BOOL)isReachableViaWWAN {
    return [[[MTReachabilityManager sharedManager] reachability] isReachableViaWWAN];
}

+ (BOOL)isReachableViaWiFi {
    return [[[MTReachabilityManager sharedManager] reachability] isReachableViaWiFi];
}

#pragma mark -
#pragma mark Private Initialization
- (id)init {
    self = [super init];

    if (self) {
        // Initialize Reachability
        self.reachability = [Reachability reachabilityWithHostname:@"www.google.com"];

        // Start Monitoring
        [self.reachability startNotifier];
    }

    return self;
}

@end

If you decide to adopt this alternative approach and use a reachability manager that manages an instance of the Reachability class, then don't forget to instantiate the singleton object when your application launches. You can do so by calling sharedManager on the MTReachabilityManager class.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Instantiate Shared Manager
    [MTReachabilityManager sharedManager];

    // Initialize View Controller
    self.viewController = [[MTViewController alloc] initWithNibName:@"MTViewController" bundle:nil];

    // Initialize Window
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // Configure Window
    [self.window setRootViewController:self.viewController];
    [self.window makeKeyAndVisible];

    return YES;
}

Conclusion

Notifying the user or updating the application's user interface when the network interface changes not only results in a better user experience, Apple requires that you do this if your application relies on network connectivity. It does take some effort, but the Reachability class makes it a lot easier.

Related Posts