Easiest way to detect Internet connection on iOS?

2019-01-01 09:35发布

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?

16条回答
倾城一夜雪
2楼-- · 2019-01-01 10:26

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()
    
查看更多
笑指拈花
3楼-- · 2019-01-01 10:29

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
    }
查看更多
像晚风撩人
4楼-- · 2019-01-01 10:35

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

查看更多
与风俱净
5楼-- · 2019-01-01 10:38

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

查看更多
登录 后发表回答