AFNetworkReachabilityManager.m 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // AFNetworkReachabilityManager.m
  2. // Copyright (c) 2011–2015 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 "AFNetworkReachabilityManager.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 AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
  29. NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
  30. typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
  31. NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {
  32. switch (status) {
  33. case AFNetworkReachabilityStatusNotReachable:
  34. return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil);
  35. case AFNetworkReachabilityStatusReachableViaWWAN:
  36. return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil);
  37. case AFNetworkReachabilityStatusReachableViaWiFi:
  38. return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil);
  39. case AFNetworkReachabilityStatusUnknown:
  40. default:
  41. return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil);
  42. }
  43. }
  44. static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(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. AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
  51. if (isNetworkReachable == NO) {
  52. status = AFNetworkReachabilityStatusNotReachable;
  53. }
  54. #if TARGET_OS_IPHONE
  55. else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
  56. status = AFNetworkReachabilityStatusReachableViaWWAN;
  57. }
  58. #endif
  59. else {
  60. status = AFNetworkReachabilityStatusReachableViaWiFi;
  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 AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
  73. AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
  74. dispatch_async(dispatch_get_main_queue(), ^{
  75. if (block) {
  76. block(status);
  77. }
  78. NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
  79. NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
  80. [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
  81. });
  82. }
  83. static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
  84. AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
  85. }
  86. static const void * AFNetworkReachabilityRetainCallback(const void *info) {
  87. return Block_copy(info);
  88. }
  89. static void AFNetworkReachabilityReleaseCallback(const void *info) {
  90. if (info) {
  91. Block_release(info);
  92. }
  93. }
  94. @interface AFNetworkReachabilityManager ()
  95. @property (readwrite, nonatomic, strong) id networkReachability;
  96. @property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
  97. @property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;
  98. @end
  99. @implementation AFNetworkReachabilityManager
  100. + (instancetype)sharedManager {
  101. static AFNetworkReachabilityManager *_sharedManager = nil;
  102. static dispatch_once_t onceToken;
  103. dispatch_once(&onceToken, ^{
  104. struct sockaddr_in address;
  105. bzero(&address, sizeof(address));
  106. address.sin_len = sizeof(address);
  107. address.sin_family = AF_INET;
  108. _sharedManager = [self managerForAddress:&address];
  109. });
  110. return _sharedManager;
  111. }
  112. #ifndef __clang_analyzer__
  113. + (instancetype)managerForDomain:(NSString *)domain {
  114. SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
  115. AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
  116. return manager;
  117. }
  118. #endif
  119. #ifndef __clang_analyzer__
  120. + (instancetype)managerForAddress:(const void *)address {
  121. SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
  122. AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
  123. return manager;
  124. }
  125. #endif
  126. - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
  127. self = [super init];
  128. if (!self) {
  129. return nil;
  130. }
  131. self.networkReachability = CFBridgingRelease(reachability);
  132. self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
  133. return self;
  134. }
  135. - (instancetype)init NS_UNAVAILABLE
  136. {
  137. return nil;
  138. }
  139. - (void)dealloc {
  140. [self stopMonitoring];
  141. }
  142. #pragma mark -
  143. - (BOOL)isReachable {
  144. return [self isReachableViaWWAN] || [self isReachableViaWiFi];
  145. }
  146. - (BOOL)isReachableViaWWAN {
  147. return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;
  148. }
  149. - (BOOL)isReachableViaWiFi {
  150. return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;
  151. }
  152. #pragma mark -
  153. - (void)startMonitoring {
  154. [self stopMonitoring];
  155. if (!self.networkReachability) {
  156. return;
  157. }
  158. __weak __typeof(self)weakSelf = self;
  159. AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
  160. __strong __typeof(weakSelf)strongSelf = weakSelf;
  161. strongSelf.networkReachabilityStatus = status;
  162. if (strongSelf.networkReachabilityStatusBlock) {
  163. strongSelf.networkReachabilityStatusBlock(status);
  164. }
  165. };
  166. id networkReachability = self.networkReachability;
  167. SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
  168. SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context);
  169. SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
  170. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
  171. SCNetworkReachabilityFlags flags;
  172. if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) {
  173. AFPostReachabilityStatusChange(flags, callback);
  174. }
  175. });
  176. }
  177. - (void)stopMonitoring {
  178. if (!self.networkReachability) {
  179. return;
  180. }
  181. SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
  182. }
  183. #pragma mark -
  184. - (NSString *)localizedNetworkReachabilityStatusString {
  185. return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
  186. }
  187. #pragma mark -
  188. - (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
  189. self.networkReachabilityStatusBlock = block;
  190. }
  191. #pragma mark - NSKeyValueObserving
  192. + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
  193. if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
  194. return [NSSet setWithObject:@"networkReachabilityStatus"];
  195. }
  196. return [super keyPathsForValuesAffectingValueForKey:key];
  197. }
  198. @end
  199. #endif