ZFReachabilityManager.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. //
  2. // ZFReachabilityManager.m
  3. // ZFPlayer
  4. //
  5. // Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights
  10. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. // copies of the Software, and to permit persons to whom the Software is
  12. // furnished to do so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in
  15. // all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. // THE SOFTWARE.
  24. #import "ZFReachabilityManager.h"
  25. #if !TARGET_OS_WATCH
  26. #import <netinet/in.h>
  27. #import <netinet6/in6.h>
  28. #import <arpa/inet.h>
  29. #import <ifaddrs.h>
  30. #import <netdb.h>
  31. #import <CoreTelephony/CTTelephonyNetworkInfo.h>
  32. NSString * const ZFReachabilityDidChangeNotification = @"com.ZFPlayer.reachability.change";
  33. NSString * const ZFReachabilityNotificationStatusItem = @"ZFNetworkingReachabilityNotificationStatusItem";
  34. typedef void (^ZFReachabilityStatusBlock)(ZFReachabilityStatus status);
  35. NSString * ZFStringFromNetworkReachabilityStatus(ZFReachabilityStatus status) {
  36. switch (status) {
  37. case ZFReachabilityStatusNotReachable:
  38. return NSLocalizedStringFromTable(@"Not Reachable", @"ZFPlayer", nil);
  39. case ZFReachabilityStatusReachableViaWiFi:
  40. return NSLocalizedStringFromTable(@"Reachable via WiFi", @"ZFPlayer", nil);
  41. case ZFReachabilityStatusReachableVia2G:
  42. return NSLocalizedStringFromTable(@"Reachable via 2G", @"ZFPlayer", nil);
  43. case ZFReachabilityStatusReachableVia3G:
  44. return NSLocalizedStringFromTable(@"Reachable via 3G", @"ZFPlayer", nil);
  45. case ZFReachabilityStatusReachableVia4G:
  46. return NSLocalizedStringFromTable(@"Reachable via 4G", @"ZFPlayer", nil);
  47. case ZFReachabilityStatusUnknown:
  48. default:
  49. return NSLocalizedStringFromTable(@"Unknown", @"ZFPlayer", nil);
  50. }
  51. }
  52. static ZFReachabilityStatus ZFReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
  53. BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
  54. BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
  55. BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
  56. BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
  57. BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
  58. ZFReachabilityStatus status = ZFReachabilityStatusUnknown;
  59. if (isNetworkReachable == NO) {
  60. status = ZFReachabilityStatusNotReachable;
  61. }
  62. #if TARGET_OS_IPHONE
  63. else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
  64. CTTelephonyNetworkInfo * info = [[CTTelephonyNetworkInfo alloc] init];
  65. NSString *currentRadioAccessTechnology = info.currentRadioAccessTechnology;
  66. if (currentRadioAccessTechnology) {
  67. if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
  68. status = ZFReachabilityStatusReachableVia4G;
  69. } else if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] || [currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS]) {
  70. status = ZFReachabilityStatusReachableVia2G;
  71. } else {
  72. status = ZFReachabilityStatusReachableVia3G;
  73. }
  74. }
  75. }
  76. #endif
  77. else {
  78. status = ZFReachabilityStatusReachableViaWiFi;
  79. }
  80. return status;
  81. }
  82. /**
  83. * Queue a status change notification for the main thread.
  84. *
  85. * This is done to ensure that the notifications are received in the same order
  86. * as they are sent. If notifications are sent directly, it is possible that
  87. * a queued notification (for an earlier status condition) is processed after
  88. * the later update, resulting in the listener being left in the wrong state.
  89. */
  90. static void ZFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, ZFReachabilityStatusBlock block) {
  91. ZFReachabilityStatus status = ZFReachabilityStatusForFlags(flags);
  92. dispatch_async(dispatch_get_main_queue(), ^{
  93. if (block) block(status);
  94. NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
  95. NSDictionary *userInfo = @{ ZFReachabilityNotificationStatusItem: @(status) };
  96. [notificationCenter postNotificationName:ZFReachabilityDidChangeNotification object:nil userInfo:userInfo];
  97. });
  98. }
  99. static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
  100. ZFPostReachabilityStatusChange(flags, (__bridge ZFReachabilityStatusBlock)info);
  101. }
  102. static const void * ZFReachabilityRetainCallback(const void *info) {
  103. return Block_copy(info);
  104. }
  105. static void ZFReachabilityReleaseCallback(const void *info) {
  106. if (info) {
  107. Block_release(info);
  108. }
  109. }
  110. @interface ZFReachabilityManager ()
  111. @property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
  112. @property (readwrite, nonatomic, assign) ZFReachabilityStatus networkReachabilityStatus;
  113. @property (readwrite, nonatomic, copy) ZFReachabilityStatusBlock networkReachabilityStatusBlock;
  114. @end
  115. @implementation ZFReachabilityManager
  116. + (instancetype)sharedManager {
  117. static ZFReachabilityManager *_sharedManager = nil;
  118. static dispatch_once_t onceToken;
  119. dispatch_once(&onceToken, ^{
  120. _sharedManager = [self manager];
  121. });
  122. return _sharedManager;
  123. }
  124. + (instancetype)managerForDomain:(NSString *)domain {
  125. SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
  126. ZFReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
  127. CFRelease(reachability);
  128. return manager;
  129. }
  130. + (instancetype)managerForAddress:(const void *)address {
  131. SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
  132. ZFReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
  133. CFRelease(reachability);
  134. return manager;
  135. }
  136. + (instancetype)manager {
  137. #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)
  138. struct sockaddr_in6 address;
  139. bzero(&address, sizeof(address));
  140. address.sin6_len = sizeof(address);
  141. address.sin6_family = AF_INET6;
  142. #else
  143. struct sockaddr_in address;
  144. bzero(&address, sizeof(address));
  145. address.sin_len = sizeof(address);
  146. address.sin_family = AF_INET;
  147. #endif
  148. return [self managerForAddress:&address];
  149. }
  150. - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
  151. self = [super init];
  152. if (!self) {
  153. return nil;
  154. }
  155. _networkReachability = CFRetain(reachability);
  156. self.networkReachabilityStatus = ZFReachabilityStatusUnknown;
  157. return self;
  158. }
  159. - (instancetype)init NS_UNAVAILABLE
  160. {
  161. return nil;
  162. }
  163. - (void)dealloc {
  164. [self stopMonitoring];
  165. if (_networkReachability != NULL) {
  166. CFRelease(_networkReachability);
  167. }
  168. }
  169. #pragma mark -
  170. - (BOOL)isReachable {
  171. return [self isReachableViaWWAN] || [self isReachableViaWiFi];
  172. }
  173. - (BOOL)isReachableViaWWAN {
  174. return (self.networkReachabilityStatus == ZFReachabilityStatusReachableVia2G ||self.networkReachabilityStatus == ZFReachabilityStatusReachableVia3G || self.networkReachabilityStatus == ZFReachabilityStatusReachableVia4G);
  175. }
  176. - (BOOL)isReachableViaWiFi {
  177. return self.networkReachabilityStatus == ZFReachabilityStatusReachableViaWiFi;
  178. }
  179. #pragma mark -
  180. - (void)startMonitoring {
  181. [self stopMonitoring];
  182. if (!self.networkReachability) {
  183. return;
  184. }
  185. __weak __typeof(self)weakSelf = self;
  186. ZFReachabilityStatusBlock callback = ^(ZFReachabilityStatus status) {
  187. __strong __typeof(weakSelf)strongSelf = weakSelf;
  188. strongSelf.networkReachabilityStatus = status;
  189. if (strongSelf.networkReachabilityStatusBlock) {
  190. strongSelf.networkReachabilityStatusBlock(status);
  191. }
  192. };
  193. SCNetworkReachabilityContext context = {0, (__bridge void *)callback, ZFReachabilityRetainCallback, ZFReachabilityReleaseCallback, NULL};
  194. SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
  195. SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
  196. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
  197. SCNetworkReachabilityFlags flags;
  198. if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
  199. ZFPostReachabilityStatusChange(flags, callback);
  200. }
  201. });
  202. }
  203. - (void)stopMonitoring {
  204. if (!self.networkReachability) {
  205. return;
  206. }
  207. SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
  208. }
  209. #pragma mark -
  210. - (NSString *)localizedNetworkReachabilityStatusString {
  211. return ZFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
  212. }
  213. #pragma mark -
  214. - (void)setReachabilityStatusChangeBlock:(void (^)(ZFReachabilityStatus status))block {
  215. self.networkReachabilityStatusBlock = block;
  216. }
  217. #pragma mark - NSKeyValueObserving
  218. + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
  219. if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
  220. return [NSSet setWithObject:@"networkReachabilityStatus"];
  221. }
  222. return [super keyPathsForValuesAffectingValueForKey:key];
  223. }
  224. @end
  225. #endif