In TMI, Michael Helmbrecht gives overly detailed answers to popular mobile development questions.
Ever browsed the web for help, only to find the popular answers leave you hungry for more? In our third episode, Michael demos the solution to the popular iOS development question: how do you check for an active internet connection? He details how to use Apple’s Reachability class to check for a connection, and discusses other solutions and best practices.
Hi! I am Michael from Realm. I am on TMI to answer your Internet questions about iOS mobile development. Today we will discuss how you can check whether a user’s device has an Internet connection, and which type of Internet connection do they have (e.g. cellular or Wi-Fi). You might want to use this to: 1) assess whether they are offline and unable to access the Internet, or 2) to differentiate between cellular data and Wi-Fi. For example, if you are doing a data intensive task, you might want to wait until they are on Wi-Fi and not use up all their cell data.
There is no one line piece of code that will just tell you whether the user is online or not. There are different options for libraries that will help you do this, and today we will focus on Apple’s own code called Reachability. Reachability is an Objective-C class that can also be used with Swift that wraps network connectivity logic in a way that’s easy to use.
Quick code walkthrough: open Xcode, create a new project, pick single view application, and call it Reachability Demo (ensure it is an Objective-C project). To start, we will have to bring in Apple’s code for the Reachability class. I will bring in Reachability.h and .m by dragging them in. In this demo, we are going to log the status updates we get for different network connectivity types that can be assessed. We will start this with #import "Reachability.h"
.
We then declare three reachability objects:
@property (nonatomic) Reachability *hostReachability;
@property (nonatomic) Reachability *internetReachability;
@property (nonatomic) Reachability *wifiReachability;
host
will check for a specific host online. In this case, we are using apple.com, but this can be great if you are only using one domain. If you are using your own web service, it really is very specific to that - make sure not only that your device can access the general Internet, but also that it can access that particular host.internet
checks that you can access some sense of the Internet. It’s still more powerful to use a specific host name, but this also an option.wifi
checks local Wi-Fi, and possibly talking to other devices on the same network.
- (void)viewDidLoad {
[super viewDidLoad];
self.hostReachability = [Reachability reachabilityWithHostName:@"www.apple.com"];
[self.hostReachability startNotifier];
self.internetReachability = [Reachability reachabilityForInternetConnection];
[self.internetReachability startNotifier];
self.wifiReachability = [Reachability reachabilityForLocalWiFi];
[self.wifiReachability startNotifier];
}
For each of these, we are using a constructor. Once we have these objects, we are using startNotifier
. The way that the Reachability class works is that it posts notifications, and you listen to them so you can react (this is some basic notification center action).
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachiabilityChangedNotification object: nil];
To listen to such notifications, we add ourselves as an observer, and give the notification a selector: -reachabilityChanged:
. kReachiabilityChangedNotification
is a constant that is defined in Reachability.h
, so we know what notifications to listen to. Next, let’s define the -reachabilityChanged:
method:
- (void)reachabilityChanged:(NSNotification *)notification {
Reachability *reachability = [notification object];
[self logReachability: reachability];
}
As with all notification center listeners, we need a method that takes an NSNotification
and returns a void. Because of the way that Apple has written the Reachability class, the reachability object that encapsulates all the information is easily retrievable from the notification. If you had more complex logic, you might want to send it to some other method that could change the user interface and notify the user via some other method. Next we will define -logReachability:
:
- (void)logReachability:(Reachability *)reachability {
NSString *whichReachabilityString = nil;
if (reachability == self.hostReachability) {
whichReachabilityString = @"www.apple.com";
} else if (reachability == self.internetReachability) {
whichReachabilityString = @"The Internet";
} else if (reachability == self.wifiReachability) {
whichReachabilityString = @"Local Wi-Fi";
}
...
}
There are different pieces of information that each of these reachability objects will give us. The first part: reachability type (since all three of them are posting the same notification and will trigger the same piece of code). We need to be able to differentiate - we named them before (host, internet and wifi reachability), and all we are doing is checking which one it is. Since were logging, I am going to store it in a string, whether we can reach apple.com, the Internet at large or local Wi-Fi.
Now we will look at how are we able to reach it:
...
NSString *howReachableString = nil;
switch (reachability.currentReachabilityStatus) {
case NotReachable: {
howReachableString = @"not reachable";
break;
}
case ReachableViaWWAN: {
howReachableString = @"reachable by cellular data";
break;
}
case ReachableViaWifi: {
howReachableString = @"reachable by Wi-Fi";
break;
}
}
...
}
The Reachability object has the currentReachabilityStatus
property, with three options: NotReachable
, ReacheableViaWWAN
(the wireless wide area network; cellular data), ReachableViaWiFi
(Wi-Fi). There is actually one more piece of information that this class gives us which can be really informative. It has this property (a boolean) called connectionRequired
, which will tell if there will be further work required to establish the connections. For example, for cellular data, if you have to establish a connection before you can start to get data from the network; or, using Wi-Fi, if you might be using a VPN and have to connect VPN before you can actually start transmitting data. If we need to establish some further connection, we will just note that in our log. If not, we will just leave the string black (since we have nothing to report).
We will finish up by logging with a format string:
NSLog(@"%@ %@%@", whichReachabilityString, howReachableString, connectionRequiredString);
When we run this, we can see a few different lines: the first one, Reachability Flag Status (part of Apple code, with some internal logging that you can optionally turn off). We have our code, “www.apple.com reachable by Wi-Fi.” You might notice we have apple.com reachable by Wi-Fi twice, but we did not get any notifications about whether the Internet at large or local Wi-Fi are reachable. These things are cached by the system so we get no notification.
To solve this, I immediately do much logic with the information when I start listening to notifiers:
- (void)viewDidLoad {
...
[self logReachailbity:self.hostReachability];
[self logReachailbity:self.internetReachability];
[self logReachailbity:self.wifiReachability];
}
As soon as I start the notifiers, I will immediately log the reachability of these three statuses. In the future, if it changes, I will be notified and I can take appropriate action. Now when I compile this, we can see that we have more where we are getting the initial status for apple.com, the Internet and local Wi-Fi are reachable by Wi-Fi, because on the simulator there is no cellular data. We get an update for apple.com, where it appears that for hosts, it will go out and recheck them for you to make sure that they are still available.
There is a third party code that you might want to use as a direct replacement for Apple’s class, also called Reachability. It works very well, but here are two issues with it: 1) it does not support CocoaPods; 2) there is an issue with frameworks and naming. There is a fork that does fix that problem, but the larger issue with it is that, because there are some Apple private frameworks that have a class called Reachability, if you use this third-party framework, some people are reporting that their App Store submissions have been rejected because the App Store thinks that you are using private frameworks. You might need to rename it to make sure that there are no naming collisions like that.
If you are more of a Swift developer, there is a Swift port of that exact third-party library called Reachability.swift.
The big frameworks also have reachability. AFNetworking (the biggest Objective-C networking framework) has a reachability component; it has a whole class, AFNetworkReachabilityManager
, that does the same thing, letting you know about notifications and status of reachability. In Alamofire, the Swift version of AFNetworking, they do not yet have a reachability component, and there’s an ongoing debate about it in this GitHub issue.
Apple’s official documentation recommends that you do not do this at all. Instead, they recommend that you use NSURLSession
to try to execute your requests. If there is an issue with reachability (or other things), it gives you an NSError object that you should use to test for connectivity. They recommend only using Reachability only as a way to diagnose errors and further debug known issue. Nevertheless, this is often applicable for displaying connection status to the user before errors start taking place.
That is how to check for an active Internet connection on iOS, and why you might not want to do it. Come back next week for more mobile development tips from me and, hopefully, Frank the Pug.
Resources
- iWasRobbed’s Answer on Stack Overflow
- Apple’s Reachability Class
- Reachability Replacement Class
- Reachability.swift
- AFNetworkingReachabilityManager
This post is licensed under a Creative Commons BY-SA 3.0 license.
Receive news and updates from Realm straight to your inbox