Easiest way to detect Internet connection on iOS?
Asked Answered
G

16

151

I know this question will appear to be a dupe of many others, however, I don't feel the simple case is well explained here. Coming from an Android and BlackBerry background, making requests through HTTPUrlConnection instantly fail if there is no connection available. This seems like completely sane behavior, and I was surprised to find NSURLConnection in iOS did not emulate it.

I understand that Apple (and others who have extended it) provide a Reachability class to assist with determining the network state. I was happy to first see this and fully expected to see something like bool isNetworkAvailable(), but instead to my surprise I found a complex system requiring notification registrations and callbacks, and a bunch of seemingly unnecessary details. There must be a better way.

My app already gracefully handles connection failures, including no connectivity. The user is notified of the failure, and the app moves on.

Thus my requirements are simple: Single, synchronous function I can call before all HTTP requests to determine if I should bother actually sending the request or not. Ideally it requires no set up and just returns a boolean.

Is this really not possible on iOS?

Gaur answered 11/1, 2012 at 0:13 Comment(2)
Possible duplicate of How to check for an active Internet connection on iOS or OSX?Fieldfare
You should never call a synchronous network reachability method prior to every HTTP request; it's just kind of crazy to do that work unless reachability asynchronously tells you there was a change in network conditions and then you can choose not to send the HTTP request. This is also the whole reason timeouts exist (and then you should handle that scenario gracefully). See the link above for the async wayFieldfare
B
253

I did a little more research and I am updating my answer with a more current solution. I am not sure if you have already looked at it but there is a nice sample code provided by Apple.

Download the sample code here

Include the Reachability.h and Reachability.m files in your project. Take a look at ReachabilityAppDelegate.m to see an example on how to determine host reachability, reachability by WiFi, by WWAN etc. For a very simply check of network reachability, you can do something like this

Reachability *networkReachability = [Reachability reachabilityForInternetConnection];   
NetworkStatus networkStatus = [networkReachability currentReachabilityStatus];    
if (networkStatus == NotReachable) {        
    NSLog(@"There IS NO internet connection");        
} else {        
     NSLog(@"There IS internet connection");        
}

@BenjaminPiette's: Don't forget to add SystemConfiguration.framework to your project.

