Skip to content

Network Reachability

If you write an iPhone application that uses the network, then you must check if the network is both operating and that your server is reachable. Apple has and will continue to reject applications that do not test reachability. To this end, Apple provides iPhone developers a sample application and class, Reachability, that can easily be used to perform these tests. While the 2.0 version of this code is much better than v1.5, it is still quite raw. (For example, there is a bare [super init]; in a class method [line 175 in Reachability.m]. It does nothing. It also has two routines with misspellings in their names: -startNotifer/-stopNotifer should be -startNotifier/-stopNotifier) To me, these are signs of raw code. To remedy this, I have significantly reengineered this class.

I had three goals when starting this process. First, I wanted to understand how to use this code. It isn’t at all apparent how these routines should be used. What order should they be called in and what do the various tests and values mean? Second, my scan of the code convinced me that it was written by a low level network engineer. They had made implementation decisions that were not terribly friendly to using it in a Cocoa Touch program. I wanted a class that fit in with my coding style. Third, because the core network testing routine -networkStatusForFlags: was not, in my opinion, clear, I did not feel comfortable shipping it in my app, weLost™. I wanted to have confidence in this code.

This class has two important features: network status transition notifications and easy reachability testing for each server your app touches. Because radios lose connections all of the time, these dynamic notifications help your user understand why they cannot use the networking features of your app.

When I start my app, the first Reachability method I use creates a Reachability instance for the network itself, +(Reachability *) reachabilityForInternetConnection;. This tests the hardware and can immediately provide you with the current network status. Because this reachability test doesn’t leave the phone, there is no network latency. Because hostname reachability instances must resolve your host’s name into an IP address, they are asynchronous. The immediately returned status is that your host is not reachable. (Bear in mind that the subsystem underlying Reachability, SCNetworkReachability, does not actually try to send a packet to your host. It only really knows the state of the radios and whether IP addresses have been allocated for the phone and that your host’s name has resolved into an IP address.)

These Reachability instances do not start notifying you of network transitions upon instantiation. You must turn this on. Before turning on the notifications, I like to register for them. The notification name in the default notification center is: kReachabilityChangedNotification. When your registered method is called, the notification’s object will be the Reachability instance with the changed status. For example, you might register this method:

-(void) networkReachabilityEvent: (NSNotification *) notification;

with the notification center. When it is called for a status change, the Reachability instance is recovered with this line of code:

Reachability *r = [notification object];

A Reachability instance’s notifications are initiated, unsurprisingly, with the -startNotifier method.

Now that you can track gross network transitions, you need to create Reachability instances for each host. In weLost’s case, I track 4 different servers. I use:

+(Reachability *) reachabilityWithHostName: (NSString*) hostName;

to create these instances. These host names must be resolved into addresses before the instance is reachable. (A notification, if you’ve turned them on for this instance, is issued when the resolution is finished.)

This class can be used both synchronously and asynchronously. Typically, you will make synchronous queries of the Reachability instance passed to you after a network status transition. You may also want to do a synchronous test before opening a new view which connects to your host. (One advantage of creating all of your Reachability instances at startup, i.e. before you need them, is that their name resolution does not delay your later network accesses.) My version of Reachability has more methods for synchronously testing network status than Apple’s. Their version also has mixed some tests, such as whether user intervention is required, into the network status method which I think should not be there. I have cracked them out into separate methods. Here are the kinds of network tests you can perform:

  • Test whether this Reachability instance is of interest.
  • If so, then test what kind of network transition it is:
    • Is the host reachable? -(BOOL) isReachable;
      • Is the connection up? -(BOOL) isConnectionRequired;
        • Will it come up automatically? -(BOOL) isConnectionOnDemand;
        • Will the user have to enter a password? -(BOOL) isInterventionRequired;
      • Is the connection slow? -(BOOL) isReachableViaWWAN;
      • Is the connection fast? -(BOOL) isReachableViaWiFi;

