2
0

WFAFNetworkReachabilityManager.m 9.7 KB

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