AFNetworkReachabilityManager.m 9.7 KB

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