How do you determine if a Reachability instance is of interest? You check its key — a hostname or a dotted quad IP address or one of two constants: kInternetConnection or kLocalWiFiConnection. This is a big change from Apple’s code. I keep a key, an NSString, along with the SCNetworkReachabilityRef. Apple just keeps a boolean and the SCNetworkReachabilityRef. In other words, I make it easy to use Reachability instances in complex Cocoa data structures. I’m sure Apple’s engineer would argue that pointer comparisons are sufficient to determine one’s interest in a notification. That doesn’t mean it is a particularly useful mechanism in a Cocoa program. Also, Apple’s boolean is used just to differentiate one instance type, kLocalWiFiConnection, from all of the other types. While this is useful to the internal functioning of Apple’s code, it isn’t particularly useful to a client of the class. I provide a convenience method, -isEqual:, to compare Reachability instances. To a Cocoa Touch programmer, a key is just more flexible.

Your decision tree to interpret network status transitions is, of course, idiosyncratic to your application. However, there are issues to consider based upon your connection. After you have determined whether your host is reachable, is the network active and connected? For example, if you are connected via the EDGE/3G wireless wide area network (WWAN), your network is always reachable but it may not be active. You start the radios by trying to connect to your host. I have seen this process take tens of seconds. Does your user need to know this? If you are connected via WiFi and if a user must enter a password, you may choose not to connect to the network. If you’ve transitioned from WiFi to WWAN, does the user need to know that things are much slower or they may have lower resolution media? And the converse? Do you enable more features when the network transitions to WiFi from WWAN? And what, exactly, do you do when there is a repeated network flap between WiFi and WWAN? What do you do when your app is brought out of sleep in a new venue? What do you tell your user? You will get a notification for each of these situations.

There are two Reachability instances I have neglected — -reachabilityWithAddress: and -reachabilityForLocalWiFi. -reachabilityWithAddress: takes an internet IP/socket address and can be queried immediately for its status. Its key is a dotted quad IP address, such as @"127.0.0.1". The -reachabilityForLocalWiFi instance is a bit more subtle. It tests for a WiFi connection using an automatic private IP address, 169.254.0.0. If this network is reachable, then your network is active but not routable. Some services, such as Bonjour, will still function fine in this case but not much else. I have not found any use for this instance type. Yet I have preserved Apple’s test criteria for this instance in my code. Perhaps it is of use to you.

Before using my extensions you should probably ask: how compatible is this stack with Apple’s? My answer is: very. My code is a superset of Apple’s API. Other than Apple’s misspelled method names, my code will be a drop in replacement for Apple’s class. It has, as far as I’ve been able to test, identical behavior. My network status test is different. It is supported by empirical observations of the status flags. Because it does not conflate a test for whether a user’s intervention is required with the basic reachability, my network status test is simpler. Nonetheless, if you do not wish to use my extensions, I provide a #define DDG_EXTENSIONS to enable easy comparisons between the two network status policies in your application. Both classes employ NSAssert and NSCAssert. This is aligned with the Cocoa policy to throw exceptions during development to catch programmer errors. These exceptions can be turned off by defining NS_BLOCK_ASSERTIONS during your release builds. Finally, I support a conditionally compiled logging system. To turn it on you need to #define CLASS_DEBUG in the class header and #define DEBUG for the debug builds of your app. Then both the -description method and synchronous logging are turned on. My log entries also list the method name and line number of the source code. Class logging is turned on by default.

This class is packaged in a .zip file here: Reachability 2.0.4ddg.zip. It has two directories: Reachability itself and my version of Apple’s Reachability project. These extensions to Apple’s class are covered by the new BSD license. Hence, if you use my version, I require a public acknowledgement of my contributions in your application.

I hope you find this version of Reachability useful. May your code compile without errors and run like the wind.

NOTE: Allen Brunson found a memory leak in Reachability. This has been corrected and a new version generated, 2.0.2ddg, and placed at the link above.

NOTE: Ling Wang suggested an additional test for a WiFi+VPN condition. This has been added and a new version generated, 2.0.3ddg, and placed at the link above.

