Reachability.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*
  2. Copyright (c) 2014, Ashley Mills
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are met:
  6. 1. Redistributions of source code must retain the above copyright notice, this
  7. list of conditions and the following disclaimer.
  8. 2. Redistributions in binary form must reproduce the above copyright notice,
  9. this list of conditions and the following disclaimer in the documentation
  10. and/or other materials provided with the distribution.
  11. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  12. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  13. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  14. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  15. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  16. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  17. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  18. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  19. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  20. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  21. POSSIBILITY OF SUCH DAMAGE.
  22. */
  23. let ReachabilityChangedNotification = "ReachabilityChangedNotification"
  24. class Reachability {
  25. typealias NetworkReachable = (Reachability) -> ()
  26. typealias NetworkUneachable = (Reachability) -> ()
  27. typealias StartNotifier = (Reachability) -> (Bool)
  28. enum NetworkStatus {
  29. // Apple NetworkStatus Compatible Names.
  30. case NotReachable, ReachableViaWiFi, ReachableViaWWAN
  31. }
  32. var reachabilityRef: SCNetworkReachability?
  33. var reachabilitySerialQueue: dispatch_queue_t?
  34. var reachabilityObject: AnyObject?
  35. var reachableBlock: NetworkReachable?
  36. var unreachableBlock: NetworkUneachable?
  37. var startNotifierBlock: StartNotifier?
  38. var reachableOnWWAN: Bool
  39. init(reachabilityRef: SCNetworkReachability) {
  40. reachableOnWWAN = true;
  41. self.reachabilityRef = reachabilityRef;
  42. }
  43. convenience init(hostname: String) {
  44. let ref = SCNetworkReachabilityCreateWithName(nil, (hostname as NSString).UTF8String).takeRetainedValue()
  45. self.init(reachabilityRef: ref)
  46. }
  47. class func reachabilityForInternetConnection() -> Reachability {
  48. var zeroAddress = sockaddr_in(sin_len: __uint8_t(0), sin_family: sa_family_t(0), sin_port: in_port_t(0), sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
  49. zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
  50. zeroAddress.sin_family = sa_family_t(AF_INET)
  51. let ref = withUnsafePointer(&zeroAddress) {
  52. SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, UnsafePointer($0)).takeRetainedValue()
  53. }
  54. return Reachability(reachabilityRef: ref)
  55. }
  56. class func reachabilityForLocalWiFi() -> Reachability {
  57. var localWifiAddress: sockaddr_in = sockaddr_in(sin_len: __uint8_t(0), sin_family: sa_family_t(0), sin_port: in_port_t(0), sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
  58. localWifiAddress.sin_len = UInt8(sizeofValue(localWifiAddress))
  59. localWifiAddress.sin_family = sa_family_t(AF_INET)
  60. // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
  61. localWifiAddress.sin_addr.s_addr = in_addr_t(Int64(0xA9FE0000).bigEndian)
  62. let ref = withUnsafePointer(&localWifiAddress) {
  63. SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, UnsafePointer($0)).takeRetainedValue()
  64. }
  65. return Reachability(reachabilityRef: ref)
  66. }
  67. func startNotifier() -> Bool {
  68. reachabilityObject = self
  69. // First, we need to create a serial queue.
  70. // We allocate this once for the lifetime of the notifier.
  71. reachabilitySerialQueue = dispatch_queue_create("com.joylordsystems.reachability", nil)
  72. if reachabilitySerialQueue == nil {
  73. return false
  74. }
  75. // TODO:
  76. let callback:(SCNetworkReachability!, SCNetworkReachabilityFlags, UnsafeMutablePointer<Void>) -> () = { (target: SCNetworkReachability!, flags: SCNetworkReachabilityFlags, info: UnsafeMutablePointer<Void>) in
  77. self.reachabilityChanged(flags)
  78. }
  79. let p = UnsafeMutablePointer<(SCNetworkReachability!, SCNetworkReachabilityFlags, UnsafeMutablePointer<Void>) -> Void>.alloc(1)
  80. p.initialize(callback)
  81. let cp = COpaquePointer(p) // convert UnsafeMutablePointer to COpaquePointer
  82. let fp = CFunctionPointer<(SCNetworkReachability!, SCNetworkReachabilityFlags, UnsafeMutablePointer<Void>) -> Void>(cp) // convert COpaquePointer to CFunctionPointer
  83. if SCNetworkReachabilitySetCallback(self.reachabilityRef, fp, nil) != 0 {
  84. println("SCNetworkReachabilitySetCallback() failed: \(SCErrorString(SCError()))")
  85. // Clear out the dispatch queue
  86. reachabilitySerialQueue = nil;
  87. reachabilityObject = nil;
  88. return false;
  89. }
  90. // Set it as our reachability queue, which will retain the queue
  91. if SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) == 0
  92. {
  93. println("SCNetworkReachabilitySetDispatchQueue() failed: \(SCErrorString(SCError()))")
  94. // First stop, any callbacks!
  95. SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil)
  96. // Then clear out the dispatch queue.
  97. reachabilitySerialQueue = nil
  98. reachabilityObject = nil
  99. return false
  100. }
  101. return true;
  102. }
  103. func stopNotifier() {
  104. // First stop, any callbacks!
  105. SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil)
  106. // Unregister target from the GCD serial dispatch queue.
  107. SCNetworkReachabilitySetDispatchQueue(nil, nil);
  108. reachabilitySerialQueue = nil;
  109. reachabilityObject = nil;
  110. }
  111. func reachabilityChanged(flags: SCNetworkReachabilityFlags) {
  112. if isReachableWithFlags(flags) {
  113. if let block = reachableBlock {
  114. block(self)
  115. }
  116. } else {
  117. if let block = unreachableBlock {
  118. block(self)
  119. }
  120. }
  121. // this makes sure the change notification happens on the MAIN THREAD
  122. dispatch_async(dispatch_get_main_queue()) {
  123. NSNotificationCenter.defaultCenter().postNotificationName(ReachabilityChangedNotification, object:self)
  124. }
  125. }
  126. func isReachableWithFlags(flags: SCNetworkReachabilityFlags) -> Bool {
  127. let reachable = isReachable(flags)
  128. if reachable == 0 {
  129. return false
  130. }
  131. if isConnectionRequiredOrTransient(flags) {
  132. return false
  133. }
  134. #if TARGET_OS_IPHONE
  135. if isOnWWAN(flags) && !reachableOnWWAN {
  136. // We don't want to connect when on 3G.
  137. return false
  138. }
  139. #endif
  140. return true
  141. }
  142. func isReachableWithTest(test: (SCNetworkReachabilityFlags) -> (Bool)) -> Bool {
  143. var flags: SCNetworkReachabilityFlags = 0
  144. let gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef, &flags) != 0
  145. if gotFlags {
  146. return test(flags)
  147. }
  148. return false
  149. }
  150. func isReachable() -> Bool {
  151. return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
  152. return self.isReachableWithFlags(flags)
  153. })
  154. }
  155. func isReachableViaWWAN() -> Bool {
  156. #if TARGET_OS_IPHONE
  157. return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
  158. // Check we're REACHABLE
  159. if self.isReachable(flags) {
  160. // Now, check we're on WWAN
  161. if self.isOnWWAN(flags) {
  162. return true
  163. }
  164. }
  165. return false
  166. })
  167. #endif
  168. return false
  169. }
  170. func isReachableViaWiFi() -> Bool {
  171. return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
  172. // Check we're reachable
  173. if self.isReachable(flags) {
  174. #if TARGET_OS_IPHONE
  175. // Check we're NOT on WWAN
  176. if self.isOnWWAN(flags) {
  177. return false
  178. }
  179. #endif
  180. return true
  181. }
  182. return false
  183. })
  184. }
  185. // WWAN may be available, but not active until a connection has been established.
  186. // WiFi may require a connection for VPN on Demand.
  187. func isConnectionRequired() -> Bool {
  188. return connectionRequired()
  189. }
  190. func connectionRequired() -> Bool {
  191. return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
  192. return self.isConnectionRequired(flags)
  193. })
  194. }
  195. // Dynamic, on demand connection?
  196. func isConnectionOnDemand() -> Bool {
  197. return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
  198. return self.isConnectionRequired(flags) && self.isConnectionOnTrafficOrDemand(flags)
  199. })
  200. }
  201. // Is user intervention required?
  202. func isInterventionRequired() -> Bool {
  203. return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
  204. return self.isConnectionRequired(flags) && self.isInterventionRequired(flags)
  205. })
  206. }
  207. func isOnWWAN(flags: SCNetworkReachabilityFlags) -> Bool {
  208. return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsWWAN) != 0
  209. }
  210. func isReachable(flags: SCNetworkReachabilityFlags) -> Bool {
  211. return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsReachable) != 0
  212. }
  213. func isConnectionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
  214. return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionRequired) != 0
  215. }
  216. func isInterventionRequired(flags: SCNetworkReachabilityFlags) -> Bool {
  217. return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsInterventionRequired) != 0
  218. }
  219. func isConnectionOnTraffic(flags: SCNetworkReachabilityFlags) -> Bool {
  220. return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0
  221. }
  222. func isConnectionOnDemand(flags: SCNetworkReachabilityFlags) -> Bool {
  223. return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnDemand) != 0
  224. }
  225. func isConnectionOnTrafficOrDemand(flags: SCNetworkReachabilityFlags) -> Bool {
  226. return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand) != 0
  227. }
  228. func isTransientConnection(flags: SCNetworkReachabilityFlags) -> Bool {
  229. return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsTransientConnection) != 0
  230. }
  231. func isLocalAddress(flags: SCNetworkReachabilityFlags) -> Bool {
  232. return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsLocalAddress) != 0
  233. }
  234. func isDirect(flags: SCNetworkReachabilityFlags) -> Bool {
  235. return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsDirect) != 0
  236. }
  237. func isConnectionRequiredOrTransient(flags: SCNetworkReachabilityFlags) -> Bool {
  238. let testcase = SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
  239. return flags & testcase == testcase
  240. }
  241. // MARK: - *** xx methods ***
  242. var currentReachabilityStatus: NetworkStatus {
  243. if isReachable() {
  244. if isReachableViaWiFi() {
  245. return .ReachableViaWiFi
  246. }
  247. #if TARGET_OS_IPHONE
  248. return .ReachableViaWWAN;a
  249. #endif
  250. }
  251. return .NotReachable
  252. }
  253. var reachabilityFlags: SCNetworkReachabilityFlags {
  254. var flags: SCNetworkReachabilityFlags = 0
  255. let gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef, &flags) != 0
  256. if gotFlags {
  257. return flags
  258. }
  259. return 0
  260. }
  261. var currentReachabilityString: String {
  262. switch currentReachabilityStatus {
  263. case .ReachableViaWWAN:
  264. return NSLocalizedString("Cellular", comment: "")
  265. case .ReachableViaWiFi:
  266. return NSLocalizedString("WiFi", comment: "")
  267. case .NotReachable:
  268. return NSLocalizedString("No Connection", comment: "");
  269. }
  270. }
  271. var currentReachabilityFlags: String {
  272. #if TARGET_OS_IPHONE
  273. let W = isOnWWAN(reachabilityFlags) ? "W" : "-"
  274. #else
  275. let W = "X"
  276. #endif
  277. let R = isReachable(reachabilityFlags) ? "R" : "-"
  278. let c = isConnectionRequired(reachabilityFlags) ? "c" : "-"
  279. let t = isTransientConnection(reachabilityFlags) ? "t" : "-"
  280. let i = isInterventionRequired(reachabilityFlags) ? "i" : "-"
  281. let C = isConnectionOnTraffic(reachabilityFlags) ? "C" : "-"
  282. let D = isConnectionOnDemand(reachabilityFlags) ? "D" : "-"
  283. let l = isLocalAddress(reachabilityFlags) ? "l" : "-"
  284. let d = isDirect(reachabilityFlags) ? "d" : "-"
  285. return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)"
  286. }
  287. deinit {
  288. stopNotifier()
  289. reachabilityRef = nil
  290. reachableBlock = nil
  291. unreachableBlock = nil
  292. }
  293. }