李洪强iOS开发之使用 Reachability 检测网络

时间:2023-11-25 11:45:08

1.iOS平台是按照一直有网络连接的思路来设计的,开发者利用这一特点创造了很多优秀的第三方应用。

大多数的iOS应用都需要联网,甚至有些应用严重依赖网络,没有网络就无法正常工作。

2.在你的应用尝试通过网络获取数据之前,你需要知道当前设备是否知道连接上了网络,

甚至有时候你可能还需要知道当前网路是由wifi还是由移动蜂窝网络提供的。

3.“在网络访问失败的时候,应用没有做出适当的提示”是苹果的iOS审核团队拒绝一个应用的常见理由。

苹果要求你必须先检测网络连接状态,当网络不可用的时候以某种方式告知用户,或者用其他优雅的方式进行处理。

***********************

Reachability类:

1.这个类用于检测当前网络状态,它不是SDK的一部分,可以在iOS Developer Library里找到这份代码。

从苹果网站上下载Reachability.zip文件,解压之。

2.重用Reachability类

    (1)把Reachability.h和Reachability.m文件拖到项目中。

(2)添加框架:SystemConfiguration.framework。

3.同步的Reachability

(1)使用同步的方式是比较简单,导入Reachability.h头文件,然后通过代码检查网络:

#import “Reachability.h”

。。。some code omitted…

Reachability *reach = [Reachability reachabilityForInternetConnection];

NetworkStatus status = [reach currentReachabilityStatus];

   (2)通过检查某个主机能否访问来判断当前网络是否可用:

Reachability *reach = [Reachability reachabilityWithHostName:@“www.apple.com”];

NetworkStatus status = [reach currentReachabilityStatus];

 (3)案例:

创建一个工程,并添加Reachability.h和Reachability.m到工程中,并链接SystemConfiguration.framework.

在AppDelegate.h头文件中导入Reachability.h,并添加一个实例方法。如图:

    李洪强iOS开发之使用 Reachability 检测网络

在AppDelegate.m中这样实现:如图:

李洪强iOS开发之使用 Reachability 检测网络

4.异步的Reachability

(1)异步的方式稍微复杂,不过通过这种方式可以来订阅实时的网络状态变化通知。导入Reachability.h头文件,然后注册一个对象来订阅网络状态变化的信息,网络状态变化的信息名称为kReachabilityChanged-Notification.如下:

[[NSNotificationCenter defaultCenter] addObserver:self

selector:@selector(reachabilityChanged:)

name:kReachabilityChangedNotification

object:nil];

(2)你需要创建一个Reachability对象实例并开始向外发布网络状态变化的消息:

Reachability *reach = [[Reachability reachabilityWithHostName:@“www.apple.com”] retain];

[reach startNotifier];

(3)当网络状态发生变化的时候,Reachability对象将调用reachabilityChanged:方法,可以在这个方法里面获取当前的网络状态,然后做相应的处理。

- (void)reachabilityChanged:(NSNotification *)notification{

Reachability *reach = [notification object];

if([reach isKindOfClass:[Reachability class]]){

NetworkStatus status = [reach currentReachabilityStatus];

//Insert your code here

}

}

****************************

5.原生 Reachability API

前面将的Reachability类实际上是苹果公司对SCNetworkReachability API的封装,这个API定义在SystemConfigure.framework库中。如果有其他特别的需求,也可以直接使用这个原生的SCNetworkReachability类。

.h 和.m文件

李洪强iOS开发之使用 Reachability 检测网络

李洪强iOS开发之使用 Reachability 检测网络

 //-------------------------.m---------------------------------------------------

 //-------------------------.m---------------------------------------------------

李洪强iOS开发之使用 Reachability 检测网络

李洪强iOS开发之使用 Reachability 检测网络

李洪强iOS开发之使用 Reachability 检测网络

李洪强iOS开发之使用 Reachability 检测网络

李洪强iOS开发之使用 Reachability 检测网络

李洪强iOS开发之使用 Reachability 检测网络

李洪强iOS开发之使用 Reachability 检测网络

李洪强iOS开发之使用 Reachability 检测网络

李洪强iOS开发之使用 Reachability 检测网络

李洪强iOS开发之使用 Reachability 检测网络

李洪强iOS开发之使用 Reachability 检测网络

/*

File: Reachability.h

Copyright (C) 2014 Apple Inc. All Rights Reserved.

*/

#import <Foundation/Foundation.h>

#import <SystemConfiguration/SystemConfiguration.h>

#import <netinet/in.h>

typedef enum : NSInteger {

NotReachable = 0,

ReachableViaWiFi,

ReachableViaWWAN

} NetworkStatus;

extern NSString *kReachabilityChangedNotification;

@interface Reachability : NSObject

/*!

*

用于检查给定主机名的可达性。

*/

+ (instancetype)reachabilityWithHostName:(NSString *)hostName;

/*!

*

用来检查给定IP地址的可达性。

*/

+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress;

/*!

*

检查是否违约路is available。should be used by应用that do not connect to a particular主机。

*/

+ (instancetype)reachabilityForInternetConnection;

/*!

* 检查是否一个本地无线连接是可用的。

*/

