123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- //
- // ZFReachabilityManager.m
- // ZFPlayer
- //
- // Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #import "ZFReachabilityManager.h"
- #if !TARGET_OS_WATCH
- #import <netinet/in.h>
- #import <netinet6/in6.h>
- #import <arpa/inet.h>
- #import <ifaddrs.h>
- #import <netdb.h>
- #import <CoreTelephony/CTTelephonyNetworkInfo.h>
- NSString * const ZFReachabilityDidChangeNotification = @"com.ZFPlayer.reachability.change";
- NSString * const ZFReachabilityNotificationStatusItem = @"ZFNetworkingReachabilityNotificationStatusItem";
- typedef void (^ZFReachabilityStatusBlock)(ZFReachabilityStatus status);
- NSString * ZFStringFromNetworkReachabilityStatus(ZFReachabilityStatus status) {
- switch (status) {
- case ZFReachabilityStatusNotReachable:
- return NSLocalizedStringFromTable(@"Not Reachable", @"ZFPlayer", nil);
- case ZFReachabilityStatusReachableViaWiFi:
- return NSLocalizedStringFromTable(@"Reachable via WiFi", @"ZFPlayer", nil);
- case ZFReachabilityStatusReachableVia2G:
- return NSLocalizedStringFromTable(@"Reachable via 2G", @"ZFPlayer", nil);
- case ZFReachabilityStatusReachableVia3G:
- return NSLocalizedStringFromTable(@"Reachable via 3G", @"ZFPlayer", nil);
- case ZFReachabilityStatusReachableVia4G:
- return NSLocalizedStringFromTable(@"Reachable via 4G", @"ZFPlayer", nil);
- case ZFReachabilityStatusUnknown:
- default:
- return NSLocalizedStringFromTable(@"Unknown", @"ZFPlayer", nil);
- }
- }
- static ZFReachabilityStatus ZFReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
- BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
- BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
- BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
- BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
- BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
-
- ZFReachabilityStatus status = ZFReachabilityStatusUnknown;
- if (isNetworkReachable == NO) {
- status = ZFReachabilityStatusNotReachable;
- }
- #if TARGET_OS_IPHONE
- else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
- CTTelephonyNetworkInfo * info = [[CTTelephonyNetworkInfo alloc] init];
- NSString *currentRadioAccessTechnology = info.currentRadioAccessTechnology;
- if (currentRadioAccessTechnology) {
- if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
- status = ZFReachabilityStatusReachableVia4G;
- } else if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] || [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS]) {
- status = ZFReachabilityStatusReachableVia2G;
- } else {
- status = ZFReachabilityStatusReachableVia3G;
- }
- }
- }
- #endif
- else {
- status = ZFReachabilityStatusReachableViaWiFi;
- }
- return status;
-
- }
- /**
- * Queue a status change notification for the main thread.
- *
- * This is done to ensure that the notifications are received in the same order
- * as they are sent. If notifications are sent directly, it is possible that
- * a queued notification (for an earlier status condition) is processed after
- * the later update, resulting in the listener being left in the wrong state.
- */
- static void ZFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, ZFReachabilityStatusBlock block) {
- ZFReachabilityStatus status = ZFReachabilityStatusForFlags(flags);
- dispatch_async(dispatch_get_main_queue(), ^{
- if (block) block(status);
- NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
- NSDictionary *userInfo = @{ ZFReachabilityNotificationStatusItem: @(status) };
- [notificationCenter postNotificationName:ZFReachabilityDidChangeNotification object:nil userInfo:userInfo];
- });
- }
- static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
- ZFPostReachabilityStatusChange(flags, (__bridge ZFReachabilityStatusBlock)info);
- }
- static const void * ZFReachabilityRetainCallback(const void *info) {
- return Block_copy(info);
- }
- static void ZFReachabilityReleaseCallback(const void *info) {
- if (info) {
- Block_release(info);
- }
- }
- @interface ZFReachabilityManager ()
- @property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
- @property (readwrite, nonatomic, assign) ZFReachabilityStatus networkReachabilityStatus;
- @property (readwrite, nonatomic, copy) ZFReachabilityStatusBlock networkReachabilityStatusBlock;
- @end
- @implementation ZFReachabilityManager
- + (instancetype)sharedManager {
- static ZFReachabilityManager *_sharedManager = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- _sharedManager = [self manager];
- });
- return _sharedManager;
- }
- + (instancetype)managerForDomain:(NSString *)domain {
- SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
- ZFReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
- CFRelease(reachability);
- return manager;
- }
- + (instancetype)managerForAddress:(const void *)address {
- SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
- ZFReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
- CFRelease(reachability);
- return manager;
- }
- + (instancetype)manager {
- #if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
- struct sockaddr_in6 address;
- bzero(&address, sizeof(address));
- address.sin6_len = sizeof(address);
- address.sin6_family = AF_INET6;
- #else
- struct sockaddr_in address;
- bzero(&address, sizeof(address));
- address.sin_len = sizeof(address);
- address.sin_family = AF_INET;
- #endif
- return [self managerForAddress:&address];
- }
- - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
- self = [super init];
- if (!self) {
- return nil;
- }
- _networkReachability = CFRetain(reachability);
- self.networkReachabilityStatus = ZFReachabilityStatusUnknown;
-
- return self;
- }
- - (instancetype)init NS_UNAVAILABLE
- {
- return nil;
- }
- - (void)dealloc {
- [self stopMonitoring];
- if (_networkReachability != NULL) {
- CFRelease(_networkReachability);
- }
- }
- #pragma mark -
- - (BOOL)isReachable {
- return [self isReachableViaWWAN] || [self isReachableViaWiFi];
- }
- - (BOOL)isReachableViaWWAN {
- return (self.networkReachabilityStatus == ZFReachabilityStatusReachableVia2G ||self.networkReachabilityStatus == ZFReachabilityStatusReachableVia3G || self.networkReachabilityStatus == ZFReachabilityStatusReachableVia4G);
- }
- - (BOOL)isReachableViaWiFi {
- return self.networkReachabilityStatus == ZFReachabilityStatusReachableViaWiFi;
- }
- #pragma mark -
- - (void)startMonitoring {
- [self stopMonitoring];
- if (!self.networkReachability) {
- return;
- }
-
- __weak __typeof(self)weakSelf = self;
- ZFReachabilityStatusBlock callback = ^(ZFReachabilityStatus status) {
- __strong __typeof(weakSelf)strongSelf = weakSelf;
- strongSelf.networkReachabilityStatus = status;
- if (strongSelf.networkReachabilityStatusBlock) {
- strongSelf.networkReachabilityStatusBlock(status);
- }
- };
-
- SCNetworkReachabilityContext context = {0, (__bridge void *)callback, ZFReachabilityRetainCallback, ZFReachabilityReleaseCallback, NULL};
- SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
- SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
-
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
- SCNetworkReachabilityFlags flags;
- if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
- ZFPostReachabilityStatusChange(flags, callback);
- }
- });
- }
- - (void)stopMonitoring {
- if (!self.networkReachability) {
- return;
- }
-
- SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
- }
- #pragma mark -
- - (NSString *)localizedNetworkReachabilityStatusString {
- return ZFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
- }
- #pragma mark -
- - (void)setReachabilityStatusChangeBlock:(void (^)(ZFReachabilityStatus status))block {
- self.networkReachabilityStatusBlock = block;
- }
- #pragma mark - NSKeyValueObserving
- + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
- if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
- return [NSSet setWithObject:@"networkReachabilityStatus"];
- }
- return [super keyPathsForValuesAffectingValueForKey:key];
- }
- @end
- #endif
|