|
|
@@ -28,18 +28,18 @@ POSSIBILITY OF SUCH DAMAGE.
|
|
|
import SystemConfiguration
|
|
|
import Foundation
|
|
|
|
|
|
-let ReachabilityChangedNotification = "ReachabilityChangedNotification"
|
|
|
+public let ReachabilityChangedNotification = "ReachabilityChangedNotification"
|
|
|
|
|
|
-class Reachability: NSObject, Printable {
|
|
|
-
|
|
|
- typealias NetworkReachable = (Reachability) -> ()
|
|
|
- typealias NetworkUneachable = (Reachability) -> ()
|
|
|
+public class Reachability: NSObject, Printable {
|
|
|
+
|
|
|
+ public typealias NetworkReachable = (Reachability) -> ()
|
|
|
+ public typealias NetworkUneachable = (Reachability) -> ()
|
|
|
+
|
|
|
+ public enum NetworkStatus: Printable {
|
|
|
|
|
|
- enum NetworkStatus: Printable {
|
|
|
-
|
|
|
case NotReachable, ReachableViaWiFi, ReachableViaWWAN
|
|
|
-
|
|
|
- var description: String {
|
|
|
+
|
|
|
+ public var description: String {
|
|
|
switch self {
|
|
|
case .ReachableViaWWAN:
|
|
|
return "Cellular"
|
|
|
@@ -50,14 +50,14 @@ class Reachability: NSObject, Printable {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// MARK: - *** Public properties ***
|
|
|
-
|
|
|
- var whenReachable: NetworkReachable?
|
|
|
- var whenUnreachable: NetworkUneachable?
|
|
|
- var reachableOnWWAN: Bool
|
|
|
|
|
|
- var currentReachabilityStatus: NetworkStatus {
|
|
|
+ public var whenReachable: NetworkReachable?
|
|
|
+ public var whenUnreachable: NetworkUneachable?
|
|
|
+ public var reachableOnWWAN: Bool
|
|
|
+
|
|
|
+ public var currentReachabilityStatus: NetworkStatus {
|
|
|
if isReachable() {
|
|
|
if isReachableViaWiFi() {
|
|
|
return .ReachableViaWiFi
|
|
|
@@ -66,95 +66,95 @@ class Reachability: NSObject, Printable {
|
|
|
return .ReachableViaWWAN
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return .NotReachable
|
|
|
}
|
|
|
-
|
|
|
- var currentReachabilityString: String {
|
|
|
+
|
|
|
+ public var currentReachabilityString: String {
|
|
|
return "\(currentReachabilityStatus)"
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// MARK: - *** Initialisation methods ***
|
|
|
- convenience init(hostname: String) {
|
|
|
+ public convenience init(hostname: String) {
|
|
|
let ref = SCNetworkReachabilityCreateWithName(nil, (hostname as NSString).UTF8String).takeRetainedValue()
|
|
|
self.init(reachabilityRef: ref)
|
|
|
}
|
|
|
-
|
|
|
- class func reachabilityForInternetConnection() -> Reachability {
|
|
|
-
|
|
|
+
|
|
|
+ public class func reachabilityForInternetConnection() -> Reachability {
|
|
|
+
|
|
|
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))
|
|
|
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
|
|
|
zeroAddress.sin_family = sa_family_t(AF_INET)
|
|
|
-
|
|
|
+
|
|
|
let ref = withUnsafePointer(&zeroAddress) {
|
|
|
SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, UnsafePointer($0)).takeRetainedValue()
|
|
|
}
|
|
|
return Reachability(reachabilityRef: ref)
|
|
|
}
|
|
|
-
|
|
|
- class func reachabilityForLocalWiFi() -> Reachability {
|
|
|
-
|
|
|
+
|
|
|
+ public class func reachabilityForLocalWiFi() -> Reachability {
|
|
|
+
|
|
|
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))
|
|
|
localWifiAddress.sin_len = UInt8(sizeofValue(localWifiAddress))
|
|
|
localWifiAddress.sin_family = sa_family_t(AF_INET)
|
|
|
-
|
|
|
+
|
|
|
// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
|
|
|
let address: Int64 = 0xA9FE0000
|
|
|
localWifiAddress.sin_addr.s_addr = in_addr_t(address.bigEndian)
|
|
|
-
|
|
|
+
|
|
|
let ref = withUnsafePointer(&localWifiAddress) {
|
|
|
SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, UnsafePointer($0)).takeRetainedValue()
|
|
|
}
|
|
|
return Reachability(reachabilityRef: ref)
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// MARK: - *** Notifier methods ***
|
|
|
- func startNotifier() -> Bool {
|
|
|
-
|
|
|
+ public func startNotifier() -> Bool {
|
|
|
+
|
|
|
reachabilityObject = self
|
|
|
let reachability = self.reachabilityRef!
|
|
|
-
|
|
|
+
|
|
|
previousReachabilityFlags = reachabilityFlags
|
|
|
if let timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timer_queue) {
|
|
|
dispatch_source_set_timer(timer, dispatch_walltime(nil, 0), 500 * NSEC_PER_MSEC, 100 * NSEC_PER_MSEC)
|
|
|
dispatch_source_set_event_handler(timer, { [unowned self] in
|
|
|
self.timerFired()
|
|
|
- })
|
|
|
-
|
|
|
+ })
|
|
|
+
|
|
|
dispatch_timer = timer
|
|
|
dispatch_resume(timer)
|
|
|
-
|
|
|
+
|
|
|
return true
|
|
|
} else {
|
|
|
return false
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- func stopNotifier() {
|
|
|
-
|
|
|
+
|
|
|
+ public func stopNotifier() {
|
|
|
+
|
|
|
reachabilityObject = nil
|
|
|
-
|
|
|
+
|
|
|
if let timer = dispatch_timer {
|
|
|
dispatch_source_cancel(timer)
|
|
|
dispatch_timer = nil
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// MARK: - *** Connection test methods ***
|
|
|
- func isReachable() -> Bool {
|
|
|
+ public func isReachable() -> Bool {
|
|
|
return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
|
|
|
return self.isReachableWithFlags(flags)
|
|
|
})
|
|
|
}
|
|
|
-
|
|
|
- func isReachableViaWWAN() -> Bool {
|
|
|
-
|
|
|
+
|
|
|
+ public func isReachableViaWWAN() -> Bool {
|
|
|
+
|
|
|
if isRunningOnDevice {
|
|
|
return isReachableWithTest() { flags -> Bool in
|
|
|
// Check we're REACHABLE
|
|
|
if self.isReachable(flags) {
|
|
|
-
|
|
|
+
|
|
|
// Now, check we're on WWAN
|
|
|
if self.isOnWWAN(flags) {
|
|
|
return true
|
|
|
@@ -165,14 +165,14 @@ class Reachability: NSObject, Printable {
|
|
|
}
|
|
|
return false
|
|
|
}
|
|
|
-
|
|
|
- func isReachableViaWiFi() -> Bool {
|
|
|
-
|
|
|
+
|
|
|
+ public func isReachableViaWiFi() -> Bool {
|
|
|
+
|
|
|
return isReachableWithTest() { flags -> Bool in
|
|
|
-
|
|
|
+
|
|
|
// Check we're reachable
|
|
|
if self.isReachable(flags) {
|
|
|
-
|
|
|
+
|
|
|
if self.isRunningOnDevice {
|
|
|
// Check we're NOT on WWAN
|
|
|
if self.isOnWWAN(flags) {
|
|
|
@@ -181,11 +181,11 @@ class Reachability: NSObject, Printable {
|
|
|
}
|
|
|
return true
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return false
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// MARK: - *** Private methods ***
|
|
|
private var isRunningOnDevice: Bool = {
|
|
|
#if (arch(i386) || arch(x86_64)) && os(iOS)
|
|
|
@@ -194,20 +194,20 @@ class Reachability: NSObject, Printable {
|
|
|
return true
|
|
|
#endif
|
|
|
}()
|
|
|
-
|
|
|
+
|
|
|
private var reachabilityRef: SCNetworkReachability?
|
|
|
private var reachabilityObject: AnyObject?
|
|
|
private var dispatch_timer: dispatch_source_t?
|
|
|
private lazy var timer_queue: dispatch_queue_t = {
|
|
|
return dispatch_queue_create("uk.co.joylordsystems.reachability_timer_queue", nil)
|
|
|
- }()
|
|
|
+ }()
|
|
|
private var previousReachabilityFlags: SCNetworkReachabilityFlags?
|
|
|
-
|
|
|
+
|
|
|
private init(reachabilityRef: SCNetworkReachability) {
|
|
|
reachableOnWWAN = true
|
|
|
self.reachabilityRef = reachabilityRef
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
func timerFired() {
|
|
|
let currentReachabilityFlags = reachabilityFlags
|
|
|
if let _previousReachabilityFlags = previousReachabilityFlags {
|
|
|
@@ -215,11 +215,11 @@ class Reachability: NSObject, Printable {
|
|
|
dispatch_async(dispatch_get_main_queue(), { [unowned self] in
|
|
|
self.reachabilityChanged(currentReachabilityFlags)
|
|
|
self.previousReachabilityFlags = currentReachabilityFlags
|
|
|
- })
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private func reachabilityChanged(flags: SCNetworkReachabilityFlags) {
|
|
|
if isReachableWithFlags(flags) {
|
|
|
if let block = whenReachable {
|
|
|
@@ -230,68 +230,68 @@ class Reachability: NSObject, Printable {
|
|
|
block(self)
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
NSNotificationCenter.defaultCenter().postNotificationName(ReachabilityChangedNotification, object:self)
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private func isReachableWithFlags(flags: SCNetworkReachabilityFlags) -> Bool {
|
|
|
-
|
|
|
+
|
|
|
let reachable = isReachable(flags)
|
|
|
-
|
|
|
+
|
|
|
if !reachable {
|
|
|
return false
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if isConnectionRequiredOrTransient(flags) {
|
|
|
return false
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if isRunningOnDevice {
|
|
|
if isOnWWAN(flags) && !reachableOnWWAN {
|
|
|
// We don't want to connect when on 3G.
|
|
|
return false
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return true
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private func isReachableWithTest(test: (SCNetworkReachabilityFlags) -> (Bool)) -> Bool {
|
|
|
var flags: SCNetworkReachabilityFlags = 0
|
|
|
let gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef, &flags) != 0
|
|
|
if gotFlags {
|
|
|
return test(flags)
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return false
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// WWAN may be available, but not active until a connection has been established.
|
|
|
// WiFi may require a connection for VPN on Demand.
|
|
|
private func isConnectionRequired() -> Bool {
|
|
|
return connectionRequired()
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private func connectionRequired() -> Bool {
|
|
|
return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
|
|
|
return self.isConnectionRequired(flags)
|
|
|
})
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Dynamic, on demand connection?
|
|
|
private func isConnectionOnDemand() -> Bool {
|
|
|
return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
|
|
|
return self.isConnectionRequired(flags) && self.isConnectionOnTrafficOrDemand(flags)
|
|
|
})
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// Is user intervention required?
|
|
|
private func isInterventionRequired() -> Bool {
|
|
|
return isReachableWithTest({ (flags: SCNetworkReachabilityFlags) -> (Bool) in
|
|
|
return self.isConnectionRequired(flags) && self.isInterventionRequired(flags)
|
|
|
})
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private func isOnWWAN(flags: SCNetworkReachabilityFlags) -> Bool {
|
|
|
return flags & SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsIsWWAN) != 0
|
|
|
}
|
|
|
@@ -326,19 +326,19 @@ class Reachability: NSObject, Printable {
|
|
|
let testcase = SCNetworkReachabilityFlags(kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
|
|
|
return flags & testcase == testcase
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private var reachabilityFlags: SCNetworkReachabilityFlags {
|
|
|
var flags: SCNetworkReachabilityFlags = 0
|
|
|
let gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef, &flags) != 0
|
|
|
if gotFlags {
|
|
|
return flags
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return 0
|
|
|
}
|
|
|
-
|
|
|
- override var description: String {
|
|
|
-
|
|
|
+
|
|
|
+ override public var description: String {
|
|
|
+
|
|
|
var W: String
|
|
|
if isRunningOnDevice {
|
|
|
W = isOnWWAN(reachabilityFlags) ? "W" : "-"
|
|
|
@@ -353,17 +353,15 @@ class Reachability: NSObject, Printable {
|
|
|
let D = isConnectionOnDemand(reachabilityFlags) ? "D" : "-"
|
|
|
let l = isLocalAddress(reachabilityFlags) ? "l" : "-"
|
|
|
let d = isDirect(reachabilityFlags) ? "d" : "-"
|
|
|
-
|
|
|
+
|
|
|
return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)"
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
deinit {
|
|
|
stopNotifier()
|
|
|
-
|
|
|
+
|
|
|
reachabilityRef = nil
|
|
|
whenReachable = nil
|
|
|
whenUnreachable = nil
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-
|