Benjamin answered 11/1, 2012 at 2:6 Comment(10)
Don't forget to add SystemConfiguration.framework to your project.Xiaoximena
this is not working for me, can U please tell, where I should add thisWouldst
@Wouldst It works. This is the easiest and most effective way I see to detect network connection. Simple and Easy!Bio
Careful with this. I'm finding it doesn't work reliably when the network is restored, at least in the simulator. I launch with wifi off, and it correctly reports no network available; but then I turn wifi back on, and SCNetworkReachabilityGetFlags continues to return 0, even though actual network queries work just fine.Discussion
I believe my answer below (https://mcmap.net/q/46287/-easiest-way-to-detect-internet-connection-on-ios) requires less work (no downloading other files), but that's just my opinion.Cornerwise
@Joe Strout: this a know problem with the simulator, AFAIK it takes much longer for the Simulator to check the DNS. Better use a device for testing Reachabilty.Octal
i seem to be facing an issue with the above approach. In case of limited connectivity the above code still returns as device is connected to the internet. is there something like a ping test which we can use ?Zachariah
The problem with this approach is that it's synchronous and it doesn't notify you if the network conditions change. More thorough solution with network change handling https://mcmap.net/q/45302/-how-can-i-check-for-an-active-internet-connection-on-ios-or-macos/308315Fieldfare
Is that provide signal strength?Diomedes
Note that this only tells you whether connection to the specific host is routable. That doesn't mean you're actually able to connect. Example: you connect your iPhone's wifi to your camera's wifi. Wifi is connected with a default gateway: all hosts will report reachable. But they are actually not -- the camera is a dead-end connection.Nannettenanni
N
45

Seeing as this thread is the top google result for this type of question, I figured I would provide the solution that worked for me. I was already using AFNetworking, but searching didn't reveal how to accomplish this task with AFNetworking until midway through my project.

What you want is the AFNetworkingReachabilityManager.

// -- Start monitoring network reachability (globally available) -- //
[[AFNetworkReachabilityManager sharedManager] startMonitoring];

[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {

    NSLog(@"Reachability changed: %@", AFStringFromNetworkReachabilityStatus(status));


    switch (status) {
        case AFNetworkReachabilityStatusReachableViaWWAN:
        case AFNetworkReachabilityStatusReachableViaWiFi:
            // -- Reachable -- //
            NSLog(@"Reachable");
            break;
        case AFNetworkReachabilityStatusNotReachable:
        default:
            // -- Not reachable -- //
            NSLog(@"Not Reachable");
            break;
    }

}];

You can also use the following to test reachability synchronously (once monitoring has started):

-(BOOL) isInternetReachable
{
    return [AFNetworkReachabilityManager sharedManager].reachable;
}
Neely answered 29/3, 2014 at 22:37 Comment(4)
I upvoted for bringing the AFNetworkReachabilityManager into light!Lachellelaches
For those to whom this wasn't immediately obvious: AFNetworking is a third-party library.Hydrogen
Network reachability is a diagnostic tool that can be used to understand why a request might have failed. It should not be used to determine whether or not to make a request. - That's what documentation says.Annular
AFNetworkReachabilityManager.reachable does NOT perform any sort of synchronous check. It returns a logical or of reachableViaWWAN and reachableViaWifi.Log
L
41

Sorry for replying too late but I hope this answer can help somebody in future.

Following is a small native C code snippet that can check internet connectivity without any extra class.

Add the following headers:

#include<unistd.h>
#include<netdb.h>

Code:

-(BOOL)isNetworkAvailable
{
    char *hostname;
    struct hostent *hostinfo;
    hostname = "google.com";
    hostinfo = gethostbyname (hostname);
    if (hostinfo == NULL){
        NSLog(@"-> no connection!\n");
        return NO;
    }
    else{
        NSLog(@"-> connection established!\n");
        return YES;
    }
}

Swift 3

func isConnectedToInternet() -> Bool {
    let hostname = "google.com"
    //let hostinfo = gethostbyname(hostname)
    let hostinfo = gethostbyname2(hostname, AF_INET6)//AF_INET6
    if hostinfo != nil {
        return true // internet available
      }
     return false // no internet
    }
Longmire answered 14/2, 2013 at 7:58 Comment(7)
works much better than Apple Reachability. But according to docs gethostbyname is obsolete and you should use getaddrinfo: struct addrinfo *res = NULL; int s = getaddrinfo("apple.com", NULL, NULL, &res); bool network_ok = (s == 0 && res != NULL); freeaddrinfo(res);Normally
Though it's common to use google.com to check for internet availability, it's worth noting that it's blocked in some countries. A better choice would be domains from more "government-friendly" companies such as yahoo.com or microsoft.com.Centipede
@Laurent: The best option is to use the server address you are going to request data from. google.com over here is just an example.Longmire
The same can be modified to run in loop in background and provide for network state change nice answerDeutoplasm
Is this just doing a DNS lookup? If so, could a cached DNS result could result in the function falsely believing that the network is available?Hyperbola
@Deutoplasm Running this in a loop will consume wireless data, you don't want to do that.Pave
This is close to the perfect solution. Only problem here is if your network doesn't have IPv6 support, which some don't, this will return false. I recommend changing AF_INET6 to AF_INET.Accomplished
C
32

I currently use this simple synchronous method which requires no extra files in your projects or delegates.

Import:

#import <SystemConfiguration/SCNetworkReachability.h>

Create this method:

+(bool)isNetworkAvailable
{
    SCNetworkReachabilityFlags flags;
    SCNetworkReachabilityRef address;
    address = SCNetworkReachabilityCreateWithName(NULL, "www.apple.com" );
    Boolean success = SCNetworkReachabilityGetFlags(address, &flags);
    CFRelease(address);

    bool canReach = success
                    && !(flags & kSCNetworkReachabilityFlagsConnectionRequired)
                    && (flags & kSCNetworkReachabilityFlagsReachable);

    return canReach;
}

Then, if you've put this in a MyNetworkClass:

if( [MyNetworkClass isNetworkAvailable] )
{
   // do something networky.
}

If you are testing in the simulator, turn your Mac's wifi on and off, as it appears the simulator will ignore the phone setting.

Update:

  1. In the end I used a thread/asynchronous callback to avoid blocking the main thread; and regularly re-testing so I could use a cached result - although you should avoid keeping data connections open unnecessarily.

  2. As @thunk described, there are better URLs to use, which Apple themselves use. http://cadinc.com/blog/why-your-apple-ios-7-device-wont-connect-to-the-wifi-network

Cornerwise answered 27/10, 2014 at 17:52 Comment(7)
Thanks, and same as most others in this thread: Don't forget to add SystemConfiguration.framework to your project.Erund
Odd, in my Xcode project, I didn't have to add the SystemConfiguration.framework. I've got SpriteKit, UIKit, StoreKit, Foundation and CoreGraphics, but not SystemConfiguration.... Is there some implicit inclusion going on?Cornerwise
This worked for me, but when I ran it when connected to a wi-fi network that has no Internet connection, it locked up my UI for about 30 seconds. An asynchronous approach is needed in that situation.Hydrogen
www.apple.com is big website... the better option will be www.google.comBasutoland
excellent answer! One suggestion: the test URL, www.appleiphonecell.com, is supposedly available strictly for this purpose and apparently not blocked in China.Comprador
Thanks @thunk I've updated my answer with more information on your contribution.Cornerwise
www.appleiphonecell.com now redirects to apple.comDid
E
11

It is possible and it is really simple if you look at it when finishing the implementation, which is again - very simple, since the only items you need are two boolean variables: internet reachability and host reachability (you often need more than one of these). Once you assemble your helper class that can determine the connections status, you really don't care again of the implementation needed for knowing these procedures.

Example:

#import <Foundation/Foundation.h>

@class Reachability;

@interface ConnectionManager : NSObject {
    Reachability *internetReachable;
    Reachability *hostReachable;
}

@property BOOL internetActive;
@property BOOL hostActive;

- (void) checkNetworkStatus:(NSNotification *)notice;

@end

And the .m file:

#import "ConnectionManager.h"
#import "Reachability.h"

@implementation ConnectionManager
@synthesize internetActive, hostActive;

-(id)init {
    self = [super init];
    if(self) {

    }
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkNetworkStatus:) name:kReachabilityChangedNotification object:nil];

    internetReachable = [[Reachability reachabilityForInternetConnection] retain];
    [internetReachable startNotifier];

    hostReachable = [[Reachability reachabilityWithHostName:@"www.apple.com"] retain];
    [hostReachable startNotifier];

    return self;
}

