ChatGPT解决这个技术问题 Extra ChatGPT

Easiest way to detect Internet connection on iOS?

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?

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 way

S
Semere Taézaz Sium

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.


Don't forget to add SystemConfiguration.framework to your project.
@chandru It works. This is the easiest and most effective way I see to detect network connection. Simple and Easy!
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.
I believe my answer below (stackoverflow.com/a/26593728/557362) requires less work (no downloading other files), but that's just my opinion.
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.
s
sherbertman

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;
}

I upvoted for bringing the AFNetworkReachabilityManager into light!
For those to whom this wasn't immediately obvious: AFNetworking is a third-party library.
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.
AFNetworkReachabilityManager.reachable does NOT perform any sort of synchronous check. It returns a logical or of reachableViaWWAN and reachableViaWifi.
M
MAhipal Singh

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
    }

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);
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.
@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.
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?
@amar Running this in a loop will consume wireless data, you don't want to do that.
G
GilesDMiddleton

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:

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


Thanks, and same as most others in this thread: Don't forget to add SystemConfiguration.framework to your project.
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?
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.
www.apple.com is big website... the better option will be www.google.com
excellent answer! One suggestion: the test URL, www.appleiphonecell.com, is supposedly available strictly for this purpose and apparently not blocked in China.
D
Dani

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

佚名

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

EDIT: Or tonymillion/Reachability.


W
Walty Yeung

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;


}

[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.
So, it seems like this is the only answer which does not check any remote server(s)?
For others testing this on MacOS, note that kSCNetworkReachabilityFlagsIsWWAN is for OS_IPHONE not macOS.
C
Community

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
    }

}

p
pooja

I think this could help..

[[AFNetworkReachabilityManager sharedManager] startMonitoring];

if([AFNetworkReachabilityManager sharedManager].isReachable)
{
    NSLog(@"Network reachable");
}
else
{   
   NSLog(@"Network not reachable");
}

This does not work if network interface is not changed and internet connectivity is lost.
R
Ravi Raja Jangid

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;
}
}

b
blwinters

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")
}

L
Lou Franco

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.


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. YMMV
This doesn't appear to work for the situation you are looking for. See stackoverflow.com/questions/9266154/…
That's used for file URLs, not Internet URLs.
M
Mike Critchley

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


C
Community

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")
}

With Xcode version 7.2 (7C68), this code has compiler errors
@Daniel , 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 ;)
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.
P
Pierre F

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

Drop the file Reachability.swift into your project. Alternatively, use CocoaPods or Carthage - See the Installation section of the project's README. 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()


n
nuynait

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.