我知道这个问题似乎是许多其他人的愚蠢行为,但是,我觉得这个简单的案例并没有得到很好的解释。来自Android和BlackBerry背景,如果没有可用的连接,通过HTTPUrlConnection
发出请求会立即失败。这似乎是完全理智的行为,我很惊讶地发现iOS中的NSURLConnection
没有模仿它。
我了解Apple(以及其他扩展它的人)提供Reachability
类来帮助确定网络状态。我很高兴第一次看到这一点并完全期望看到像bool isNetworkAvailable()
这样的东西,但令我惊讶的是,我发现了一个复杂的系统,需要通知注册和回调,以及一堆看似不必要的细节。必须有更好的方法。
我的应用已经优雅地处理连接失败,包括没有连接。用户会收到失败通知,然后应用程序继续运行。
因此我的要求很简单:我可以在所有HTTP请求之前调用单个同步函数来确定我是否应该打扰实际发送请求。理想情况下,它不需要设置,只返回一个布尔值。
这在iOS上真的不可能吗?
答案 0 :(得分:249)
我做了一些研究,我正在用更新的解决方案更新我的答案。我不确定你是否已经看过它,但Apple提供了一个很好的示例代码。
下载示例代码here
在项目中包含Reachability.h和Reachability.m文件。看看ReachabilityAppDelegate.m,看看如何通过WiFi,WWAN等确定主机可达性,可达性的示例。只需检查网络可达性,就可以做到这样的事情
Reachability *networkReachability = [Reachability reachabilityForInternetConnection];
NetworkStatus networkStatus = [networkReachability currentReachabilityStatus];
if (networkStatus == NotReachable) {
NSLog(@"There IS NO internet connection");
} else {
NSLog(@"There IS internet connection");
}
@ BenjaminPiette:不要忘记将SystemConfiguration.framework添加到您的项目中。
答案 1 :(得分:45)
看到这个帖子是这类问题的最佳google结果,我想我会提供适合我的解决方案。我已经在使用AFNetworking,但搜索没有透露如何使用AFNetworking完成此任务,直到我的项目中途。
你想要的是什么 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;
}
}];
您还可以使用以下方法同步测试可达性(一旦监控开始):
-(BOOL) isInternetReachable
{
return [AFNetworkReachabilityManager sharedManager].reachable;
}
答案 2 :(得分:39)
很抱歉回复得太晚了,但我希望这个答案可以在将来帮助某人。
以下是一个小型本机C代码段,可以检查互联网连接,而无需任何额外的课程。
添加以下标题:
#include<unistd.h>
#include<netdb.h>
代码:
-(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
}
答案 3 :(得分:31)
我目前使用这种简单的同步方法,在项目或代理中不需要额外的文件。
导入:
#import <SystemConfiguration/SCNetworkReachability.h>
创建此方法:
+(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;
}
然后,如果你把它放在MyNetworkClass
:
if( [MyNetworkClass isNetworkAvailable] )
{
// do something networky.
}
如果您在模拟器中进行测试,请打开和关闭Mac的wifi,因为看起来模拟器将忽略手机设置。
更新
最后我使用了一个线程/异步回调来避免阻塞主线程;并定期重新测试,以便我可以使用缓存结果 - 尽管你应该避免不必要地打开数据连接。
正如@thunk所描述的,有更好的URL可供使用,Apple自己使用这些URL。 http://cadinc.com/blog/why-your-apple-ios-7-device-wont-connect-to-the-wifi-network
答案 4 :(得分:11)
这是可能的,如果你在完成实现的时候看它真的很简单,这又是 - 非常简单,因为你需要的唯一项目是两个布尔变量:互联网可达性和主机可达性(你经常需要超过其中之一)。一旦组装了可以确定连接状态的帮助程序类,您实际上并不关心知道这些程序所需的实现。
示例:
#import <Foundation/Foundation.h>
@class Reachability;
@interface ConnectionManager : NSObject {
Reachability *internetReachable;
Reachability *hostReachable;
}
@property BOOL internetActive;
@property BOOL hostActive;
- (void) checkNetworkStatus:(NSNotification *)notice;
@end
.m文件:
#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
答案 5 :(得分:6)
某人已经以简单,可重复使用的方式解决了这个问题。 DDGReachability
答案 6 :(得分:4)
我提取了代码并放入一个单一的方法,希望它能帮助别人。
#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;
}
答案 7 :(得分:3)
我认为这可以帮助..
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
if([AFNetworkReachabilityManager sharedManager].isReachable)
{
NSLog(@"Network reachable");
}
else
{
NSLog(@"Network not reachable");
}
答案 8 :(得分:3)
我正在编写接受的答案here的快速版本,如果有人发现它有用,则代码写得很快2,
您可以从SampleCode
下载所需的文件将Reachability.h
和Reachability.m
文件添加到您的项目中,
现在,如果您的项目不存在,则需要创建Bridging-Header.h
文件,
在Bridging-Header.h
文件中添加以下行:
#import "Reachability.h"
现在为了检查Internet连接
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
}
}
答案 9 :(得分:2)
如果您已在项目中配置了AFNetworking,也可以尝试使用此功能。
-(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;
}
}
答案 10 :(得分:1)
这是使用Swift检查连接性的一个很好的解决方案,而不使用Reachability。我在blog上找到了它。
在项目中创建一个名为TokenParam annotation = parameter.getAnnotation(TokenParam.class);
if (annotation != null && paramType.isAssignableFrom(Token.class)) {
return tokenFactory.clone(annotation.someAttribute());
}
的新Swift文件(例如)。将此代码粘贴到该文件中:
Network.swift
然后,您可以使用以下方法检查项目中的任何位置的连接:
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
}
}
答案 11 :(得分:0)
编辑:这不适用于网络网址(请参阅评论)
从iOS 5开始,有一个新的NSURL实例方法:
- (BOOL)checkResourceIsReachableAndReturnError:(NSError **)error
将其指向您关注的网站或将其指向apple.com;我认为这是一个新的单线电话,看看互联网是否正在您的设备上运行。
答案 12 :(得分:0)
我也对可用的互联网检查选项不满意(为什么这不是原生API?!?!)
我自己的问题是100%丢包 - 当设备连接到路由器但路由器未连接到Internet时。可达性和其他可能会持续多年。我创建了一个实用程序单例类来通过添加异步超时来处理它。它在我的应用程序中工作正常。希望能帮助到你。这是github上的链接:
答案 13 :(得分:0)
检查(iOS)Xcode 8.2,Swift 3.0中的Internet连接可用性
这是检查网络可用性的简单方法。 我设法将它翻译成Swift 2.0,这里是最终的代码。 现有的Apple Reachability类和其他第三方库似乎过于复杂,无法转换为Swift。
适用于3G和WiFi连接。
不要忘记将“SystemConfiguration.framework”添加到项目构建器中。
//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")
}
答案 14 :(得分:0)
Apple的Reachability的替换重写了在Swift中的与闭包,灵感来自tonymillion:https://github.com/ashleymills/Reachability.swift
将文件Reachability.swift
放入项目中。或者,使用CocoaPods或Carthage - 请参阅项目自述文件的安装部分。
获取有关网络连接的通知:
//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")
}
并停止通知
reachability.stopNotifier()
答案 15 :(得分:0)
如果您已经在所有RESTful Api中使用 Alamofire ,那么您可以从中受益匪浅。
您可以将以下类添加到您的应用中,并调用MNNetworkUtils.main.isConnected()
获取其是否已连接的布尔值。
#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
}
}
这是一个单身人士课程。每当用户连接或断开网络时,它都会正确覆盖self.reachable
为真/假,因为我们开始在单例初始化时监听NetworkReachabilityManager
。
另外,为了监控可达性,您需要提供一个主机,目前我正在使用google.com
随时更改为任何其他主机或其中一个主机(如果需要)。