- (void) checkNetworkStatus:(NSNotification *)notice {
    NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
    switch (internetStatus)

    {
        case NotReachable:
        {
            NSLog(@"The internet is down.");
            self.internetActive = NO;

            break;

        }
        case ReachableViaWiFi:
        {
            NSLog(@"The internet is working via WIFI.");
            self.internetActive = YES;

            break;

        }
        case ReachableViaWWAN:
        {
            NSLog(@"The internet is working via WWAN.");
            self.internetActive = YES;

            break;

        }
    }

    NetworkStatus hostStatus = [hostReachable currentReachabilityStatus];
    switch (hostStatus)

    {
        case NotReachable:
        {
            NSLog(@"A gateway to the host server is down.");
            self.hostActive = NO;

            break;

        }
        case ReachableViaWiFi:
        {
            NSLog(@"A gateway to the host server is working via WIFI.");
            self.hostActive = YES;

            break;

        }
        case ReachableViaWWAN:
        {
            NSLog(@"A gateway to the host server is working via WWAN.");
            self.hostActive = YES;

            break;

        }
    }

}

// If lower than SDK 5 : Otherwise, remove the observer as pleased.

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super dealloc];
}

@end
Ernestinaernestine answered 11/1, 2012 at 0:21 Comment(0)
D
6

Someone has solved this in a simple, reusable way before. DDGReachability.

EDIT: Or tonymillion/Reachability.

Dacron answered 11/1, 2012 at 2:9 Comment(0)
B
4

I extracted the code and put into one single method, hope it would help others.

#import <SystemConfiguration/SystemConfiguration.h>

#import <netinet/in.h>
#import <netinet6/in6.h>

...

- (BOOL)isInternetReachable
{    
    struct sockaddr_in zeroAddress;
    bzero(&zeroAddress, sizeof(zeroAddress));
    zeroAddress.sin_len = sizeof(zeroAddress);
    zeroAddress.sin_family = AF_INET;

    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress);
    SCNetworkReachabilityFlags flags;

    if(reachability == NULL)
        return false;

    if (!(SCNetworkReachabilityGetFlags(reachability, &flags)))
        return false;

    if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
        // if target host is not reachable
        return false;


    BOOL isReachable = false;


    if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
    {
        // if target host is reachable and no connection is required
        //  then we'll assume (for now) that your on Wi-Fi
        isReachable = true;
    }


    if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
         (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
    {
        // ... and the connection is on-demand (or on-traffic) if the
        //     calling application is using the CFSocketStream or higher APIs

        if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
        {
            // ... and no [user] intervention is needed
            isReachable = true;
        }
    }

    if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
    {
        // ... but WWAN connections are OK if the calling application
        //     is using the CFNetwork (CFSocketStream?) APIs.
        isReachable = true;
    }


    return isReachable;


}
Bareback answered 20/12, 2012 at 10:17 Comment(3)
[A] From where did you extract this code? Can you post a link to the source? [B] Thank you so very much for your extraction work! Just what I needed. I was about to perform this chore myself.Discreditable
So, it seems like this is the only answer which does not check any remote server(s)?Kinetic
For others testing this on MacOS, note that kSCNetworkReachabilityFlagsIsWWAN is for OS_IPHONE not macOS.Coma
M
4

