题 如何检查iOS或macOS上的活动Internet连接?


我想检查我是否在iOS上使用了Internet连接 可可触摸 库或使用macOS的macOS 可可 库。

我想出了一种方法来使用 NSURL。我这样做的方式似乎有点不可靠(因为即使谷歌有一天可能会失败并依赖第三方看起来很糟糕),而且如果谷歌没有回复,我可以查看其他网站的回复,在我的应用程序中看起来似乎很浪费并且不必要的开销

- (BOOL) connectedToInternet
{
    NSString *URLString = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.google.com"]];
    return ( URLString != NULL ) ? YES : NO;
}

我做得不好,(更不用说了 stringWithContentsOfURL 在iOS 3.0和macOS 10.4中已弃用,如果是这样,有什么更好的方法来实现这一目标?


1225
2017-07-05 08:45


起源


宁 return (BOOL)URLString;,甚至更好, return !!URLString 要么 return URLString != nil
我不知道你的用例是什么,但如果可以,最好尝试请求并处理任何错误,例如缺少连接。如果你不能这样做,那么在这种情况下有很多好的建议。 - SK9
你的解决方案很聪明,我更喜欢它。你也可以使用 NSString *URLString = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"https://twitter.com/getibox"] encoding:NSUTF8StringEncoding error:nil]; 摆脱恼人的警告。 - Abdelrahman Eid
尝试使用以下链接中的Reachability类,它将适合您 github.com/tonymillion/Reachability - Dhaval H. Nena
对于最近找到这个答案的人: stackoverflow.com/a/8813279 - afollestad


答案:


重要:这个检查应该 总是 异步执行。以下大部分答案都是同步的,所以要小心,否则你会冻结你的应用程序。


迅速

1)通过CocoaPods或Carthage安装: https://github.com/ashleymills/Reachability.swift

2)通过闭包测试可达性

let reachability = Reachability()!

reachability.whenReachable = { reachability in
    if reachability.connection == .wifi {
        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")
}

Objective-C的

1)添加 SystemConfiguration 该项目的框架,但不要担心将其包含在任何地方

2)添加Tony Million的版本 Reachability.h 和 Reachability.m 到项目(在这里找到: https://github.com/tonymillion/Reachability

3)更新接口部分

#import "Reachability.h"

// Add this to the interface in the .m file of your view controller
@interface MyViewController ()
{
    Reachability *internetReachableFoo;
}
@end

4)然后在您可以调用的视图控制器的.m文件中实现此方法

// Checks if we have an internet connection or not
- (void)testInternetConnection
{   
    internetReachableFoo = [Reachability reachabilityWithHostname:@"www.google.com"];

    // Internet is reachable
    internetReachableFoo.reachableBlock = ^(Reachability*reach)
    {
        // Update the UI on the main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"Yayyy, we have the interwebs!");
        });
    };

    // Internet is not reachable
    internetReachableFoo.unreachableBlock = ^(Reachability*reach)
    {
        // Update the UI on the main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"Someone broke the internet :(");
        });
    };

    [internetReachableFoo startNotifier];
}

重要的提示: 该 Reachability class是项目中使用最多的类之一,因此您可能会遇到与其他项目的命名冲突。如果发生这种情况,您将不得不重命名其中一对 Reachability.h 和 Reachability.m 文件到其他东西来解决问题。

注意: 您使用的域名无关紧要。它只是测试任何域的网关。


1220
2017-08-29 23:58



@gonzobrains:您使用的域名无关紧要。它只是测试网关 任何 域。 - iwasrobbed
@KaanDedeoglu这只是一个例子,使用你想要的任何域名。它只是检查到互联网的网关,而不是域实际可用。 - iwasrobbed
哦,顺便说一下你需要补充一下 SystemConfiguration.framework 对项目也是如此(方法1)。 - wiseindy
google.com不适用于中国! - Dmitry
使用www.appleiphonecell.com而不是google.com - 这个网址是由apple创建的,正是出于这个原因。 - Smikey


我喜欢简单易懂。我这样做的方式是:

//Class.h
#import "Reachability.h"
#import <SystemConfiguration/SystemConfiguration.h>

- (BOOL)connected;

//Class.m
- (BOOL)connected
{
    Reachability *reachability = [Reachability reachabilityForInternetConnection];
    NetworkStatus networkStatus = [reachability currentReachabilityStatus];
    return networkStatus != NotReachable;
}

