AFNetworkReachabilityManager.m 9.9 KB

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