YYReachability.m 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. //
  2. // YYReachability.m
  3. // YYKit <https://github.com/ibireme/YYKit>
  4. //
  5. // Created by ibireme on 15/2/6.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import "YYReachability.h"
  12. #import <objc/message.h>
  13. #import <CoreTelephony/CTTelephonyNetworkInfo.h>
  14. static YYReachabilityStatus YYReachabilityStatusFromFlags(SCNetworkReachabilityFlags flags, BOOL allowWWAN) {
  15. if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
  16. return YYReachabilityStatusNone;
  17. }
  18. if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
  19. (flags & kSCNetworkReachabilityFlagsTransientConnection)) {
  20. return YYReachabilityStatusNone;
  21. }
  22. if ((flags & kSCNetworkReachabilityFlagsIsWWAN) && allowWWAN) {
  23. return YYReachabilityStatusWWAN;
  24. }
  25. return YYReachabilityStatusWiFi;
  26. }
  27. static void YYReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) {
  28. YYReachability *self = ((__bridge YYReachability *)info);
  29. if (self.notifyBlock) {
  30. dispatch_async(dispatch_get_main_queue(), ^{
  31. self.notifyBlock(self);
  32. });
  33. }
  34. }
  35. @interface YYReachability ()
  36. @property (nonatomic, assign) SCNetworkReachabilityRef ref;
  37. @property (nonatomic, assign) BOOL scheduled;
  38. @property (nonatomic, assign) BOOL allowWWAN;
  39. @property (nonatomic, strong) CTTelephonyNetworkInfo *networkInfo;
  40. @end
  41. @implementation YYReachability
  42. + (dispatch_queue_t)sharedQueue {
  43. static dispatch_queue_t queue;
  44. static dispatch_once_t onceToken;
  45. dispatch_once(&onceToken, ^{
  46. queue = dispatch_queue_create("com.ibireme.yykit.reachability", DISPATCH_QUEUE_SERIAL);
  47. });
  48. return queue;
  49. }
  50. - (instancetype)init {
  51. /*
  52. See Apple's Reachability implementation and readme:
  53. The address 0.0.0.0, which reachability treats as a special token that
  54. causes it to actually monitor the general routing status of the device,
  55. both IPv4 and IPv6.
  56. https://developer.apple.com/library/ios/samplecode/Reachability/Listings/ReadMe_md.html#//apple_ref/doc/uid/DTS40007324-ReadMe_md-DontLinkElementID_11
  57. */
  58. struct sockaddr_in zero_addr;
  59. bzero(&zero_addr, sizeof(zero_addr));
  60. zero_addr.sin_len = sizeof(zero_addr);
  61. zero_addr.sin_family = AF_INET;
  62. SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)&zero_addr);
  63. return [self initWithRef:ref];
  64. }
  65. - (instancetype)initWithRef:(SCNetworkReachabilityRef)ref {
  66. if (!ref) return nil;
  67. self = super.init;
  68. if (!self) return nil;
  69. _ref = ref;
  70. _allowWWAN = YES;
  71. if (NSFoundationVersionNumber >= NSFoundationVersionNumber_iOS_7_0) {
  72. _networkInfo = [CTTelephonyNetworkInfo new];
  73. }
  74. return self;
  75. }
  76. - (void)dealloc {
  77. self.notifyBlock = nil;
  78. self.scheduled = NO;
  79. CFRelease(self.ref);
  80. }
  81. - (void)setScheduled:(BOOL)scheduled {
  82. if (_scheduled == scheduled) return;
  83. _scheduled = scheduled;
  84. if (scheduled) {
  85. SCNetworkReachabilityContext context = { 0, (__bridge void *)self, NULL, NULL, NULL };
  86. SCNetworkReachabilitySetCallback(self.ref, YYReachabilityCallback, &context);
  87. SCNetworkReachabilitySetDispatchQueue(self.ref, [self.class sharedQueue]);
  88. } else {
  89. SCNetworkReachabilitySetDispatchQueue(self.ref, NULL);
  90. }
  91. }
  92. - (SCNetworkReachabilityFlags)flags {
  93. SCNetworkReachabilityFlags flags = 0;
  94. SCNetworkReachabilityGetFlags(self.ref, &flags);
  95. return flags;
  96. }
  97. - (YYReachabilityStatus)status {
  98. return YYReachabilityStatusFromFlags(self.flags, self.allowWWAN);
  99. }
  100. - (YYReachabilityWWANStatus)wwanStatus {
  101. if (!self.networkInfo) return YYReachabilityWWANStatusNone;
  102. NSString *status = self.networkInfo.currentRadioAccessTechnology;
  103. if (!status) return YYReachabilityWWANStatusNone;
  104. static NSDictionary *dic;
  105. static dispatch_once_t onceToken;
  106. dispatch_once(&onceToken, ^{
  107. dic = @{CTRadioAccessTechnologyGPRS : @(YYReachabilityWWANStatus2G), // 2.5G 171Kbps
  108. CTRadioAccessTechnologyEdge : @(YYReachabilityWWANStatus2G), // 2.75G 384Kbps
  109. CTRadioAccessTechnologyWCDMA : @(YYReachabilityWWANStatus3G), // 3G 3.6Mbps/384Kbps
  110. CTRadioAccessTechnologyHSDPA : @(YYReachabilityWWANStatus3G), // 3.5G 14.4Mbps/384Kbps
  111. CTRadioAccessTechnologyHSUPA : @(YYReachabilityWWANStatus3G), // 3.75G 14.4Mbps/5.76Mbps
  112. CTRadioAccessTechnologyCDMA1x : @(YYReachabilityWWANStatus3G), // 2.5G
  113. CTRadioAccessTechnologyCDMAEVDORev0 : @(YYReachabilityWWANStatus3G),
  114. CTRadioAccessTechnologyCDMAEVDORevA : @(YYReachabilityWWANStatus3G),
  115. CTRadioAccessTechnologyCDMAEVDORevB : @(YYReachabilityWWANStatus3G),
  116. CTRadioAccessTechnologyeHRPD : @(YYReachabilityWWANStatus3G),
  117. CTRadioAccessTechnologyLTE : @(YYReachabilityWWANStatus4G)}; // LTE:3.9G 150M/75M LTE-Advanced:4G 300M/150M
  118. });
  119. NSNumber *num = dic[status];
  120. if (num != nil) return num.unsignedIntegerValue;
  121. else return YYReachabilityWWANStatusNone;
  122. }
  123. - (BOOL)isReachable {
  124. return self.status != YYReachabilityStatusNone;
  125. }
  126. + (instancetype)reachability {
  127. return self.new;
  128. }
  129. + (instancetype)reachabilityForLocalWifi {
  130. struct sockaddr_in localWifiAddress;
  131. bzero(&localWifiAddress, sizeof(localWifiAddress));
  132. localWifiAddress.sin_len = sizeof(localWifiAddress);
  133. localWifiAddress.sin_family = AF_INET;
  134. localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
  135. YYReachability *one = [self reachabilityWithAddress:(const struct sockaddr *)&localWifiAddress];
  136. one.allowWWAN = NO;
  137. return one;
  138. }
  139. + (instancetype)reachabilityWithHostname:(NSString *)hostname {
  140. SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
  141. return [[self alloc] initWithRef:ref];
  142. }
  143. + (instancetype)reachabilityWithAddress:(const struct sockaddr *)hostAddress {
  144. SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);
  145. return [[self alloc] initWithRef:ref];
  146. }
  147. - (void)setNotifyBlock:(void (^)(YYReachability *reachability))notifyBlock {
  148. _notifyBlock = [notifyBlock copy];
  149. self.scheduled = (self.notifyBlock != nil);
  150. }
  151. @end