NOTE: After complaints about my added NSAssert in the -networkStatusForFlags: method, I have now surrounded the assert with a conditional compile macro. This change has been added and a new version generated, 2.0.4ddg, and placed at the link above.

28 Comments

  1. the following method from Reachability.m looks wrong. you are calling CFRelease() on reachabilityRef if it is NULL, whereas i’m pretty sure the right test would be NOT NULL.

    -(void)dealloc
    {
    [self stopNotifier];

    if (!reachabilityRef)
    {
    CFRelease(reachabilityRef);
    reachabilityRef = NULL;
    }

    self.key = nil;
    [super dealloc];
    }

    Thursday, December 31, 2009 at 2:35 am | Permalink
  2. kL wrote:

    Yeah, Reachability example code is a bad apple :)

    It sucked so much, that I decided to completely ignore it and fake reachability test – I just download whatever I need, and when it fails, I say user is not connected.

    Thursday, December 31, 2009 at 6:24 am | Permalink
  3. admin wrote:

    Allen,

    Thank you for looking at my code. You are correct. Since Reachability instances last for the length of the program, that is a part of the code that had never been tested in my app.

    Count me embarrassed and thankful that you found this memory leak. I have made the change to the code and placed the new .zip on my server.

    Also, I took the opportunity to move #define CLASS_DEBUG back into the .m file. Changing the state of conditionally compiling debugging/logging into your code should not trigger a recompile of your client code.

    Andrew

    Thursday, December 31, 2009 at 7:43 am | Permalink
  4. Julio Barros wrote:

    I’m not sure your new link is working. I can’t download the code and it just redirects back to the article.

    Thursday, December 31, 2009 at 10:49 am | Permalink
  5. admin wrote:

    Julio,

    I have fixed the link.

    Thank you,
    Andrew

    Thursday, December 31, 2009 at 11:09 am | Permalink
  6. Have you filed a radar against the Reachability example code? If you have not then you should otherwise it may not get noticed or fixed.

    Thursday, December 31, 2009 at 11:17 am | Permalink
  7. admin wrote:

    Marcus,

    At the suggestion of an Apple engineer friend of mine, I have filed a radar against Reachability. I’ve forgotten to file it with OpenRadar. I’ll go do so now.

    Andrew

    Thursday, December 31, 2009 at 11:35 am | Permalink
  8. admin wrote:

    Here is my Open Radar link: http://openradar.appspot.com/7502738

    Andrew

    Thursday, December 31, 2009 at 11:41 am | Permalink
  9. heh! glad i could be of service.

    i’m pretty sure that, if you had ever run that code, it wouldn’t just leak, it would crash. that’s the reason i mentioned CFRelease() in that comment. every time i’ve called it with a NULL pointer, it has crashed.

    Thursday, December 31, 2009 at 8:21 pm | Permalink
  10. Seunghoon Yeon wrote:

    Thank you for sharing new Reachability class. Happy New Year!

    Friday, January 1, 2010 at 6:39 pm | Permalink
  11. Jeremy wrote:

    I assume there’s a good answer, but why do we need all this overhead to check network reachability?

    In my app, I have a method in my App Delegate that simply loads a single-character string from a php file hosted on my server (using -stringWithContentsOfURL).

    I then have a separate method in my app delegate that reads the contents of the string after two seconds as such:

    [self performSelector:@selector(checkConnectivity) withObject: nil afterDelay: 2.0];

    There’s no way it should take two seconds to read a single character from my server. If the string is NULL, I assume we’re not connected.

    Is this a bad strategy, and if so, why?

    Sunday, January 3, 2010 at 11:11 pm | Permalink
  12. admin wrote:

    Jeremy,

    It isn’t a bad strategy. It is different than what Apple’s underlying library provides. You actually get better info on the real reachability of your host with your method. But you have to keep polling. (This has a side benefit of actually turing on the radios and bringing up your network connection. Reachability does one other thing and that is kick off DNS resolution for a hostname.)

    All Reachability does is tell you when the network hardware in the phone detects a state change. This may or may not be useful to your user. Only your app can tell. As I understand it, Apple does test interrupting the network connection of your app. They expect your app to respond intelligently and, if necessary, notify your user.

    My extensions to Reachability are about making your decision tree, in response to that state change, simpler.

    Andrew

    Monday, January 4, 2010 at 12:02 am | Permalink
  13. Nathan wrote:

    Is this something that should be posted to github (or another code hosting site) so that other’s can contribute easily?

    I think a standard Reachability library would be very helpful to everyone.

    Monday, January 4, 2010 at 3:05 pm | Permalink
  14. admin wrote:

    Nathan,

    As this is derived from Apple’s code and it fixes multiple bugs, this is probably already the standard Reachability library. How it is hosted is somewhat immaterial. That said, I’ve been considering posting it somewhere. I haven’t yet decided where. Until then, the source code is available for anyone to modify under a liberal license.

    For those who wish to return their changes to a common code base, I am happy to integrate their changes and produce a new version.

    Andrew

    Monday, January 4, 2010 at 4:49 pm | Permalink
  15. Ling Wang wrote:

    Thanks for your code which is used in my GTD/todo app Voodo.(I’ve referred to this page in the blog.)

    I’v found a bug when connecting via WiFi using VPN(PPTP, what I’m using).

    You didn’t consider WiFi+VPN connection(-R -t—–) reachable, but it seems you should.

    Here is the patch:
    // VPN WiFi: Reachability Flag Status: -R -t—– Reachable.
    if (flags & kSCNetworkReachabilityFlagsTransientConnection) { return kReachableViaWiFi; }

    I added it below the case of “-R cxxxxxx” in networkStatusForFlags:.

    Thursday, January 28, 2010 at 6:21 am | Permalink
  16. admin wrote:

    Ling,

    Good catch! I’ve incorporated your code but higher in the decision tree. I’ve also added an assert to guard the final return in the decision tree. This logs any cases we are not catching.

    I’ve placed Reachability v2.0.3ddg up on the blog.

    Andrew

    Thursday, January 28, 2010 at 9:06 am | Permalink
  17. Glen Byram wrote:

    I incorporated the Reachability class into my project. I kept getting a compile error for _readabilityFlags_ (symbol not found.
    Unfortunately I am only just learning the details of developing in c and variants. It took me ages to get rid of the error (eventually setting DEBUG=1 on the project worked.

    My question is, shouldn’t the code compile without errors if the DEBUG option is not defined. (Apologies in advance if this is a silly question).
    Thanks for the code though – excellent.

    Sunday, February 7, 2010 at 2:33 pm | Permalink
  18. admin wrote:

    Glen,

    I’m glad you’re using Reachability.

    One comment, there is no symbol _readabilityFlags_ in the code.

    There is the following function:
    static NSString *reachabilityFlags_(SCNetworkReachabilityFlags flags);

    As to your problem:

    The below recommendation from the header file is now more than a recommendation.

    #define USE_DDG_EXTENSIONS 1 // Use DDG’s Extensions to test network criteria.
    // Since NSAssert and NSCAssert are used in this code,
    // I recommend you set NS_BLOCK_ASSERTIONS=1 in the release versions of your projects.

    For Mr. Wang’s fix I added an NSAssert to catch any missed Reachability tests during development. As is the Cocoa convention, those asserts/exceptions need to be turned off for release. As a programming community, I think we have a choice. Either make code trivial to compile for beginners or document code such that beginners can learn the Cocoa conventions. I myself fall into the latter camp. In particular, with networking code, for which there are no trivial examples, I think we need to focus on requiring Cocoa beginners to learn and follow the Cocoa conventions.

    How do you learn those conventions? By asking on venues like this one.

    Hence, I would rather not fix this non-problem. I would rather help folks like you learn to use the facilities of the framework.

    What do other readers of this thread think? Should I generate another version of Reachability with this assert removed?

    Andrew

    Sunday, February 7, 2010 at 3:33 pm | Permalink
  19. RealTimed wrote:

    Hi Andrew,

    Thanks for your code. I have not clear what do you mean with the following:

    The below recommendation from the header file is now more than a recommendation.

    #define USE_DDG_EXTENSIONS 1 // Use DDG’s Extensions to test network criteria.
    // Since NSAssert and NSCAssert are used in this code,
    // I recommend you set NS_BLOCK_ASSERTIONS=1 in the release versions of your projects.

    I also got the error that Glen got, and could solve it by setting USE_DDG_EXTENSIONS to 0, although I don’t know if this is correct.

    Thanks a lot, and I look forward your help on this.

    Monday, February 15, 2010 at 9:32 am | Permalink
  20. admin wrote:

    OK, OK, OK.

    I’ll conditionally compile out the new NSAssert. If you don’t have DEBUG set, then the new NSAssert will not appear in the code.

    I recommend that you leave USE_DDG_EXTENSIONS to 1. If you change it to 0, then you get Apple’s behavior. That may be what you want. (Though, by using my version of Reachability and by your comments, I suspect not.)

    In answer to your question about my recommendation. Both Apple’s and my version of Reachability use NSAssert and NSCAssert. These macros throw exceptions if the conditions for proper execution are not met. This is a bad behavior in code you ship to your customers.

    Hence, to turn this behavior off, you need to learn how to set compile time variables in Xcode. (There are many, better discussions than I can author about setting these variables on the web.)

    In debug builds, I recommend that you set DEBUG=1. In release, ad hoc and App Store versions of your code, I recommend that you set NS_BLOCK_ASSERTIONS=1 and remove the DEBUG setting.

    Andrew

    Monday, February 15, 2010 at 10:14 am | Permalink
  21. Lutz wrote:

    I got a question about timeouts – What happens when a host is initially available but then the WWAN data connection drops out? The code doesn’t seem to catch that, and I don’t get notification about the change in reachability. You can test yourself with SBSettings (toggle data off and on) on a JB phone (I do that to emulate non-roaming)

    Saturday, May 8, 2010 at 2:14 pm | Permalink
  22. admin wrote:

    Lutz,

    I do not have a jailbroken phone. Nor am I going to jailbreak one of my phones to track this down. I am not sure what you mean by SBSettings?

    That said, the Reachability class is a thin Obj-C wrapper around Apple’s C network reachability API, SCNetworkReachability. The callback originates within Apple’s stack. All the Reachability class does is convert the callback into a Cocoa notification. You can satisfy yourself about my statements by looking at the implementations of: ReachabilityCallback(), -startNotifier and -stopNotifier. All three routines are quite simple (and other than some cosmetic tweaking are still substantially identical to Apple’s code).

    As to seeing the EDGE/3G transition, I have personally observed it in my testing with this code. I have been able to force a 3G transition by wrapping the phone in aluminum foil. Hence, I respectfully submit that this problem is probably on your end and not in Reachability.

    If you would care to share more information about your problem on an unbroken phone, then I am happy to help.

    Andrew

    Saturday, May 8, 2010 at 3:49 pm | Permalink
  23. Eric wrote:

    I just started playing with your Reachability class. One thing that puzzles me is the Local WiFi behavior. If I start the app up in a connected environment, all 3 fields say “Reachable WiFi”. Then if I kill the network connection, all 3 fields become “Access Not Available”.

    This all makes sense, but finally, if I reconnect the network connection, the first two fields become “Reachable WiFi” but the last field remains at “Access Not Available”. This happens in both the simulator and device for me (tested 3.1.3, 3.1.2).

    Is this expected behavior?

    Friday, May 28, 2010 at 4:43 pm | Permalink
  24. admin wrote:

    Eric,

    The local wi-fi connection is curious. It really is a way to determine if you have a self assigned IP address.

    I haven’t used Apple’s example code in about 9 months. Hence, I suspect you’re running in to a bug in their example.

    Andrew

    Friday, May 28, 2010 at 6:26 pm | Permalink
  25. Dan Zeitman wrote:

    Awesome work, clear explanation of Apples work, and how you addressed the pitfalls.

    I’d love it if you could address their Lazy Table image example
    – Apple example has the guts in the app delegate – real world use is within a view controller. The problem becomes handling view changes, if an image is in the middle of a download, while changing to another view you will crash, since the tableview is no longer there.

    Overall it’s clear we need more thoughtful examples of dealing with networking tasks such as lazy loading images, smart caching, paging data, etc. I’m baffled why it’s so hard to find clear examples, considering we are dealing with a network device.

    Sunday, July 11, 2010 at 9:35 am | Permalink
  26. admin wrote:

    Dan,

    Thank you for your kind comments.

    While your suggestions are interesting, I tend to focus upon open sourcing code I use that has direct applicability to shipping code. Apple’s sample code does not meet those criteria. (Particularly when Apple has admitted to me that they can never adopt outside code.)

    That said, I have approached a publisher to write an advanced book on asynchronous and network programming. (Blocks, threads and all that …) Hence, I want to focus on more complete solution than just a series of blog posts. The publisher may allow me to blog chapters but I’m not so sure.

    BTW, I am putting the finishing touches on Reachability v2.1.0ddg. At WWDC I spoke with the engineer who wrote Apple’s Reachability class. He is supportive of my efforts and we are interacting to improve my class. Primarily, I want to add IPv6 support and convert the boolean tests into properties. This makes it easy for client code use KVO to track reachability status. (Because we can’t get a call from the OS before the network state changes, Reachability does not fit that well into KVO.) Nonetheless, I’ve added KVO to 2.1.0ddg. I’ve also stripped out Apple’s decision code. Though the interface routines remain. IOW, if you use 2.0.4ddg as a drop-in replacement for Apple’s class, then v2.1.0ddg still works.

    Once again, thank you for reading my blog.

    Andrew

    Sunday, July 11, 2010 at 10:01 am | Permalink
  27. Toby wrote:

    Andrew,

    A very interesting read. I have a question regarding the license. The new BSD license states that

    ” Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.”

    How do you want that realized in real life? I am considering using the code in an app I am writing for a client, but I doubt that they will be willing to agree to me adding a view that includes the copyright notice. I fully understand that you have a right for your work to be acknowledged. However, the license probably makes it impossible for me to use the code. Have you ever considered different license models?

    Kind regards
    Toby

    Saturday, July 24, 2010 at 4:50 pm | Permalink
  28. admin wrote:

    Toby,

    There are many reasons to open source software. One of the most important is resumé buffing. That is why I chose the BSD license. As this work has already resulted in an expansion of my consulting business, it does not make economic sense for me to change licenses.

    If they don’t want to publicly acknowledge my contribution to their product, I am willing to provide them a commercial license for a fee. Because this would be a license with different terms, I may have to engage an attorney. Overall, it is almost certainly cheaper to just create a button on their own copyright page that brings up a modal view with a big UITextView in it. This is about 20 minutes of work. It can also be used for the next piece of open source software you bring into the product.

    Or they can just use Apple’s code. Apple’s June 1st release addresses the deficiencies I corrected. That said, in my discussions with Apple, it was clear that the code is stable and is not going to have much more work put into it. In addition to those fixes, I wrote the large number of convenience routines. Version 2.1.0ddg will also support KVO and have a new method -touchHost: that will bring up an iOS device’s radios. I am also investigating how to support IPv6 for
    +reachabilityForInternetConnection, +reachabilityForLocalWiFi and
    +reachabilityWithAddress:. I do not know what Apple’s plans are for supporting IPv6. I am sure they will have some sort of solution.

    I hope you can find a good solution with the choices you have above.

    Andrew

    Saturday, July 24, 2010 at 5:26 pm | Permalink

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*