I am writing the swift version of the accepted answer here, incase if someone finds it usefull, the code is written swift 2,

You can download the required files from SampleCode

Add Reachability.h and Reachability.m file to your project,

Now one will need to create Bridging-Header.h file if none exists for your project,

Inside your Bridging-Header.h file add this line :

#import "Reachability.h"

Now in order to check for Internet Connection

static func isInternetAvailable() -> Bool {
    let networkReachability : Reachability = Reachability.reachabilityForInternetConnection()
    let networkStatus : NetworkStatus = networkReachability.currentReachabilityStatus()

    if networkStatus == NotReachable {
        print("No Internet")
        return false
    } else {
        print("Internet Available")
        return true
    }

}
Mourant answered 14/12, 2015 at 6:50 Comment(0)
K
3

I think this could help..

[[AFNetworkReachabilityManager sharedManager] startMonitoring];

if([AFNetworkReachabilityManager sharedManager].isReachable)
{
    NSLog(@"Network reachable");
}
else
{   
   NSLog(@"Network not reachable");
}
Keating answered 20/10, 2015 at 7:6 Comment(1)
This does not work if network interface is not changed and internet connectivity is lost.Unbound
H
3

You may also try this one if you already configured AFNetworking in your project.

-(void)viewDidLoad{  // -- add connectivity notification --//
[[NSNotificationCenter defaultCenter ] addObserver:self selector:@selector(ReachabilityDidChangeNotification:) name:AFNetworkingReachabilityDidChangeNotification object:nil];}
-(void)ReachabilityDidChangeNotification:(NSNotification *)notify
{
// -- NSLog(@"Reachability changed: %@", AFStringFromNetworkReachabilityStatus(status));  -- //
NSDictionary *userInfo =[notif userInfo];
AFNetworkReachabilityStatus status= [[userInfo valueForKey:AFNetworkingReachabilityNotificationStatusItem] intValue];
switch (status)
{
    case AFNetworkReachabilityStatusReachableViaWWAN:
    case AFNetworkReachabilityStatusReachableViaWiFi:
        // -- Reachable -- //
// -- Do your stuff when internet connection is available -- //
        [self getLatestStuff];
        NSLog(@"Reachable");
        break;
    case AFNetworkReachabilityStatusNotReachable:
    default:
        // -- Not reachable -- //
        // -- Do your stuff for internet connection not available -- //
NSLog(@"Not Reachable");
        break;
}
}
Hypoxanthine answered 30/8, 2017 at 8:59 Comment(0)
B
1

Here is a good solution for checking connectivity using Swift, without using Reachability. I found it on this blog.

Create a new Swift file in your project called Network.swift (for example). Paste this code inside that file:

import Foundation

public class Network {

    class func isConnectedToNetwork()->Bool{

        var Status:Bool = false
        let url = NSURL(string: "http://google.com/")
        let request = NSMutableURLRequest(URL: url!)
        request.HTTPMethod = "HEAD"
        request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
        request.timeoutInterval = 10.0

        var response: NSURLResponse?

        var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: nil) as NSData?

        if let httpResponse = response as? NSHTTPURLResponse {
            if httpResponse.statusCode == 200 {
                Status = true
            }
        }

        return Status
    }
}

You can then check for connectivity anywhere in your project by using:

if Network.isConnectedToNetwork() == true {
    println("Internet connection OK")
} else {
    println("Internet connection FAILED")
}
Browband answered 24/5, 2015 at 17:23 Comment(0)
B
0

EDIT: This will not work for network URLs (see comments)

As of iOS 5, there is a new NSURL instance method:

- (BOOL)checkResourceIsReachableAndReturnError:(NSError **)error

Point it to the website you care about or point it to apple.com; I think it is the new one-line call to see if the internet is working on your device.