然后,每当我想看看我是否有连接时,我都会使用它:

if (![self connected]) {
    // Not connected
} else {
    // Connected. Do some Internet stuff
}

此方法不会等待更改的网络状态以执行操作。它只是在您要求时测试状态。


303
2017-08-26 13:34



嗨@msec,你可以在这个页面尝试安德鲁齐默解决方案,它与adsl断开连接(和wifi连接)工作正常 - Williew
你为什么要回来!= NotReachable而不是“return netWorkStatus;” ??? - Fede Cugliandolo
对于那些只是复制和粘贴的人,就像我用上面的代码做的那样。也可以手动添加SystemConfiguration.framework,否则会出现链接错误。 - Iftikhar
可达性未定义??? - coolcool1994


使用Apple的Reachability代码,我创建了一个函数,可以正确地检查这个,而不必包含任何类。

在项目中包含SystemConfiguration.framework。

做一些进口:

#import <sys/socket.h>
#import <netinet/in.h>
#import <SystemConfiguration/SystemConfiguration.h>

现在只需调用此函数:

/*
Connectivity testing code pulled from Apple's Reachability Example: https://developer.apple.com/library/content/samplecode/Reachability
 */
+(BOOL)hasConnectivity {
    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);
    if (reachability != NULL) {
        //NetworkStatus retVal = NotReachable;
        SCNetworkReachabilityFlags flags;
        if (SCNetworkReachabilityGetFlags(reachability, &flags)) {
            if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
            {
                // If target host is not reachable
                return NO;
            }

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


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

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

    return NO;
}

它就是 iOS 5的 为你测试过。


143
2017-10-28 20:37



你能描述一下这段代码吗?这项检查有何用途? - Valerii Pavlov
这也适用于iOS3.0,thx - RocketMan
@JezenThomas这不会异步执行互联网检查,这就是为什么它“更加苗条”......你应该总是通过订阅通知异步这样做,这样你就不会在这个过程中挂断应用程序。 - iwasrobbed
谢谢,这项工作,即使你使用wifi adsl和adsl没有连接,正是我需要的。 - Williew
这会泄漏内存 - 需要使用CFRelease()释放'可读性'结构(对象,事物)。 - Russell Mull


这曾经是正确的答案,但它现在已经过时,因为您应该订阅可达性通知。此方法同步检查:


您可以使用Apple的Reachability类。它还允许您检查是否启用了Wi-Fi:

Reachability* reachability = [Reachability sharedReachability];
[reachability setHostName:@"www.example.com"];    // Set your host name here
NetworkStatus remoteHostStatus = [reachability remoteHostStatus];

if (remoteHostStatus == NotReachable) { }
else if (remoteHostStatus == ReachableViaWiFiNetwork) { }
else if (remoteHostStatus == ReachableViaCarrierDataNetwork) { }

SDK中不包含Reachability类,而是SDK的一部分 这个Apple示例应用程序。只需下载它,并将Reachability.h / m复制到您的项目中。此外,您必须将SystemConfiguration框架添加到项目中。


118
2017-07-05 10:58



请参阅上面的评论,关于不使用这样的Reachability。在异步模式下使用它并订阅它发送的通知 - 不要。 - Kendall Helmstetter Gelner
对于可以使用可达性类的委托方法之前需要设置的内容,此代码是一个很好的起点。 - Brock Woolf


这是一个非常简单的答案:

NSURL *scriptUrl = [NSURL URLWithString:@"http://www.google.com/m"];
NSData *data = [NSData dataWithContentsOfURL:scriptUrl];
if (data)
    NSLog(@"Device is connected to the Internet");
else
    NSLog(@"Device is not connected to the Internet");

该URL应指向一个非常小的网站。我在这里使用谷歌的移动网站,但如果我有一个可靠的网络服务器,我会上传 一个只有一个字符的小文件 为了最大速度。

如果检查设备是否 不知何故 连接到互联网是你想做的一切,我绝对建议使用这个简单的解决方案。如果您需要了解用户的连接方式,可以使用Reachability。

小心:这会在加载网站时短暂阻止你的线程。在我的情况下,这不是一个问题,但你应该考虑这一点(Brad指出这一点)。


78
2018-05-14 22:25



我真的很喜欢这个想法,但我会说99.999%的可靠性同时保持较小的响应大小,请访问www.google.com/m这是google的移动视图。 - rwyland
令人敬畏的解决方案@Erik。正如rwyland所说,我还建议你使用www.google.com而不是www.google.com/m。这很奇怪,但从我的测试来看,移动版本总是比www.google.com多出约120ms - Sebyddd
Apple文档建议不要这样做,因为它可以阻止慢速网络上的线程,可能导致应用程序在iOS中终止 - Brad Thomas
感谢您的积极反馈,同意www.google.com/m是最可靠的解决方案! - Erik Wegener
大声笑,我相信Google会感谢您建议人们将它们用作互联网检查资源。 - mxcl


以下是我在我的应用程序中执行的操作:虽然200状态响应代码不保证任何内容,但它对我来说足够稳定。这不需要像在此处发布的NSData答案那样多的加载,因为我只检查HEAD响应。

SWIFT代码

func checkInternet(flag:Bool, completionHandler:(internet:Bool) -> Void)
{
    UIApplication.sharedApplication().networkActivityIndicatorVisible = true

    let url = NSURL(string: "http://www.appleiphonecell.com/")
    let request = NSMutableURLRequest(URL: url!)

    request.HTTPMethod = "HEAD"
    request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
    request.timeoutInterval = 10.0

    NSURLConnection.sendAsynchronousRequest(request, queue:NSOperationQueue.mainQueue(), completionHandler:
    {(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in

        UIApplication.sharedApplication().networkActivityIndicatorVisible = false

        let rsp = response as! NSHTTPURLResponse?

        completionHandler(internet:rsp?.statusCode == 200)
    })
}

func yourMethod()
{
    self.checkInternet(false, completionHandler:
    {(internet:Bool) -> Void in

        if (internet)
        {
            // "Internet" aka Apple's region universal URL reachable
        }
        else
        {
            // No "Internet" aka Apple's region universal URL un-reachable
        }
    })
}

Objective-C代码

typedef void(^connection)(BOOL);

- (void)checkInternet:(connection)block
{
    NSURL *url = [NSURL URLWithString:@"http://www.appleiphonecell.com/"];
    NSMutableURLRequest *headRequest = [NSMutableURLRequest requestWithURL:url];
    headRequest.HTTPMethod = @"HEAD";

    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration ephemeralSessionConfiguration];
    defaultConfigObject.timeoutIntervalForResource = 10.0;
    defaultConfigObject.requestCachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;

    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue: [NSOperationQueue mainQueue]];

    NSURLSessionDataTask *dataTask = [defaultSession dataTaskWithRequest:headRequest
        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
    {
        if (!error && response)
        {
            block([(NSHTTPURLResponse *)response statusCode] == 200);
        }
    }];
    [dataTask resume];
}

