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.
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.
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).
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.
Comments