Beggarweed answered 28/1, 2012 at 15:54 Comment(3)
Actually in my own tests this always returns false. The docs state this is the case for 4.x but in 5+ it should be working. YMMVBeggarweed
This doesn't appear to work for the situation you are looking for. See #9266654Sketchy
That's used for file URLs, not Internet URLs.Upbeat
S
0

I also was not happy with the Internet checking options available (Why is this not a native API?!?!)

My own issue was with 100% packet loss -- when a device is connected to the router, but the router is not connected to the Internet. Reachability and others will hang for ages. I created a utility singleton class to deal with that by adding an asynch time-out. It works fine in my app. Hope it helps. Here's the link on github:

https://github.com/fareast555/TFInternetChecker

Skep answered 22/8, 2015 at 16:53 Comment(0)
B
0

Checking the Internet connection availability in (iOS) Xcode 8.2 , Swift 3.0

This is simple method for checking the network availability. I managed to translate it to Swift 2.0 and here the final code. The existing Apple Reachability class and other third party libraries seemed to be too complicated to translate to Swift.

This works for both 3G and WiFi connections.

Don’t forget to add “SystemConfiguration.framework” to your project builder.

//Create new swift class file Reachability in your project.

import SystemConfiguration

public class Reachability {
class func isConnectedToNetwork() -> Bool {
    var zeroAddress = sockaddr_in()
    zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
    zeroAddress.sin_family = sa_family_t(AF_INET)
    let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
        $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
            SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
        }
    }
    var flags = SCNetworkReachabilityFlags()
    if !SCNetworkReachabilityGetFlags(defaultRouteReachability! , &flags) {
        return false
    }
    let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
    let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
    return (isReachable && !needsConnection)
   }
}

// Check network connectivity from anywhere in project by using this code.

if Reachability.isConnectedToNetwork() == true {
     print("Internet connection OK")
} else {
 print("Internet connection FAILED")
}
Biyearly answered 17/12, 2015 at 11:46 Comment(3)
With Xcode version 7.2 (7C68), this code has compiler errorsTruc
@Truc , thanks for pointing issue, this because of unsafe pointers, as Swift 2/ Xcode is updating there syntaxes , we should follow them. i have updated code for the same. ty ;)Biyearly
If you are connected to a wi-fi but not able to connect to the internet (open a google page) still it returns true for reachable. It's wrong.Mitten
O
0

Replacement for Apple's Reachability re-written in Swift with closures, inspired by tonymillion: https://github.com/ashleymills/Reachability.swift

  1. Drop the file Reachability.swift into your project. Alternatively, use CocoaPods or Carthage - See the Installation section of the project's README.

  2. Get notifications about network connectivity:

    //declare this property where it won't go out of scope relative to your listener
    let reachability = Reachability()!
    
    reachability.whenReachable = { reachability in
        if reachability.isReachableViaWiFi {
            print("Reachable via WiFi")
        } else {
            print("Reachable via Cellular")
        }
    }
    
    reachability.whenUnreachable = { _ in
        print("Not reachable")
    }
    
    do {
        try reachability.startNotifier()
    } catch {
        print("Unable to start notifier")
    }
    

    and for stopping notifications

    reachability.stopNotifier()
    
Outlawry answered 9/10, 2017 at 13:27 Comment(0)
D
0

Alamofire

If you are already using Alamofire for all the RESTful Api, here is what you can benifit from that.

You can add following class to your app, and call MNNetworkUtils.main.isConnected() to get a boolean on whether its connected or not.

#import Alamofire

class MNNetworkUtils {
  static let main = MNNetworkUtils()
  init() {
    manager = NetworkReachabilityManager(host: "google.com")
    listenForReachability()
  }

  private let manager: NetworkReachabilityManager?
  private var reachable: Bool = false
  private func listenForReachability() {
    self.manager?.listener = { [unowned self] status in
      switch status {
      case .notReachable:
        self.reachable = false
      case .reachable(_), .unknown:
        self.reachable = true
      }
    }
    self.manager?.startListening()
  }

  func isConnected() -> Bool {
    return reachable
  }
}

This is a singleton class. Every time, when user connect or disconnect the network, it will override self.reachable to true/false correctly, because we start listening for the NetworkReachabilityManager on singleton initialization.

Also in order to monitor reachability, you need to provide a host, currently I am using google.com feel free to change to any other hosts or one of yours if needed.

Downey answered 2/4, 2018 at 17:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.