+ (instancetype)reachabilityForLocalWiFi;

/*!

*开始在当前运行的循环上侦听可达性通知。

*/

- (BOOL)startNotifier;

- (void)stopNotifier;

- (NetworkStatus)currentReachabilityStatus;

/*!

* WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand.

*广域网可能是可用的,但不主动到连接已经建立。无线网络可能需要连接VPN的需求。

*/

- (BOOL)connectionRequired;

@end

 //-------------------------.m---------------------------------------------------

 //-------------------------.m---------------------------------------------------

/*

File: Reachability.m

Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.

Version: 3.5

Copyright (C) 2014 Apple Inc. All Rights Reserved.

*/

#import <arpa/inet.h>

#import <ifaddrs.h>

#import <netdb.h>

#import <sys/socket.h>

#import <CoreFoundation/CoreFoundation.h>

#import "Reachability.h"

NSString *kReachabilityChangedNotification = @"kNetworkReachabilityChangedNotification";

#pragma mark - Supporting functions

#define kShouldPrintReachabilityFlags 1

static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)

{

#if kShouldPrintReachabilityFlags

NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",

(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',

(flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',

(flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',

(flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',

(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',

(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',

(flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',

(flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',

(flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',

comment

);

#endif

}

static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)

{

#pragma unused (target, flags)

NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");

NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");

Reachability* noteObject = (__bridge Reachability *)info;

// Post a notification to notify the client that the network reachability changed.

[[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];

}

#pragma mark - Reachability implementation

@implementation Reachability

{

BOOL _alwaysReturnLocalWiFiStatus; //default is NO

SCNetworkReachabilityRef _reachabilityRef;

}

+ (instancetype)reachabilityWithHostName:(NSString *)hostName

{

Reachability* returnValue = NULL;

SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);

if (reachability != NULL)

{

returnValue= [[self alloc] init];

if (returnValue != NULL)

{

returnValue->_reachabilityRef = reachability;

returnValue->_alwaysReturnLocalWiFiStatus = NO;

}

}

return returnValue;

}

+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress

{

SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);

Reachability* returnValue = NULL;

if (reachability != NULL)

{

returnValue = [[self alloc] init];

if (returnValue != NULL)

{

returnValue->_reachabilityRef = reachability;

returnValue->_alwaysReturnLocalWiFiStatus = NO;

}

}

return returnValue;

}

+ (instancetype)reachabilityForInternetConnection

{

struct sockaddr_in zeroAddress;

bzero(&zeroAddress, sizeof(zeroAddress));

zeroAddress.sin_len = sizeof(zeroAddress);

zeroAddress.sin_family = AF_INET;

return [self reachabilityWithAddress:&zeroAddress];

}

+ (instancetype)reachabilityForLocalWiFi

{

struct sockaddr_in localWifiAddress;

bzero(&localWifiAddress, sizeof(localWifiAddress));

localWifiAddress.sin_len = sizeof(localWifiAddress);

localWifiAddress.sin_family = AF_INET;

// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0.

localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);

Reachability* returnValue = [self reachabilityWithAddress: &localWifiAddress];

if (returnValue != NULL)

{

returnValue->_alwaysReturnLocalWiFiStatus = YES;

}

return returnValue;

}

#pragma mark - Start and stop notifier

- (BOOL)startNotifier

{

BOOL returnValue = NO;

SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};

if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context))

{

if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))

{

returnValue = YES;

}

}

return returnValue;

}

- (void)stopNotifier

{

if (_reachabilityRef != NULL)

{

SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);

}

}

- (void)dealloc

{

[self stopNotifier];

if (_reachabilityRef != NULL)

{

CFRelease(_reachabilityRef);

}

}

#pragma mark - Network Flag Handling

- (NetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags

{

PrintReachabilityFlags(flags, "localWiFiStatusForFlags");

NetworkStatus returnValue = NotReachable;

if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))

{

returnValue = ReachableViaWiFi;

}

return returnValue;

}

- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags

{

PrintReachabilityFlags(flags, "networkStatusForFlags");

if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)

{

// The target host is not reachable.

return NotReachable;

}

NetworkStatus returnValue = NotReachable;

if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)

{

/*

If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi...

*/

returnValue = ReachableViaWiFi;

}

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

*/

returnValue = ReachableViaWiFi;

}

}

if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)

{

/*

... but WWAN connections are OK if the calling application is using the CFNetwork APIs.

*/

returnValue = ReachableViaWWAN;

}

return returnValue;

}

- (BOOL)connectionRequired

{

NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");

SCNetworkReachabilityFlags flags;

if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))

{

return (flags & kSCNetworkReachabilityFlagsConnectionRequired);

}

return NO;

}

- (NetworkStatus)currentReachabilityStatus

{

NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef");

NetworkStatus returnValue = NotReachable;

SCNetworkReachabilityFlags flags;

if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))

{

if (_alwaysReturnLocalWiFiStatus)

{

returnValue = [self localWiFiStatusForFlags:flags];

}

else

{

returnValue = [self networkStatusForFlags:flags];

}

}

return returnValue;

}

@end