- (void)yourMethod
{
    [self checkInternet:^(BOOL internet)
    {
         if (internet)
         {
             // "Internet" aka Apple's region universal URL reachable
         }
         else
         {
             // No "Internet" aka Apple's region universal URL un-reachable
         }
    }];
}

70
2017-11-30 03:26



似乎这是最快的方式 - Pavel
警告:根据我的经验,此解决方案不会一直有效。在许多情况下,在获得甜蜜时间之后,返回的响应是403。这个解决方案似乎很完美,但不能保证100%的结果。 - Mustafa
“没有谷歌,没有互联网”? - Kiran Ruth R
由于中国政府现在完全封锁google.com,截至2014年6月,这将在中国大陆失败。 (google.cn虽然有效,但在中国大陆,没有baidu.com,没有互联网)可能更好地ping你需要与之通信的服务器。 - prewett
请使用www.appleiphonecell.com - 苹果正是出于这个原因创建了此网址。 - Smikey


苹果用品 示例代码 检查不同类型的网络可用性。或者有一个  在iPhone开发者的食谱中。

注意: 有关使用Apple的可访问性代码,请参阅@KHG对此答案的评论。


54
2017-07-05 08:59



谢谢。我发现3.0中的Xcode文档还包含源代码,通过在文档中搜索“reachability”找到。 - Brock Woolf
请注意,Apple的Reachability示例代码的新版本(09-08-09)是异步的。 - Daniel Hepper